1 puntos por GN⁺ 2 시간 전 | 1 comentarios | Compartir por WhatsApp
  • 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 --dynamic ejecuta JavaScript de paquetes npm y código de tipo any con quickjs-ng
  • Soporta desde clases, genéricos, async/await, excepciones y expresiones regulares hasta APIs de servidor de Node, fetch y 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.json y la biblioteca real es2025 de 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
  • scriptc coverage muestra 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
    1. Compilación estática: es el modo predeterminado y convierte a código nativo sin motor JavaScript
    2. Ejecución dinámica: al especificar --dynamic, incluye unos 620 KB de quickjs-ng para ejecutar JavaScript de paquetes npm y código de tipo any
      • 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 TypeError capturable sin corromper memoria
    3. 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/await se 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 JSON pasan por validación en tiempo de ejecución
    • También ofrece Math, typed arrays, Buffer y una jerarquía Error con soporte para catch tipado

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ñales
    • fs ofrece APIs síncronas y basadas en Promise
    • child_process soporta streams por pipe
    • El event loop no tiene dependencias externas
  • La pila de servidor incluye net, http, https, tls, dgram, dns, fs.watch y readline, y puede compilar servidores proxy reales
    • Para TLS usa mbedTLS incluido
  • Implementa fetch, streams, Headers, AbortSignal y algunas otras APIs web WHATWG sobre la misma pila nativa de red y TLS
    • Soporta redirecciones, gzip, AbortSignal.timeout y causas de error al estilo Node
    • No usa libcurl ni dependencias HTTP del sistema

Dependencias npm y ejecución dinámica

  • En --dynamic usa la resolución de módulos de Node y comprueba tipos con base en los .d.ts que 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 --dynamic muestra 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 double contra Node
    • Los servidores se prueban conectando drivers de cliente reales a ambas implementaciones
  • 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 --dynamic y 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 f64 ajustada 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
  • --ffi conecta 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 Config insertan 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

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/compiler incluye 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 c genera resultados legibles con información de líneas de origen
  • packages/runtime implementa valores basados en conteo de referencias y recolector de ciclos, fibras con pool de stacks, event loop kqueue, 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/cli ofrece los comandos scriptc build, scriptc run y scriptc coverage

Instalación y desarrollo

  • Se instala con npm install -g scriptc y 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 build
    • pnpm test ejecuta el conjunto de pruebas diferenciales y snapshots de diagnósticos
    • SCRIPTC_SAN=1 pnpm test ejecuta las mismas pruebas bajo ASan y auditoría de conteo de referencias
    • pnpm scriptc build x.ts --emit-ir conserva el C generado y x.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

 
GN⁺ 2 시간 전
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

    • Una contribución verdaderamente enorme ;-) commit
    • El mismo líder de vibe coding también encabezó zerolang, el “lenguaje de programación para agentes” que Vercel presentó a lo grande en mayo. Hizo 1,200 commits y el desarrollo se detuvo hacia mediados de junio
      proyecto / post relacionado en HN
    • Simon Willison parece haber modificado solo el README, y no parece haber participado de forma sustancial en el proyecto
    • Viendo el historial de contribuciones, parece que una sola persona hizo alrededor del 99% del vibe coding, y tampoco da la impresión de tener experiencia en compiladores
    • Muchos productos SaaS colaboran con Vercel, y en herramientas para desarrolladores Next.js y React son tratados como los SDK de más alto nivel
  • 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

    • scriptc parece resolver esto incluyendo opcionalmente en el bundle un motor quickjs-ng de 620KB cuando necesita ejecutar esas dependencias
    • Más que para código con muchas dependencias, me interesaría usarlo para herramientas de línea de comandos con un propósito claro que necesiten compartir código con proyectos TypeScript más grandes
    • Eso justifica distribuir librerías sin tipos, pero cuesta entender la conclusión de que esa razón supera el valor de los tipos
  • 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

    • Según entiendo, el equipo de Graal también intentó un enfoque de metaintérprete parecido. Buscaban interpretar con la implementación Java de Espresso la carga dinámica de bytecode o la reflexión que la ejecución nativa no pudiera manejar
    • GCJ siempre estuvo más cerca de un prototipo. Un usuario serio habría comprado un JDK comercial con herramientas AOT, como Excelsior JET o BEA JRockit
      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