6.7 KiB
| description |
|---|
| Otimizar código e documentar padrões de qualidade (estrutura, limites, JSDoc e tipagem) |
Workflow: Qualidade e Documentação de Código
🎯 Objetivo
Unificar as regras de estrutura de arquivos, otimização e documentação em um único fluxo de referência para o projeto eli-vue.
1️⃣ Limite de Linhas por Arquivo
Nenhum arquivo *.vue, *.ts ou *.tsx pode ultrapassar 250 linhas.
Essa regra existe para facilitar a leitura humana, a revisão de código e a manutenção a longo prazo.
O script
scripts/check-vue-linhas.shvalida automaticamente os arquivos.vuenoprebuild.
Estratégias quando o limite se aproxima:
| Situação | Ação |
|---|---|
| Template longo | Extrair seções como subcomponentes |
| CSS extenso | Mover para <nome>.css co-localizado |
setup() extenso |
Extrair subcomponentes, CSS ou Dispatch Table |
| Modal/Dialog embutido | Sempre em componente separado (*Modal.vue) |
| Handlers e dispatch | Extrair para handlers.ts ou dispatch.ts |
2️⃣ Estrutura de Componentes Vue
defineComponent obrigatório
Todo componente Vue deve usar defineComponent para garantir inferência de tipos correta e consistência.
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'MeuComponente',
setup() {
// lógica aqui
}
})
</script>
Por que é importante:
- Inferência de tipos TypeScript correta dentro do componente
- Reconhecimento por ferramentas de análise estática e IDEs
- Compatível com Options API e Composition API com
setup() - Padronização entre todos os componentes do projeto
setup() não pode ser externo
O setup() deve estar sempre dentro do componente .vue. Não é permitido extrair ou delegar o setup para nenhum arquivo externo.
// ❌ Proibido — setup delegado inteiramente para arquivo externo
// useEliTabela.ts
export function useEliTabela(props, emit) { ... }
// EliTabela.vue
export default defineComponent({
setup(props, { emit }) {
return useEliTabela(props, emit) // setup é apenas um proxy vazio
}
})
// ✅ Correto — setup com lógica própria dentro do componente
// EliTabela.vue
export default defineComponent({
setup(props, { emit }) {
const estado = ref(null)
const carregar = async () => { ... }
return { estado, carregar }
}
})
Composables
use*.tspodem encapsular lógica reutilizável pontual (ex:useFormatador,usePermissao), mas nunca substituir ou esvaziar osetup()do componente. Se o componente ultrapassar 250 linhas, a solução é extrair subcomponentes, mover CSS ou usar Dispatch Table — não esvaziar osetup.
Desassociação de CSS
A separação de estilos em arquivo externo é permitida e recomendada quando o CSS contribui significativamente para o total de linhas.
Padrão de nomenclatura:
MeuComponente.vue
MeuComponente.css
O .vue importa o arquivo CSS no script:
import './MeuComponente.css'
Ou via tag <style>:
<style src="./MeuComponente.css" />
Dispatch Table em Arquivo Separado
O uso de Dispatch Table (mapa de funções) em arquivos separados é bem-vindo para eliminar blocos longos de if/else ou switch.
// handlers.ts
export const acaoHandlers: Record<string, () => void> = {
salvar: () => { ... },
cancelar: () => { ... },
excluir: () => { ... },
}
// MeuComponente.vue
import { acaoHandlers } from './handlers'
function executar(acao: string) {
acaoHandlers[acao]?.()
}
3️⃣ Documentação em Código (JSDoc)
Princípios básicos
- Por que, não o Quê: Documente o raciocínio por trás da decisão arquitetural, não o mecanismo óbvio que o TypeScript já expressa.
- Nomenclatura limpa: Confie na auto-documentação de nomes expressivos antes de escrever comentários de texto livre.
Funções compartilhadas — JSDoc obrigatório
Toda função central ou de uso compartilhado (frontend, backend, mobile ou bibliotecas comuns) deve ter cabeçalho em JSDoc.
/**
* Busca os detalhes de uma entidade baseando-se no ID fornecido e calcula privilégios aplicáveis.
* Não utilizar em fluxos de processamento em lote para evitar sobrecarga de requisições.
*
* @param tenantId - Identificador único do cliente/tenant no sistema
* @param entityId - ID exclusivo identificando o registro na base de dados
* @returns Retorna o objeto consolidado com dados ou `null` caso não seja encontrado
*/
export const obterDetalhesEntidade = async (tenantId: string, entityId: string) => { ... }
4️⃣ Casts Temporários e Exceções de Tipagem
Devido à política TypeScript sem any, coerções agressivas requerem justificativa obrigatória em comentário.
// Necessário cast duplo pois a biblioteca externa não provê tipagem genérica adequada na versão atual
const dados = payload.raw as unknown as RespostaFormatada
5️⃣ TODOs Estruturados
Toda dívida técnica ou dúvida de negócio deixada em código deve seguir o formato // TODO:
- Tipagem:
// TODO de tipagem: Criar interface estrita para o retorno da API de listagem - Negócio:
// TODO de regra: Validar com Produto se a taxa adicional se aplica a clientes antigos
6️⃣ Resumo das Regras
| Regra | Permitido | Proibido |
|---|---|---|
| Tamanho de arquivo | Até 250 linhas | Acima de 250 linhas |
defineComponent |
Obrigatório em todo .vue |
Componentes sem defineComponent |
setup() externo |
— | Proibido em qualquer forma |
| CSS externo | <nome>.css vinculado ao .vue |
CSS inline quando ultrapassa o limite |
| Dispatch Table | Em arquivo separado | — |
| JSDoc | Obrigatório em funções compartilhadas | Omitir em funções de uso amplo |
Casts as any |
Com biome-ignore justificado |
Sem comentário explicativo |
7️⃣ Checklist de Validação
Antes de concluir qualquer refatoração ou adição de código:
- O arquivo resultante tem menos de 250 linhas?
- Funções compartilhadas têm
JSDoccom@parame@returns? - Casts temporários têm comentário justificando?
- TODOs estão no formato padronizado?
- A documentação explica o porquê, não apenas o o quê?
defineComponentestá presente em todo componente.vue?
Referências
- Script:
scripts/check-vue-linhas.sh(validação automática de tamanho) - Rule:
.agent/rules/vue-limite-linhas.global.md(política de limite de linhas) - Rule:
.agent/rules/typescript-sem-any.global.md(política de tipagem) - Skill:
.agent/skills/documentar-decisao-tecnica/SKILL.md(ADRs para decisões arquiteturais)