Ir para o conteúdo
decodecodeveloper docs
Storefront → Templates → Migração

Playbook de migração

Passo a passo de mover uma loja Fresh para TanStack Start.

Este é o caminho manual. Se preferir automatizar, rode deco-migrate primeiro — ele faz os 70% mecânicos. Leia este playbook quando o script sinalizar algo que não auto-corrige, ou quando quiser entender o que ele está fazendo.

Fase 1 — Análise

Antes de mexer em qualquer coisa, faça um inventário:

cd /caminho/para/site-v1
 
# contagem de sections
find src/sections -name "*.tsx" | wc -l
 
# contagem de islands (precisam ser eliminadas)
find src/islands -name "*.tsx" 2>/dev/null | wc -l
 
# rotas customizadas
find routes -type f | wc -l
 
# loaders / actions
find src/loaders src/actions -name "*.ts" 2>/dev/null | wc -l
 
# blocks
find .deco/blocks -name "*.json" | wc -l

Anote os números. Eles viram a baseline de verificação pós-migração.

Procure por:

  • Imports de @deco/deco/* e $fresh/* — o script lida.
  • Imports de @preact/signals e preact/* — o script reescreve para React.
  • Chamadas custom de useScript(fn) — precisam de revisão manual.
  • Leituras custom de Deno.env — precisam de conversão.
  • Tudo em src/islands/ — toda island precisa de um destino.

Fase 2 — Scaffold

Gere os arquivos baseline da v2:

  • vite.config.ts (com decoVitePlugin, plugin Cloudflare, plugin TanStack Start, React, Tailwind v4)
  • wrangler.jsonc (com nodejs_compat + no_handle_cross_request_promise_resolution)
  • src/setup.ts (chama createSiteSetup)
  • src/server.ts (server entry do TanStack Start)
  • src/worker-entry.ts (chama createDecoWorkerEntry)
  • src/routes/__root.tsx, src/routes/index.tsx, src/routes/$.tsx

O script gera todos. Se for fazer na mão, copie dos templates em decocms/blocks/.agents/skills/deco-to-tanstack-migration/templates/.

Fase 3 — Transformação

Esta é a fase de reescrita em massa:

  • Imports — preact/* → react, @preact/signals → @tanstack/store, $fresh/* → @tanstack/react-start, @deco/deco/* → @decocms/blocks/* e @decocms/apps-*.
  • Atributos JSX — class → className, for → htmlFor.
  • APIs Fresh — defineRoute / defineApp para padrões TanStack Router.
  • Deno-isms — Deno.env.get(X) para bindings, import.meta.url para equivalentes Vite.
  • Tailwind — v3 → v4 (referências de cor por token, sintaxe de opacidade, reescrita de @apply).

As 70+ regras moram no script de migração. Veja Referência do script para a lista.

Fase 4 — Limpeza

Remova o que não é mais necessário:

  • src/islands/ — toda island virou outra coisa.
  • routes/ (a pasta antiga do Fresh).
  • deno.json, deno.lock.
  • static/ — movido para public/.
  • Pastas compat/ que o time tenha criado durante tentativas incrementais.

Fase 5 — Bootstrap

Instale dependências e regenere tudo:

npm install
npm run generate:blocks
npm run generate:schema
npm run generate:sections
npm run generate:loaders
npx tsr generate

Conecte tudo isso como scripts compostos no package.json para que um único npm run generate (ou equivalente) mantenha tudo em sincronia.

Fase 6 — Verificação

Rode typecheck e build:

npm run typecheck   # tsc --noEmit
npx vite build      # build de prod completo
npx wrangler deploy --dry-run

Typecheck limpo + build limpo significa que a migração mecânica terminou. Correção em runtime é um pass à parte.

Fase 7 — Ajustes manuais

O script registra tudo que precisa de atenção humana em MIGRATION_REPORT.md. Os itens mais comuns:

Islands → "use client" ou estado içado

Padrão: uma island que era dona de estado local (toggle de drawer do carrinho, modal de busca). Em React, marque a section pai como "use client" se a section toda for interativa, ou ice o estado para um provider no nível da section.

// antes (Fresh)
// src/islands/CartDrawer.tsx ← island inteira
// src/sections/Header.tsx importa a island
 
// depois (React)
// src/sections/Header/Header.tsx
"use client";
 
import { useState } from "react";
import CartDrawer from "~/components/CartDrawer";
 
export default function Header({ ... }: Props) {
  const [open, setOpen] = useState(false);
  return (
    <>
      <header>...</header>
      <CartDrawer open={open} onClose={() => setOpen(false)} />
    </>
  );
}

Caminhe pelas islands uma a uma, decidindo por island se ela pode subir para a section (server) ou se precisa continuar como client component.

Remoção de useScript(fn)

useScript(fn) era um padrão Fresh que extraía uma função para uma <script> inline. Em v2 não hidrata limpo. Substitua por:

  • inlineScript de @decocms/blocks/sdk/useScript para conteúdo estático.
  • Um componente client de verdade para qualquer coisa que toque estado ou DOM.

Hooks de plataforma (useCart, useUser, useWishlist)

Se sua loja v1 tinha overrides custom, porte. As implementações default em @decocms/apps-vtex/hooks cobrem os casos comuns. Veja Hooks VTEX.

Scripts de terceiros no <head>

Scripts que mutam o <head> (Google Tag Manager, Adobe Launch, Hotjar) frequentemente causam mismatch de hidratação em React. A correção é injetar via resposta do worker, depois do React renderizar, em vez de em JSX.

Fase 8 — Pass de performance

Depois da correção funcional:

  • Tunning do foldThreshold — geralmente 2-3 sections eager, resto deferred.
  • Auditoria de profiles de cache — defaults são conservadores; afrouxe para conteúdo estático, aperte para páginas perto do checkout.
  • Pode loaders.gen.ts — use --decofile-dir .deco/blocks para que só apareçam loaders que o CMS realmente referencia. Recomendado para sites novos; sites existentes podem adotar incrementalmente.
  • Verifique sections deferred em dev — se aparecer "I/O across requests", configure no_handle_cross_request_promise_resolution em wrangler.jsonc.

Fase 9 — QA e deploy

Use o checklist de migração para a verificação.

Veja também