Svelte 5 Somente Estático (Runes)
O que esta skill cobre
"Somente estático" significa que o entregável é uma pasta de HTML/CSS/JS pré-construídos, sem servidor Node/edge no momento da requisição — pode ser hospedado no GitHub Pages, Netlify/Cloudflare/Vercel estático, S3+CloudFront ou aberto como arquivos locais. Essa restrição elimina +page.server.js, +server.js, ações de formulário, hooks.server.js e qualquer caminho de código que presuma um servidor em execução. "Runes" significa que a reatividade do componente é escrita com as primitivas $state/$derived/$effect/$propsdo Svelte 5, não comexport let/$:` / reatividade implícita do Svelte 4.
Essas duas restrições interagem: uma build estática só funciona perfeitamente se nenhum código — incluindo dentro de $effect — presumir que está sendo executado em um servidor por requisição. Mantenha isso em mente o tempo todo; é a fonte da maioria dos bugs quando as pessoas movem um aplicativo SvelteKit para adapter-static.
Passo 1 — Escolha a forma correta do projeto
Pergunte (ou deduza do contexto) se o aplicativo precisa de roteamento baseado em arquivos, várias páginas pré-renderizadas ou HTML por rota otimizado para SEO. Isso determina toda a configuração, então não pule esta etapa.
| Necessidade | Use |
|---|---|
| Página única / dashboard / widget / incorporado, sem roteamento | Vite + Svelte puro (npm create vite@latest -- --template svelte-ts). A configuração estática mais simples possível — não há servidor para introduzir acidentalmente. |
| Várias rotas, cada uma pré-renderizada em seu próprio arquivo HTML, SEO importa | SvelteKit + @sveltejs/adapter-static com prerender = true. |
| Várias rotas, mas SEO não importa (ferramenta interna, extensão Chrome, UI Electron/Tauri) | SvelteKit + adapter-static em modo SPA (um único shell fallback: 'index.html', roteamento no lado do cliente). |
Opte pelo Vite+Svelte puro a menos que precise especificamente de roteamento — há menos o que configurar errado. As etapas completas de configuração, arquivos de configuração e notas de implantação para ambos os caminhos estão em references/static-build.md; leia-o antes de gerar svelte.config.js / vite.config.js de memória, porque as opções de pré-renderização/adaptador mudam entre versões do Svelte e são fáceis de errar sutilmente (por exemplo, esquecer trailingSlash ou definir ssr = false em uma página que você também deseja pré-renderizar, o que produz um shell vazio em vez de HTML real).
Passo 2 — Escreva componentes com runes, não com sintaxe do Svelte 4
Nunca escreva export let, $:, on:click ou uma store writable() simples como a forma principal de gerenciar estado de componente em código Svelte 5 novo — eles ainda compilam na maioria das configurações, mas são padrões legados; o que está sendo perguntado aqui são especificamente os runes. O mapeamento principal:
| Svelte 4 | Svelte 5 |
|---|---|
let count = 0 (reativo por ser de nível superior) | let count = $state(0) |
$: doubled = count * 2 | let doubled = $derived(count * 2) (ou $derived.by(() => ...) para lógica com múltiplas instruções) |
$: { sideEffect(count) } | $effect(() => { sideEffect(count) }) |
export let name | let { name } = $props() |
export let value; bind:value (filho modifica) | let { value = $bindable() } = $props() |
onMount/onDestroy para configuração/limpeza apenas no DOM | $effect(() => { ...; return () => cleanup(); }) |
<slot name="header"> | {#snippet header()}...{/snippet} + {@render header()} |
store writable()/readable(), $store em componentes | $state(...) em um módulo .svelte.js/.svelte.ts, importado diretamente (a reatividade agora funciona fora dos componentes) |
Leia references/runes.md antes de escrever código de runes não trivial — ele cobre as partes que as pessoas invariavelmente erram: $derived deve permanecer puro (sem efeitos colaterais, sem mutação de estado dentro dele), $effect nunca deve escrever no mesmo estado que lê (loop infinito), $state.raw para dados grandes/congelados que você reatribui em vez de mutar, $effect.root/untrack para estado compartilhado fora dos ciclos de vida de componentes, e o padrão exato baseado em classes para estado entre módulos/componentes. Errar esses pontos produz bugs que só aparecem como loops de re-renderização estranhos ou UI desatualizada, que são difíceis de diagnosticar depois — é melhor escrever corretamente desde o início.
Ao migrar código Svelte 4 existente, npx sv migrate svelte-5 cuida da reescrita mecânica (esta é a ferramenta atual; documentação mais antiga pode referenciar svelte-migrate), mas sempre revise o diff — a migração automatizada não corrige de forma confiável padrões de stores como estado entre módulos ou efeitos que deveriam ter sido valores derivados.
Passo 3 — Aplique a checklist de segurança
Hospedagem estática não significa "nenhuma superfície de segurança" — ela remove vetores de ataque do lado do servidor, mas os do lado do cliente (XSS, vazamento de segredos, incorporações inseguras de terceiros) são inteiramente seus, já que não há camada de servidor para capturá-los. Antes de finalizar qualquer tarefa que envolva conteúdo gerado pelo usuário, {@html}, variáveis de ambiente ou scripts de terceiros, consulte references/security.md. As regras de maior impacto, para que não sejam perdidas mesmo em uma tarefa rápida:
- Nunca passe conteúdo não sanitizado para
{@html}. O Svelte escapa automaticamente tudo em chaves{expressão}, mas{@html ...}ignora isso completamente — é a maior fonte de XSS em aplicativos Svelte. Sanitize com DOMPurify (ou uma lista de permissões restrita) imediatamente antes de renderizar, não no início do pipeline onde pode ser contornada por uma edição posterior. - Nunca coloque um segredo real atrás de uma variável de ambiente prefixada com
VITE_(ouPUBLIC_do SvelteKit). O Vite incorpora estas no bundle JS enviado no momento da build — qualquer um pode lê-las nos arquivos construídos. Um aplicativo somente estático não tem servidor algum para esconder um segredo, portanto, se o aplicativo precisar de uma chave de API real, a chave pertence ao servidor do serviço terceiro ou atrás de uma função serverless/proxy separada que eu implanto em outro lugar — sinalize isso para mim em vez de conectar silenciosamente a chave no código do cliente. - Defina uma Política de Segurança de Conteúdo (CSP) mesmo que não haja servidor para enviar cabeçalhos. Hosts estáticos ainda suportam isso através de uma tag
<meta http-equiv="Content-Security-Policy">emapp.html/index.html, ou através de um arquivo de cabeçalhos específico do host (Netlify_headers, Cloudflare Pages_headers, cabeçalhos Vercelvercel.json) — vejareferences/security.mdpara ambos. - Trate cada
href/srcdinâmico como controlado por atacante se puder vir de entrada do usuário ou de uma resposta de API. O Svelte não bloqueia URLsjavascript:em bindings dehref— valide/defina uma lista de permissões para o esquema você mesmo. - Execute
npm audit(ou equivalente) e fixe os lockfiles antes de enviar — sites estáticos ainda enviam o código de cada dependência diretamente para o navegador, então uma dependência comprometida é um comprometimento direto do lado do cliente.
Arquivos de referência
references/runes.md— guia detalhado rune por rune com padrões corretos/incorretos, estado compartilhado baseado em classes, tipagem TypeScript para props/snippets e a tabela de migração Svelte-4-para-5 em detalhes.references/static-build.md— configuração Vite+Svelte puro, configuração SvelteKit+adapter-static (modos de pré-renderização total e fallback SPA), pegadinhas de trailing-slash/base-path e notas de implantação por host (GitHub Pages, Netlify, Cloudflare Pages, S3/CloudFront).references/security.md— checklist completa: XSS/{@html}/DOMPurify, CSP para hosts estáticos (tag meta + modelos_headers), segredos/variáveis de ambiente, links externos seguros, auditoria de dependências, Trusted Types, sensibilidade de localStorage/sessionStorage e uma checklist pré-envio.assets/— arquivos iniciais copiáveis:vite.config.ts,svelte.config.js(adapter-static),_headers(modelo CSP) eSecureHtml.svelte(um componente wrapper DOMPurify).
Incorpore estes conforme necessário em vez de reproduzir seu conteúdo de memória — os nomes das opções de configuração e os detalhes específicos das diretivas CSP são exatamente o tipo de informação que se desatualiza ou é lembrada incorretamente.