01 — Stack e decisões
01 — Stack e decisões
Define a stack fixa do projeto web e o que cada peça resolve. Itens marcados FIXO não são escolha do projeto novo — mudá-los quebra compatibilidade com o time. Itens NEGOCIÁVEL podem variar se houver motivo declarado. Versões verificadas em 2026-08-19 contra o npm registry e testadas de verdade (
tsc --noEmit,lint,build, tela no browser) — não é combinação só especulada.
Stack
| Peça | Versão | Status | Por quê |
|---|---|---|---|
| Node | >= 24 |
FIXO | Última LTS ativa (Node 24, "Krypton"). process.loadEnvFile exige ≥ 20.6 |
| TypeScript | ^6.0 |
FIXO | Bumpado de 5.9. Teto real: typescript-eslint (v8, latest) exige typescript >=4.8.4 <6.1.0 — 6.0.3 é a versão mais alta possível hoje. 7.x fica pra quando houver suporte |
| Vite | ^8.2 |
FIXO | Bumpado de 4. @vitejs/plugin-react v6 exige vite ^8 — os três (vite, @vitejs/plugin-react, vite-plugin-checker) sobem juntos |
| React | ^19.2 |
FIXO | Bumpado de 18. @curio/client só exige react >=16.8.0 — sem bloqueio |
@curio/client |
^1.4.2 |
FIXO | Transporte proprietário para o backend. Não avaliado nesta rodada (decisão do projeto) |
MUI (@mui/material) |
^9.3 |
FIXO | Bumpado de 5 direto para 9 — ver migração MUI 5→9 |
@mui/x-date-pickers |
^9.11 |
FIXO | Acoplado ao major do @mui/material. Peer cobre React 19 |
@tanstack/react-query |
^5.101 |
FIXO | Bumpado de 4.36. cacheTime→gcTime já ajustado nos hooks |
react-router-dom |
^7.18 |
FIXO | Bumpado de 6. BrowserRouter não aceita mais a prop future
|
react-hook-form |
^7.85 |
FIXO | Bumpado de 7.48 — exigido pelo @hookform/resolvers v5 (ver Zod abaixo) |
@hookform/resolvers |
^5.9 |
FIXO | Bumpado de 3 — obrigatório para usar Zod v4, não é opcional |
zod |
^4.4 |
FIXO | Bumpado de 3. Mudança de tipos exigiu ajuste em useValidatedForm — ver abaixo |
date-fns |
^4.4 |
FIXO | Bumpado de 2. Exige @mui/x-date-pickers/AdapterDateFns (v9 já é o adapter para v3/v4 — ver nota) |
| ESLint 10 + Prettier 3 | — | FIXO | Bumpado de 8. Flat config (eslint.config.js, não mais .eslintrc.json) — ver 14
|
typescript-eslint |
^8.67 |
FIXO | Bumpado de 7. Compatível com ESLint 8/9/10 e TS <6.1.0 — é o teto que bloqueia o TypeScript acima |
eslint-plugin-react-hooks |
^7.1 |
FIXO | Bumpado de 4. recommended ficou bem mais amplo (regras novas tipo set-state-in-effect) — ver 14
|
| husky + lint-staged | — | FIXO | Gate de pré-commit. lint-staged bumpado de 15 para 17 (dev tool, sem mudança de config) |
| Emotion | ^11.14 |
FIXO | Peer dependency do MUI |
Sem suíte de testes por padrão. Este guia não inventa uma nem assume que ela existe. O gate de qualidade é type-check + lint + build — ver 14 e 19. Se o projeto novo quiser testes, essa é uma decisão a tomar explicitamente no início, não depois.
Por que nem tudo foi para o latest
Em 2026-08-19, depois de uma rodada completa de atualização testada de verdade, só sobrou um pacote fora do latest:
| Pacote | Aqui | Latest hoje | Ficou de fora porque |
|---|---|---|---|
| TypeScript | 6.0.3 |
7.0.2 |
typescript-eslint@8.67.0 (latest estável) exige typescript >=4.8.4 <6.1.0 — não há versão estável do typescript-eslint que aceite TS 7 ainda (só canary). 6.0.3 é o teto real: existe uma linha 6.x entre o 5.9 antigo e o 7.0 novo, e ela cabe no range aceito — não pule direto de 5→7 sem checar se há uma minor intermediária. Bumpar além de 6.0.x quebraria o lint, não é escolha. |
Todo o resto (MUI, Vite, ESLint, date-fns, typescript-eslint, react-hooks) já está no latest — ver as notas de migração abaixo para quem for repetir isso num projeto mais antigo.
Migração MUI 5 → 9: o que mudou de verdade
O caminho não é incremental por major (5→6→7→8→9): @mui/x-date-pickers@9 exige
@mui/material: "^7.3.0 || ^9.0.0" — não aceita a v8 como peer. Suba os três pacotes
(@mui/material, @mui/icons-material, @mui/x-date-pickers) juntos, direto pra v9.
Superfície de quebra real, testada com tsc --noEmit:
-
Sem uso de
Gridno projeto — a maior fonte de quebra entre majors do MUI não se aplicou aqui. Se o projeto novo usaGrid, verifique a API v1→v2 separadamente. -
InputProps,inputProps,InputLabelPropsforam removidos deTextField/Checkbox(não são só deprecados — o tipo não aceita mais). ViramslotProps:InputProps→slotProps.input,inputProps→slotProps.htmlInput,InputLabelProps→slotProps.inputLabel. Isso afeta qualquer componente decommon/que componhaTextField(FormField,CurrencyInput,PhoneInput,CnpjInput,DatePickerInput,DataTable,TransferListCard). -
Props de atalho de estilo direto em
Box/Stack/Typography/DialogTitle(display,alignItems,justifyContent,fontWeight, etc., sem passar porsx) pararam de tipar — MUI removeu o suporte a "system props" desses componentes. Mova prasx={{ ... }}. -
disableEscapeKeyDownfoi removido deDialog/Modalsem substituto direto — se oDialognão temonClose, a prop já era redundante (nada fecha no Escape de qualquer forma); se tem, trate areasondentro do próprioonClose. -
@mui/x-date-pickersinverteu a convenção de adapter entre majors: na v6/v7,AdapterDateFnsera para date-fns v2 eAdapterDateFnsV3para v3/v4. Na v9, inverteu —AdapterDateFns(sem sufixo) já é o adapter para v3/v4, eAdapterDateFnsV2é o legado. Confira o import de novo a cada major, não assuma que o nome antigo continua correto.
date-fns e o adapter do x-date-pickers
Bumpar date-fns sem trocar o subpath do adapter (ver acima) quebra silenciosamente em runtime, não
em tipo — o import resolve, mas o parsing de data se comporta diferente. Sempre os dois juntos.
Decisões que valem entender
O backend não é REST
@curio/client é um transporte RPC proprietário. Não existe GET /api/fornecedores. Existe:
abrir um caso de uso por id numérico e enviar requests nomeados (RM_OBTEM_LISTA) dentro dele.
Consequência prática: não use axios, fetch direto (fora do carregamento do config.json), nem
qualquer geração de cliente a partir de OpenAPI. Ver 06.
O fetch do config é a única exceção
src/lib/curio/index.ts faz fetch("./config.json") para descobrir a URL do serviço em runtime.
Isso é intencional: permite trocar de ambiente sem rebuild. Ver 04.
React Query v5 — gcTime, não cacheTime
A opção cacheTime foi renomeada para gcTime na v5, em defaultOptions.queries e
defaultOptions.mutations do queryClient.ts, e em qualquer useQuery que a declare
(useConnectQuery). isPending já era o nome correto desde a v4.36 — nenhuma mudança ali.
Zod v4 exige @hookform/resolvers v5 e um ajuste de generics
Zod v4 mudou a arquitetura interna de tipos de ZodType. Duas consequências reais, encontradas
rodando tsc de verdade, não hipotéticas:
-
@hookform/resolversv3 só aceita Zod^3— subir o Zod exige subir os resolvers para v5 (que por sua vez exigereact-hook-form >= 7.55.0). Não é uma escolha independente. - O hook genérico
useValidatedForm<T extends z.ZodType>deixou de compilar: ooutputdez.ZodTypeem v4 não fecha automaticamente emFieldValues. A correção é dupla — apertar o bound do genérico paraz.ZodType<FieldValues>, e no ponto de chamada dozodResolver, usar um cast de tipo (as anyjustificado +as unknown as Resolver<...>) porque o tipo internoZod4Typedo resolver não compõe com um schema genérico sem perder a inferência. Versrc/hooks/useValidatedForm.ts— o cast é só de tipo;zodResolvercontinua validando em runtime exatamente como antes.
React Router v7 — sem prop future
Os future flags (v7_startTransition, v7_relativeSplatPath) que o BrowserRouter aceitava na v6
viraram comportamento padrão na v7. A prop future não existe mais — remova-a de main.tsx.
Estado de servidor no React Query, estado de UI no componente
Não há Redux, Zustand ou store global. O que vem do backend vive no React Query; o resto é useState
local ou Context (AuthProvider, NotificationProvider, TabsContext). Se você sentir falta de uma
store global, provavelmente está guardando resposta de servidor no lugar errado.
Erro é global, não local
Nenhuma página trata erro de request com try/catch para exibir mensagem. O QueryCache/MutationCache
do queryClient captura e empurra para o NotificationProvider. Ver 07.
Negociável
| Item | Padrão sugerido | Quando mudar |
|---|---|---|
| Porta do dev server | 5000 |
Conflito local; ajuste em vite.config.ts
|
| Paleta do tema | Azul #1976d2
|
Identidade visual do produto novo |
| Navegação por abas | Opcional | Só adote se o produto realmente precisar de abas |
| Dark mode | Ausente | Se o produto exigir; ver 12 |
sourcemap em produção |
true |
Desligue se o bundle for público e sensível |
No Comments