A promessa que ninguém ousou fazer
Compila TypeScript. Gera um binário nativo. Sem Node. Sem V8. Sem engine JavaScript dentro do executável. Parece piada, mas é exatamente o que o Scriptc, projeto da Vercel Labs, entrega.
Quando o repositório apareceu no GitHub nesta semana, a reação inicial da comunidade foi previsível: “mais um compilador hypado que vai morrer em 3 meses”. Justo. A própria Vercel já enterrou o ZeroLang, que acumulou 1.200 commits e parou do nada em junho. Mas o Scriptc tem algo que esses projetos abandonados não tinham: 800 testes de paridade byte a byte contra o Node.js, binários de 320KB que iniciam em 4 milissegundos, e um pipeline de compilação que faz qualquer dev de sistemas levantar a sobrancelha.
Eu não costumo me empolgar com ferramentas novas. Depois de ver dezenas de “Node.js killers” irem pro cemitério, meu ceticismo default é alto. Mas o Scriptc mexeu comigo por um motivo específico: ele não tenta substituir o Node. Ele elimina o runtime inteiro.
Como o Scriptc funciona (sem enrolação)
O pipeline é direto:
- Seu código TypeScript passa pelo compilador oficial do TypeScript para checagem de tipos
- O código é rebaixado para uma representação intermediária (IR)
- A IR é convertida para C
- O Clang compila o C em binário nativo
É isso. Sem mágica, sem VM escondida, sem interpretador rodando por baixo dos panos.
O compilador opera em três camadas, que a Vercel chama de “tiers”:
| Tier | Comportamento | Resultado |
|---|---|---|
| Tier 1 (Static) | Compila para código nativo puro | Sem engine no binário |
| Tier 2 (Dynamic) | Embarca o QuickJS-NG (~620KB) | Para código que não pode ser estático |
| Tier 3 (Rejected) | Falha na compilação | Com erro específico e sugestão de rewrite |
O Tier 1 é o padrão. Se seu código usa classes, closures, async/await, a stdlib, ou APIs do Node como fs, path, process e http, ele compila direto para nativo. Nada de engine JavaScript no executável final.
O Tier 2 existe para a realidade: quando você precisa de dependências npm que distribuem JavaScript puro, ou quando o código é muito dinâmico para compilar estaticamente. Nesse caso, você passa a flag --dynamic e o compilador embarca o QuickJS-NG, um engine leve de ~620KB. Compare com o V8 do Node, que sozinho já ocupa dezenas de megabytes.
O Tier 3 é onde o Scriptc mostra maturidade: em vez de compilar errado ou gerar comportamento inesperado, ele rejeita o código, mostra um código de erro específico, e sugere como reescrever.
Os números que importam
Vamos aos benchmarks, rodados em hardware Apple M-series:
| Métrica | Scriptc | Node.js | Go | Nota |
|---|---|---|---|---|
| Startup | ~2.4ms | ~47ms | ~2ms | Scriptc empata com Go |
| Binário (hello world) | ~320KB | ~120MB (SEA) | ~2MB | 375x menor que Node SEA |
| Memória | 1 a 4MB | 67 a 116MB | ~8MB | Até 29x menos que Node |
O hello world do Node como Single Executable Application (SEA) pesa entre 60 e 120MB. O do Scriptc? 320KB. Não é typo. Trezentos e vinte kilobytes.
Startup de 2.4ms significa que você pode usar TypeScript para CLIs que precisam responder rápido. Scripts que hoje levam 47ms só para o Node inicializar passam a rodar em tempo equivalente ao de um binário Go ou Rust.
A memória é outro ponto brutal. Se você roda containers, sabe o peso que o Node carrega. Um processo Node típico consome 67 a 116MB de RAM antes de executar qualquer linha do seu código. O Scriptc comprime isso para 1 a 4MB.
O que compila e o que não compila
A lista de features suportadas é surpreendentemente longa:
Linguagem:
- Classes com herança simples e dispatch dinâmico
- Closures com semântica de captura do JavaScript
- Generics (monomorphizados, como templates em C++)
- Discriminated unions
async/awaitsobre fibers stackful- Exceptions com
finally - Destructuring, spread, rest parameters
- Getters/setters, iterators, template literals
- Expressões regulares
Standard Library:
- Strings com semântica UTF-16 exata (sim, incluindo surrogate pairs)
- Arrays, Maps, Sets com ordenação JavaScript-exact
- JSON com casts validados em runtime
- Math, typed arrays, Buffer, Error hierarchies
APIs do Node.js:
- Filesystem (sync e promises)
- Path, process, child_process com streams
- Crypto, URL, compression
- Timers, signal handlers
- O stack completo de rede:
net,http,https,tls(usando mbedTLS), UDP, DNS - File watching e readline
Web Standards:
- Fetch API
- WHATWG subset (streams, Headers, AbortSignal)
Eu li essa lista três vezes porque parecia boa demais. Mas o mecanismo de testes diferenciais é real: 800+ programas rodam tanto no Node quanto como binário nativo, e a saída precisa ser idêntica byte a byte. stdout, stderr e exit code.
O elefante na sala: é tudo IA?
A discussão no Hacker News foi inevitável. Um commit único adicionou centenas de milhares de linhas de código. Vários devs apontaram que o codebase parece ter sido gerado por IA (provavelmente Claude), e as críticas foram pesadas. Um comentário resumiu o sentimento: “isso fede a psicose de IA com armas nucleares”.
É uma crítica válida? Parcialmente.
O código gerado por IA tem um problema real de manutenção a longo prazo. Se a equipe não entende profundamente cada decisão arquitetural do compilador, qualquer bug em edge cases vai ser um pesadelo. E compiladores são 90% edge cases.
Por outro lado, o mecanismo de testes é sólido. Testes diferenciais contra o Node, com verificação de memory safety via AddressSanitizer e auditoria de reference counting. Se vaza memória ou tem use-after-free, o build falha. Isso não é vibe coding: é engenharia com safety net.
A pergunta certa não é “IA escreveu o código?” e sim “os testes cobrem os cenários que importam?”. E a resposta, pelo menos olhando o que está público, é que sim.
Comptime: execução em tempo de compilação
Uma feature que passou despercebida pela maioria é o comptime:
const config = comptime(() => {
// Esse código roda no build, não no runtime
return JSON.parse(readFileSync('config.json', 'utf-8'));
});
O comptime executa TypeScript durante a compilação, dentro de uma VM isolada no compilador, e injeta o resultado como literal no binário. Se você conhece comptime do Zig, é exatamente a mesma ideia.
Na prática, isso significa que configurações, feature flags, e qualquer dado estático pode ser resolvido em build time, eliminando I/O e parsing em runtime.
FFI nativo: chamando C direto do TypeScript
Com a flag --ffi, você pode fazer chamadas diretas ao C ABI a partir de declarações TypeScript:
// Declaração type-safe
declare function sqlite3_open(filename: string, db: Pointer): number;
// Uso direto, sem binding manual
const db = new Pointer();
sqlite3_open("data.db", db);
Isso abre possibilidades que antes exigiam N-API ou node-ffi: bindings nativos para SQLite, OpenGL, CUDA, qualquer biblioteca C. Sem overhead de marshalling entre JavaScript e nativo, porque não tem JavaScript rodando.
Pensa no que isso significa na prática. Hoje, se você quer chamar uma lib C do Node, precisa escrever um addon com N-API em C/C++, compilar com node-gyp (boa sorte com isso no Windows), e lidar com o overhead de crossing boundary entre o V8 e o código nativo. Com Scriptc, a chamada é direta. O TypeScript vira C, e a chamada FFI é uma chamada de função normal no binário final.
Runtime validation: casts que funcionam de verdade
Uma decisão de design interessante: o Scriptc trata as casts do TypeScript como contratos verificáveis. No TypeScript convencional, as é uma mentira que o compilador aceita sem questionar:
// No TypeScript normal, isso compila sem erro
// mas explode em runtime se o JSON não tiver "name"
const user = JSON.parse(data) as User;
No Scriptc, esse as vira uma validação real. Se o JSON não tem a estrutura esperada, o programa lança um TypeError com o caminho exato do campo que falhou. É como ter Zod embutido no compilador, sem dependência extra, sem schema duplicado.
Isso sozinho já justifica experimentar a ferramenta. Quantos bugs em produção nascem de um as any ou um as Config que nunca foi validado?
Coverage: saiba exatamente o que compila
O comando scriptc coverage analisa seu código e mostra, statement por statement, o que compila estaticamente e o que cairia pro Tier 2:
$ scriptc coverage src/server.ts
Statements: 94.2% static (1,247 / 1,324)
Dynamic sites:
src/plugins/legacy.ts:42 - eval() call
src/utils/reflect.ts:18 - Reflect.get with computed key
src/vendor/old-lib.ts:* - untyped npm dependency
É uma ferramenta de diagnóstico que te mostra exatamente onde seu código perde performance. Refatore os pontos dinâmicos, rode coverage de novo, e veja o percentual subir. Gamificação involuntária de código limpo.
Por que a comunidade está cética (e por que faz sentido)
O ceticismo do HN não é gratuito. Existem problemas reais:
1. O QuickJS como fallback é lento
Quando o código cai pro Tier 2 (dynamic), o engine embarcado é o QuickJS-NG. Ele é leve (620KB), mas a performance é ordens de magnitude inferior ao V8. Se metade do seu código precisa de --dynamic, o ganho de performance evapora.
2. A Vercel tem histórico de abandonar projetos
O ZeroLang ganhou 1.200 commits e morreu silenciosamente. O Turbopack levou anos para atingir paridade com o Webpack. A Vercel é ótima em gerar hype, nem tão boa em manter projetos de longo prazo que não alimentam diretamente o Next.js.
3. Competidores já existem
O Porffor, um projeto solo, já passa ~68% do Test262 (a suíte oficial de testes do JavaScript). O AssemblyScript faz TypeScript para WebAssembly há anos. O Static TypeScript da Microsoft Research explorou ideias similares. Nenhum ganhou tração massiva.
4. Compilar JavaScript é intrinsecamente difícil
JavaScript é uma linguagem projetada para ser interpretada. eval(), with, protótipos dinâmicos, coerção de tipos: tudo isso existe para ser resolvido em runtime. Compilar estaticamente significa ou ignorar esses features (limitando a compatibilidade) ou emular eles nativamente (eliminando o ganho de performance).
O Scriptc faz a primeira opção: rejeita o que não consegue compilar. É uma decisão pragmática, mas significa que seu código precisa ser “Scriptc-friendly” para aproveitar o Tier 1.
Onde o Scriptc faz sentido hoje
Se eu fosse apostar em cenários de uso imediato:
CLIs e ferramentas de dev
Binários de 320KB que iniciam em 2.4ms. Sem dependência de Node instalado. Distribuição é copiar um arquivo. Para ferramentas internas, scripts de CI/CD, e CLIs de linha de comando, isso é transformador.
Microserviços em containers
Imagens Docker minúsculas. Um binário Scriptc + Alpine Linux resulta em containers de poucos megabytes. O cold start em serverless seria praticamente instantâneo.
Edge computing
Cloudflare Workers, Deno Deploy, Vercel Edge Functions: todos executam JavaScript em runtimes leves. Com Scriptc, você poderia compilar para binários nativos que rodam direto no edge, sem engine.
IoT e embarcados
1 a 4MB de RAM. Sem runtime pesado. TypeScript compilado para ARM. Para dispositivos com recursos limitados, isso abre portas que o Node nunca conseguiu.
Scripts de automação e DevOps
Se você mantém scripts em Bash porque “Node é pesado demais para um script simples”, o Scriptc elimina essa desculpa. Escreva em TypeScript com type safety, compile para um binário estático, distribua sem dependências. O devops da sua equipe não precisa ter Node instalado para rodar a ferramenta.
Scriptc vs. o ecossistema atual
| Ferramenta | Abordagem | Engine | Binário típico |
|---|---|---|---|
| Node.js | Interpreta JS via V8 | V8 (~30MB) | N/A (precisa do Node) |
| Bun | Runtime rápido com JavaScriptCore | JSC (~20MB) | ~60MB (SEA) |
| Deno | Runtime seguro com V8 | V8 (~30MB) | ~80MB (compile) |
| Scriptc | Compila TS para nativo | Nenhum (Tier 1) | ~320KB |
| Porffor | Compila JS para Wasm/nativo | Nenhum | Variável |
A diferença filosófica é clara: Node, Bun e Deno são runtimes que carregam um engine JavaScript completo. Scriptc é um compilador que elimina o engine.
O que vem por aí
O repositório é Apache 2.0, o que significa que forks e contribuições são bem-vindos. O suporte primário é macOS ARM, com Linux e Windows via cross-compilation. A documentação no scriptc.dev ainda é básica, mas funcional.
O que eu gostaria de ver nos próximos meses:
- Benchmarks em workloads reais (servidores HTTP com carga, processamento de dados)
- Suporte melhorado para Linux como plataforma primária
- Integração com o ecossistema Vercel (deploy direto de binários Scriptc no Edge)
- Mais transparência sobre o papel da IA na geração do código
Se a Vercel mantiver o projeto vivo (grande “se”), o Scriptc tem potencial para mudar como pensamos sobre TypeScript em produção. Não como substituição do Node para apps complexas, mas como alternativa para os milhares de scripts, CLIs e microserviços que não precisam de 120MB de runtime para rodar um console.log.
A pergunta que fica: quantos dos seus projetos TypeScript realmente precisam de um engine JavaScript completo rodando por baixo?
Fonte de inspiração: Scriptc: TypeScript-to-Native Compiler (GitHub)













