Skip to main content

Guia de Estilo e Padrões

Este documento define as convenções rígidas de código e os requisitos de qualidade para o projeto tupynambalucas.dev. Todo o código deve aderir a estes padrões para garantir consistência e sustentabilidade em todo o monorepo.


1. Formatação de Código (Prettier)​

O código deve seguir as regras definidas em shared/config/prettierrc.json:

  • Indentação: 2 espaços.
  • Ponto e vírgula: Sempre usar (true).
  • Aspas: Use aspas simples (true), exceto em JSX.
  • Vírgula final: Sempre use quando possível (all).
  • Largura da linha: Máximo de 100 caracteres.

2. Regras do TypeScript (Modo Estrito)​

Priorizamos a segurança máxima de tipos por meio de uma configuração rigorosa do TypeScript.

  • Definições de Objetos: Use interface para definições de objetos para garantir consistência. Aliases de tipos são permitidos para uniões ou tipos utilitários (por exemplo, z.infer<>).
  • Arrays: Use a sintaxe T[] em vez de Array\<T>.
  • Importações de Tipos: Sempre use import type para tipos. Importe tipos separadamente dos valores (style: separate-type-imports).
  • Variáveis Não Utilizadas: Prefixe com um sublinhado (por exemplo, _id) para sinalizar a intenção.
  • Tipagem Estrita: O uso de any é estritamente proibido.
  • Comparações: Sempre use igualdade estrita (===).
  • Booleanos Estritos: Expressões booleanas devem ser explícitas. Use if (value !== undefined) em vez de if (value).
  • Conversões Seguras: Não use String(value) ou ${value} em tipos genéricos unknown ou object. Use guardas de tipo explícitas para primitivos para evitar bugs do tipo [object Object].
  • Asserções de Tipo: Evite asserções de tipo desnecessárias (por exemplo, value as T quando value já é T). O linter sinalizará isso; remova-os para manter o código limpo.

3. Gerenciamento de Código Assíncrono (Crítico)​

  • Sem Promises Flutuantes: Nunca deixe promises "flutuantes" sem tratamento ou await.
  • O Operador void: Quando uma função assíncrona for chamada por seus efeitos colaterais e NÃO for aguardada com await, prefixe-a com void (por exemplo, void startServer()). Isso sinaliza uma execução intencional não bloqueante.
  • Handlers do Fastify: Handlers de rota devem usar o tipo FastifyZodHandler e retornar Promise\<void>. Use void reply.send() quando não retornar a resposta diretamente.
  • Plugins do Fastify: Se um FastifyPluginAsync não usar await, remova a palavra-chave async e retorne Promise.resolve() para cumprir com require-await enquanto mantém a integridade dos tipos.
  • Tratamento de Erros: Cada operação com await deve estar dentro de um bloco try/catch ou fazer parte de uma cadeia .catch().

4. Padrões Específicos de Pacotes​

4.1. Pacotes Principais (@repo/hub/core / @repo/hub/core)​

  • Zero Avisos: Estes pacotes devem ter zero avisos ou erros de lint.
  • APIs Públicas: Todos os membros exportados devem ter tipos de retorno explicitamente definidos.

4.2. API (Fastify)​

  • Nomenclatura: Controladores e Serviços usam PascalCase para classes e camelCase para métodos.
  • Mapeamento: Os controladores são responsáveis por mapear modelos de banco de dados para DTOs do Core por meio de métodos privados.

4.3. Web (React)​

  • Padrões do React 19: Use o hook use() para consumir promises e contexto quando aplicável, reduzindo o código boilerplate de useEffect. Prefira Server Actions (se aplicável) ou propriedades de action otimizadas em formulários para uma experiência de usuário mais fluida.
  • Estado Global: Acione efeitos colaterais assíncronos em Stores ou Effects usando o operador void para chamadas não aguardadas.
  • Refs: O acesso a ref.current durante a renderização é estritamente proibido. Ao passar múltiplas refs para um componente filho, passe-as individualmente em vez de em um objeto agrupado para evitar confusão do linter e garantir clareza.
  • Booleanos Estritos na UI: Em JSX, sempre use comparações explícitas: {isValid === true && <Component />} para evitar a renderização de 0 ou NaN na UI.
  • Sincronização de Estado: Evite chamar setState de forma síncrona dentro de useEffect se a atualização for derivada de props ou de outro estado. Em vez disso, sincronize o estado durante a renderização (o padrão "prevProps") ou inicialize o estado com uma função.
  • Chaves de Listas: Sempre use IDs estáveis e exclusivos (por exemplo, _id) como chaves em listas. Evite usar índices de array, a menos que a lista seja estática e não tenha identificadores exclusivos. Se os índices precisarem ser usados, documente o motivo.
  • Estilização e Medidas Responsivas: Use CSS Modules (.module.css). Classes utilitárias do Tailwind são permitidas dentro de módulos via @apply. Adira estritamente aos Padrões Responsivos definidos na Seção 5.
  • Logs de Console: console.log é proibido e fará com que o build de produção falhe. Use console.info/warn/error com moderação.

5. Padrões de CSS e Responsividade (Rigorosos)​

Para garantir uma interface de usuário fluida, acessível e moderna, as seguintes regras são obrigatórias para todas as aplicações:

  • Sem Pixels (px): O uso de valores fixos em px é estritamente proibido para dimensionamento, espaçamento e tipografia.
    • Exceção: 1px é permitido para bordas quando um efeito de linha fina (hairline) for desejado.
  • Tipografia Relativa: Sempre use rem para tamanhos de fonte. Nunca use px ou em para tipografia.
  • Espaçamento Fluido: Use rem para espaçamento consistente ou clamp() para espaçamento fluido que se dimensiona com a viewport.
  • Layouts Estruturais: Use primitivas de layout modernas: flexbox, grid, %, vw e vh.
  • Funções Lógicas: Aproveite clamp(), min() e max() para criar limites para elementos fluidos sem usar media queries para cada pequeno ajuste.

Exemplos Responsivos​

ResponsiveHero.module.css
.container {
/* Fluid width: min 320px, preferred 90%, max 75rem (1200px) */
width: clamp(20rem, 90%, 75rem);
padding: 1.5rem;
}

.heroTitle {
/* Fluid typography: min 1.5rem, scales with 4vw, max 3rem */
font-size: clamp(1.5rem, 4vw, 3rem);
color: var(--color-title-dark);
}

.heroSection {
/* Full viewport height minus header height (assumed 4rem) */
min-height: calc(100vh - 4rem);
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-background-tint);
}
ResponsiveHero.tsx
import styles from './ResponsiveHero.module.css';

export const ResponsiveHero = () => {
return (
<section className={styles.heroSection}>
<div className={styles.container}>
<h1 className={styles.heroTitle}>Design Fluido e Responsivo</h1>
</div>
</section>
);
};

6. Convenções de Nomenclatura​

  • Schemas: Devem terminar com Schema (por exemplo, ProductSchema).
  • Arquivos: Siga o padrão name.type.ts (por exemplo, auth.controller.ts, apiPlugin.ts).
  • Diretórios: Use camelCase para nomes de diretórios dentro de pastas de código-fonte.

7. Padrões de Idioma (Inglês Obrigatório em Primeiro Lugar)​

Para manter a acessibilidade global, escalabilidade e consistência de código em todos os contextos do monorepo:

  • Código-Fonte e Comentários: Todo o código-fonte (nomes de variáveis, funções, classes, interfaces, propriedades, schemas, arquivos) e comentários dentro de arquivos de código DEVEM ser escritos exclusivamente em Inglês (en-US).
  • Documentação: Toda a documentação técnica e de produto, READMEs, diretrizes de segurança e relatórios arquiteturais DEVEM ser escritos em inglês.
  • Histórico do Git: As mensagens de commit e os títulos/descrições de Pull Request DEVEM seguir a especificação Conventional Commits e ser escritos em inglês (consulte Commit Conventions).
  • Exceções de Localização e i18n: As ÚNICAS exceções são arquivos de localização (por exemplo, configurações de i18n, tabelas de tradução, arquivos JSON de dicionário) e dados mockados explícitos que representam texto do usuário final em português. Toda a lógica e definições internas da aplicação devem permanecer estritamente em inglês.

8. Padrões de Blocos de Código (blocos live)​

Uso Restrito

O bloco de código live do Docusaurus (editor interativo) é EXCLUSIVAMENTE reservado para este Guia de Estilo. Ele NÃO deve ser usado em nenhuma outra página de documentação.

Seu propósito é estritamente demonstrar:

  1. Componentes React: Seguindo padrões rigorosos (booleanos explícitos, tipos estritos, etc.).
  2. Estruturas de Domínio de API: Visualizando como os modelos de domínio devem ser estruturados.

8.1. Exemplo Rigoroso de Padrão de React e CSS Module​

O exemplo abaixo demonstra nosso padrão preferido para componentes interativos usando CSS Modules. Ele exibe a integração de gerenciamento de estado, memoização e tratamento assíncrono, aderindo estritamente às nossas regras de TypeScript, lógica booleana e estilização.

AdvancedCycleManager.module.css
.container {
padding: 1.5rem;
border: 0.0625rem solid var(--color-border-light);
border-radius: 0.75rem;
background-color: var(--color-background-white);
box-shadow: 0 0.25rem 0.375rem -0.0625rem rgba(0, 0, 0, 0.1);
}

.header {
display: flex;
justify-content: space-between;
margin-bottom: 1.5rem;
align-items: center;
}

.title {
margin: 0;
color: var(--color-title-dark);
}

.statusBadge {
font-size: 0.7rem;
font-weight: 800;
padding: 0.3rem 0.8rem;
border-radius: 1.25rem;
letter-spacing: 0.05em;
}

.statusInvalid {
background-color: #fee2e2;
color: #991b1b;
}

.statusReady {
background-color: #f0fdf4;
color: #166534;
}

.productList {
display: flex;
flex-direction: column;
gap: 0.8rem;
margin-bottom: 1.5rem;
}

.productItem {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.8rem;
border-radius: 0.5rem;
border: 0.0625rem solid var(--color-border-light);
background-color: var(--color-background-tint);
}

.productInvalid {
border-color: #fecaca;
background-color: #fff1f2;
}

.productName {
font-weight: 700;
font-size: 0.9rem;
color: var(--color-title-dark);
}

.productId {
font-size: 0.7rem;
color: var(--color-subtitle-dark);
}

.inputGroup {
display: flex;
align-items: center;
gap: 0.5rem;
}

.currency {
font-size: 0.8rem;
font-weight: 600;
color: var(--color-subtitle-dark);
}

.priceInput {
width: 5rem;
padding: 0.4rem;
border-radius: 0.375rem;
border: 0.0625rem solid var(--color-border-light);
text-align: right;
font-size: 0.9rem;
}

.submitButton {
width: 100%;
padding: 1rem;
border-radius: 0.5rem;
border: none;
background-color: var(--color-identity-primary);
color: white;
font-weight: 700;
font-size: 0.95rem;
transition:
opacity 0.2s,
background-color 0.2s;
}

.submitButton:disabled {
cursor: not-allowed;
opacity: 0.6;
}

.submitButton:hover:not(:disabled) {
filter: brightness(1.1);
cursor: pointer;
}
Live Editor
/**
 * Advanced Cycle Product Manager
 * Demonstrates: CSS Modules (styles object), useCallback, void operator, explicit booleans, and stable keys.
 */

// import styles from './AdvancedCycleManager.module.css';

function AdvancedCycleManager() {
  // In real development, 'styles' comes from the CSS Module import above.
  // In this live demo, we map keys to the simulated class names defined below.
  const styles = {
    container: 'ACM_container',
    header: 'ACM_header',
    title: 'ACM_title',
    statusBadge: 'ACM_statusBadge',
    statusInvalid: 'ACM_statusInvalid',
    statusReady: 'ACM_statusReady',
    productList: 'ACM_productList',
    productItem: 'ACM_productItem',
    productInvalid: 'ACM_productInvalid',
    productName: 'ACM_productName',
    productId: 'ACM_productId',
    inputGroup: 'ACM_inputGroup',
    currency: 'ACM_currency',
    priceInput: 'ACM_priceInput',
    submitButton: 'ACM_submitButton',
  };

  const [products, setProducts] = React.useState([
    { id: 'uuid-1', name: 'Green Lettuce', price: 4.5, isValid: true },
    { id: 'uuid-2', name: 'Cherry Tomato', price: 0, isValid: false },
  ]);
  const [isSubmitting, setIsSubmitting] = React.useState(false);

  const hasErrors = React.useMemo(() => {
    return products.some((p) => p.isValid === false);
  }, [products]);

  const updateProductPrice = React.useCallback((id, value) => {
    const numericValue = parseFloat(value) || 0;
    setProducts((prev) =>
      prev.map((p) => (p.id === id ? { ...p, price: numericValue, isValid: numericValue > 0 } : p)),
    );
  }, []);

  const handleSubmit = async () => {
    setIsSubmitting(true);
    try {
      await new Promise((resolve) => setTimeout(resolve, 1500));
      console.info('Products submitted:', products);
      alert('Cycle products updated successfully!');
    } catch (err) {
      console.error('[Update Error]:', err);
    } finally {
      setIsSubmitting(false);
    }
  };

  const handleAction = () => {
    void handleSubmit();
  };

  return (
    <div className={styles.container}>
      <div className={styles.header}>
        <h3 className={styles.title}>Cycle Manager</h3>

        <span
          className={`${styles.statusBadge} ${
            hasErrors === true ? styles.statusInvalid : styles.statusReady
          }`}
        >
          {hasErrors === true ? '⚠ INVALID ITEMS' : '✓ READY TO SAVE'}
        </span>
      </div>

      <div className={styles.productList}>
        {products.map((product) => (
          <div
            key={product.id}
            className={`${styles.productItem} ${
              product.isValid === false ? styles.productInvalid : ''
            }`}
          >
            <div>
              <div className={styles.productName}>{product.name}</div>
              <div className={styles.productId}>ID: {product.id}</div>
            </div>

            <div className={styles.inputGroup}>
              <span className={styles.currency}>R$</span>
              <input
                type="number"
                value={product.price}
                onChange={(e) => updateProductPrice(product.id, e.target.value)}
                className={styles.priceInput}
              />
            </div>
          </div>
        ))}
      </div>

      <button
        disabled={isSubmitting === true || hasErrors === true}
        onClick={handleAction}
        className={styles.submitButton}
      >
        {isSubmitting === true ? 'Processing Update...' : 'Save Cycle Changes'}
      </button>

      {/* Internal CSS for the demo purpose only - NOT for production */}
      <style>{`
        .ACM_container { padding: 1.5rem; border: 1px solid #e2e8f0; border-radius: 12px; background: white; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); }
        .ACM_header { display: flex; justify-content: space-between; margin-bottom: 1.5rem; align-items: center; }
        .ACM_title { margin: 0; color: #1e293b; }
        .ACM_statusBadge { font-size: 0.7rem; font-weight: 800; padding: 0.3rem 0.8rem; border-radius: 20px; letter-spacing: 0.05em; }
        .ACM_statusInvalid { background-color: #fee2e2; color: #991b1b; }
        .ACM_statusReady { background-color: #f0fdf4; color: #166534; }
        .ACM_productList { display: flex; flex-direction: column; gap: 0.8rem; margin-bottom: 1.5rem; }
        .ACM_productItem { display: flex; align-items: center; justify-content: space-between; padding: 0.8rem; border-radius: 8px; border: 1px solid #f1f5f9; background: #fafafa; }
        .ACM_productInvalid { border-color: #fecaca; background-color: #fff1f2; }
        .ACM_productName { font-weight: 700; font-size: 0.9rem; color: #334155; }
        .ACM_productId { font-size: 0.7rem; color: #94a3b8; }
        .ACM_inputGroup { display: flex; align-items: center; gap: 0.5rem; }
        .ACM_currency { font-size: 0.8rem; font-weight: 600; color: #64748b; }
        .ACM_priceInput { width: 80px; padding: 0.4rem; border-radius: 6px; border: 1px solid #cbd5e1; text-align: right; }
        .ACM_submitButton { width: 100%; padding: 1rem; border-radius: 8px; border: none; background: #16a34a; color: white; font-weight: 700; cursor: pointer; }
        .ACM_submitButton:disabled { opacity: 0.6; cursor: not-allowed; }
      `}</style>
    </div>
  );
}
Result
Loading...

Última atualização: Junho de 2026