- scriptc compila TypeScript común en binarios nativos pequeños que se ejecutan sin Node, V8 ni un motor JavaScript, manteniendo la comprobación de tipos del compilador real de TypeScript y la compatibilidad con el comportamiento de Node
- Determina si es posible compilar estáticamente según la estructura del código y, por defecto, genera código nativo; solo cuando se elige
--dynamicejecuta JavaScript de paquetes npm y código de tipoanycon quickjs-ng - Soporta desde clases, genéricos,
async/await, excepciones y expresiones regulares hasta APIs de servidor de Node,fetchy dependencias npm; las sintaxis no soportadas se rechazan con código de error, marco de código y sugerencias de corrección - Ejecuta más de 800 programas tanto en Node como en binarios nativos para comparar la salida y el código de salida, y verifica errores de memoria con AddressSanitizer y auditoría de conteo de referencias
- En mediciones sobre Apple M series, el tiempo de arranque es de unos 2.4 ms, los binarios estáticos miden 170~200 KB, el RSS típico es de 1~4 MB, y al incluir el modo dinámico y dependencias embebidas el binario queda en torno a 3 MB
Modelo de compilación estática
- Usa TypeScript existente sin dialectos ni anotaciones adicionales, y aplica el nivel de rigor de comprobación de
tsconfig.jsony la biblioteca reales2025de TypeScript- Si el proyecto tiene
@types/node, también lo incluye en la comprobación de tipos - El código alcanzable que no puede bajarse emite diagnósticos precisos y detiene la compilación
- Si el proyecto tiene
scriptc coveragemuestra la cantidad de sentencias analizadas, el porcentaje que se compila estáticamente, los elementos bloqueantes y los códigos de error- En el ejemplo, 99% de 4,451 de 4,481 sentencias se compilan estáticamente
- La forma de ejecución se divide explícitamente en tres etapas
- Compilación estática: es el modo predeterminado y convierte a código nativo sin motor JavaScript
- Ejecución dinámica: al especificar
--dynamic, incluye unos 620 KB de quickjs-ng para ejecutar JavaScript de paquetes npm y código de tipoany- Valida en tiempo de ejecución todos los valores que pasan al código estático
- Si el valor no coincide con el tipo declarado, lanza un
TypeErrorcapturable sin corromper memoria
- Rechazo: el código que no puede procesarse recibe un código de error, un marco de código y, por lo general, sugerencias de corrección; no se compila mal en silencio
TypeScript y biblioteca estándar soportados
- Entre las funciones del lenguaje soporta clases de herencia simple y despacho dinámico, closures, monomorfización de genéricos, uniones discriminadas, desestructuración, spread, template literals, getters/setters e iteradores
- Desvirtualiza el despacho dinámico cuando se demuestra que es seguro
- Las uniones discriminadas se manejan como valores de etiqueta usando el narrowing de TypeScript
async/awaitse implementa con fibras con pool de stacks y planificación ajustada a JavaScript- Soporta excepciones y
finally, además de parámetros opcionales, por defecto y rest
- Las expresiones regulares usan el mismo intérprete de bytecode compatible con ECMAScript que usa QuickJS, y solo se enlazan en los binarios que utilizan expresiones regulares
- La biblioteca estándar incluye strings que respetan la semántica UTF-16, arrays, Map y Set con las mismas reglas de orden e identidad que JavaScript
- Las conversiones de tipos de
JSONpasan por validación en tiempo de ejecución - También ofrece
Math, typed arrays,Buffery una jerarquíaErrorcon soporte paracatchtipado
- Las conversiones de tipos de
APIs de Node y web
- Las APIs de Node soportan
fs,path,process,child_process,os,crypto,url/URL,zlib, timers y manejadores de señalesfsofrece APIs síncronas y basadas en Promisechild_processsoporta streams por pipe- El event loop no tiene dependencias externas
- La pila de servidor incluye
net,http,https,tls,dgram,dns,fs.watchyreadline, y puede compilar servidores proxy reales- Para TLS usa mbedTLS incluido
- Implementa
fetch, streams,Headers,AbortSignaly algunas otras APIs web WHATWG sobre la misma pila nativa de red y TLS- Soporta redirecciones, gzip,
AbortSignal.timeouty causas de error al estilo Node - No usa libcurl ni dependencias HTTP del sistema
- Soporta redirecciones, gzip,
Dependencias npm y ejecución dinámica
- En
--dynamicusa la resolución de módulos de Node y comprueba tipos con base en los.d.tsque proporciona el paquete - El JavaScript de los paquetes npm se incluye en el binario durante el build, por lo que en ejecución no lee
node_modules scriptc coverage --dynamicmuestra si cada sentencia se ejecuta en la zona estática o dinámica, además de los bloqueos restantes- El motor JavaScript solo se incluye cuando se elige explícitamente el modo dinámico, por lo que el tamaño del binario no crece en silencio
Corrección y seguridad de memoria
- Las pruebas diferenciales ejecutan más de 800 programas en Node y en el binario nativo, y comparan byte a byte stdout, stderr y el código de salida
- La salida numérica sigue la representación round-trip más corta, y se valida con fuzzing comparando un millón de
doublecontra Node - Los servidores se prueban conectando drivers de cliente reales a ambas implementaciones
- La salida numérica sigue la representación round-trip más corta, y se valida con fuzzing comparando un millón de
- Todo el conjunto de pruebas se vuelve a ejecutar bajo AddressSanitizer y auditoría de conteo de referencias; si hay fugas o use-after-free, el build falla
- Las diferencias intencionales respecto de Node son decenas y se relacionan principalmente con detalles internos de timing y propiedades de objetos de error
- Cada diferencia está documentada y numerada; no se permiten diferencias ocultas
Características de rendimiento
- En Apple M series se mide contra Node, Go, Rust y Zig usando las mismas tareas y salidas idénticas byte a byte
- El tiempo de arranque es de unos 2.4 ms, menor que los aproximadamente 47 ms de Node y similar a Zig, por delante de Go y Rust
- El tamaño del binario estático es de 170~200 KB, y con
--dynamicy dependencias embebidas ronda los 3 MB- Como comparación, el binario de Go presentado mide unos 2 MB y Node SEA entre 60~100 MB
- El uso típico de memoria es de 1~4 MB RSS, mientras que Node usa 67~116 MB
- El runtime compite con lenguajes de sistema en la mayoría de las tareas, manteniendo la semántica
f64ajustada a JavaScript- La inferencia de enteros y el análisis de ownership están en el roadmap
Vías de escape explícitas
comptime(() => ...)ejecuta TypeScript en tiempo de build dentro de una VM aislada del compilador e inserta el resultado como literal en el binario--fficonecta directamente declaraciones TypeScript que solo tienen firma con llamadas C ABI, y enlaza los archivos, objetos y bibliotecas del sistema declarados en el manifiesto- El límite es explícito e incluye información de longitud
- Los detalles se pueden ver en la guía de Native FFI
- Las aserciones de tipo comprobadas como
JSON.parse(...) as Configinsertan código de validación en tiempo de ejecución- Si la validación falla, lanza una excepción con la ruta incorrecta y los tipos esperado y real, como
expected number at $.port, got string
- Si la validación falla, lanza una excepción con la ruta incorrecta y los tipos esperado y real, como
Estructura del compilador
- El proceso sigue el orden
TypeScript → parsing y comprobación de tipos con tsc → lowering → IR tipado → C → clang → ejecutable nativo packages/compilerincluye un frontend basado en la API de tsc, validación y serialización de IR, y backends LLVM y C- Solo el IR se usa como interfaz entre frontend y backend
- LLVM es el generador de código predeterminado, y para programas fuera del alcance soportado usa una ruta alternativa transparente
- C se mantiene como backend de referencia permanente, y
--backend cgenera resultados legibles con información de líneas de origen
packages/runtimeimplementa valores basados en conteo de referencias y recolector de ciclos, fibras con pool de stacks, event loopkqueue, pila de servidor y salida numérica compatible con JavaScript- Usa enlazado por función para incluir en el binario solo las funciones que realmente se usan
packages/cliofrece los comandosscriptc build,scriptc runyscriptc coverage
Instalación y desarrollo
- Se instala con
npm install -g scriptcy requiere clang - La plataforma principal es macOS arm64, y los binarios para Linux y Windows se compilan de forma cruzada
- Cada plataforma se valida con una ruta independiente de pruebas diferenciales
- El desarrollo empieza con
pnpm install && pnpm buildpnpm testejecuta el conjunto de pruebas diferenciales y snapshots de diagnósticosSCRIPTC_SAN=1 pnpm testejecuta las mismas pruebas bajo ASan y auditoría de conteo de referenciaspnpm scriptc build x.ts --emit-irconserva el C generado yx.ir.json
- Todas las funciones se agregan junto con pruebas diferenciales, y solo pueden fusionarse si pasan tanto las pruebas normales como las de seguridad de memoria
1 comentarios
Comentarios en Hacker News
Parece que Vercel saca un proyecto llamativo más o menos una vez al mes para mantener su confiabilidad y presencia. No creo que una empresa o proyecto serio vaya a usar scriptc
Respeto a los contribuyentes, pero el código se nota fuertemente generado con Claude, y que Claude no figure como contribuyente lo hace todavía más sospechoso
proyecto / post relacionado en HN
Porffor lleva tiempo persiguiendo el mismo objetivo. Su desarrollador, CanadaHonk, es extremadamente talentoso, pero el proyecto todavía solo pasa alrededor del 68% de Test262
A menos que yo esté entendiendo mal el alcance del proyecto, es bastante sospechosa la forma en que Vercel avanzó tan rápido
Es el proyecto típico de Vercel. Lleva 5 días publicado, todo es vibe coding, recibió 1,500 estrellas sin motivo, no resuelve el problema de nadie y probablemente dejarán de mantenerlo en unos meses como mucho
En vez de solo criticarlo, intenté aplicarlo directamente a varios proyectos locales, pero en todos aparecieron cientos de errores en el análisis del alcance del código, así que en la práctica era inutilizable
Tal vez se pueda compilar a binario si escribes desde cero sin librerías externas, pero entonces no habría razón para no usar un lenguaje diseñado desde el inicio para compilar bien, como Rust, Go, Zig, D, C, V, Ada, C++, Nim, Swift, Kotlin Native o Haskell
La ventaja de TypeScript no es solo su expresividad, sino también su compatibilidad con el enorme ecosistema de npm. La mayoría de los paquetes define la interfaz con declaraciones de tipos, pero distribuye el código real como JavaScript, así que para usar esos paquetes en la práctica necesitas un motor de JavaScript
Si vas a empezar totalmente desde cero y no piensas usar ningún paquete de npm, sería mejor usar AssemblyScript. Node recomienda explícitamente no distribuir paquetes en TypeScript, porque TypeScript no es retrocompatible ni siquiera entre versiones menores, y la configuración del compilador tampoco es portable entre paquetes
Un proyecto así puede mantenerse en la portada de servicios como HN. Es una estrategia de crecimiento: invertir tokens para crear proyectos aparentes que nadie quiere, ampliar el alcance con la publicación y repetir
En 12 meses, el 90% de los proyectos open source podrían ser solo resultados de vibe coding que se ven interesantes pero no tienen usuarios reales. Ahora hasta es fácil generar compiladores completos, pero lo importante es el mantenimiento a largo plazo y la comunidad; un título llamativo por sí solo no retiene usuarios
Si Vercel hablara en serio, debería adoptarlo en su propio runtime experimental y asumir los costos y riesgos reales
Es un área de problema excelente. Apliqué un trabajo parecido de hacer un compilador que optimice código de runtime con IA a Zod: zod-compiler
Compila esquemas de Zod en cadenas simples de operaciones booleanas en tiempo de build para hacerlos de 2 a 74 veces más rápidos sin cambiar el código, y el plugin reemplaza las llamadas de Zod por parsing compilado. La mayoría de las optimizaciones las escribió Claude en más de 100 iteraciones
Igual que con scriptc, no hace falta juzgar subjetivamente la exactitud porque puedes comparar el resultado con Zod real. Esto se puede aplicar a compiladores, herramientas de serialización, formateadores y planificadores de consultas que tengan una implementación de referencia y benchmarks
Ejecuté con Claude benchmarks de scriptc y Node. Incluso en el resultado más favorable con arreglos de bytes, scriptc sigue siendo unas 7.5 veces más lento que Node 24 después de optimización dedicada
A cambio, el inicio del ejecutable es 12 veces más rápido (1.5ms frente a 18.6ms), usa 72 veces menos memoria (2.5MiB frente a 181MiB) y se convierte en un único ejecutable de 370KB sin dependencias de runtime
Está bien que reconozca la necesidad de ejecutables nativos pequeños y rápidos, pero viendo lo que Java ha pasado durante décadas, soy escéptico sobre su utilidad práctica. GCJ en los 90 era técnicamente aceptable, pero no tenía soporte del ecosistema
Después GraalVM Native abordó el problema de forma más integral y las principales librerías y frameworks empezaron a trabajar en la compatibilidad, pero incluso hoy sigue siendo muy difícil ejecutar perfectamente en nativo una aplicación existente aunque sea simple. Intentos como scriptc son bienvenidos, pero parece que el camino hacia algo práctico será largo y difícil
Una de las razones por las que Excelsior desapareció probablemente es que GraalVM y OpenJ9 se ofrecen gratis. PTC y Aicas siguen funcionando bien gracias a su base de clientes de sistemas embebidos y de tiempo real, que recibe poca atención
Si el desarrollo continúa, tiene potencial para ser un gran logro al nivel de .NET AOT. Apenas lleva unos días publicado, así que por ahora solo da para probarlo por encima, pero si no lo abandonan y sigue evolucionando, podría ayudar bastante al ecosistema
El código hecho con IA, igual que el hecho por humanos, tiene un rango amplio de calidad. Si es software importante, hay que generarlo con los mismos estándares que si lo escribieras tú mismo y revisar todo el código; usado así, es un método excelente. Los proyectos con poca revisión pueden tener menor calidad y menor sentido de responsabilidad por parte de los desarrolladores, así que cuesta más adoptarlos
Las discusiones en línea se van a los extremos de “todo generado por IA” y “no usar IA jamás”, pero en la práctica lo razonable es un punto intermedio que acelere el juicio cuidadoso. El software que no siga eso da menos ganas de usarse por el riesgo de baja calidad o abandono