SCRIPTC : votre TypeScript compilé en binaire natif, sans Node
⏱️ Temps de lecture estimé : 4 minutes
📌 SCRIPTC est un compilateur TypeScript vers natif développé par Vercel Labs : il transforme un fichier .ts ordinaire en exécutable autonome de quelques centaines de kilo-octets, sans Node, sans V8, et sans moteur JavaScript embarqué.
Le poids de Node se constate à chaque déploiement. Un runtime d’environ 120 Mo, un node_modules qui se compte en centaines de mégaoctets, et une trentaine de millisecondes rien que pour afficher une ligne. Pour un outil en ligne de commande, un script de build ou un petit serveur, c’est disproportionné. SCRIPTC part du constat inverse : le TypeScript que vous écrivez est déjà beaucoup plus statique que l’écosystème ne le suppose, alors autant en profiter.
bash
npm install -g scriptc
scriptc run hello.ts # compile et exécute
scriptc build hello.ts -o hello # produit l'exécutable
Le binaire de l’exemple pèse 329 752 octets, démarre en 4 ms contre 35 ms pour Node, et ne lie que libSystem. Node 24 sert à faire tourner le compilateur ; le fichier produit n’en a plus besoin.
Le point qui m’intéresse le plus n’est pas la taille, c’est la visibilité. Chaque construction de votre programme tombe dans exactement un palier, et le compilateur vous le dit.
- 📦 Statique par défaut : classes, closures,
async/await, la bibliothèque standard, et une large surface Node —fs,path,process,child_process, plus la pile serveurnet,http,https,tls,dgram,dns - 🧱
--dynamic: le JavaScript livré par vos dépendances npm s’exécute dans quickjs-ng embarqué (~620 Ko), chaque valeur qui repasse en statique étant validée au passage - 🚫 Rejet : tout le reste échoue à la compilation avec un code
SC, un code frame et le plus souvent un indice de réécriture — jamais de compilation silencieusement fausse
Et avant de compiler quoi que ce soit, scriptc coverage analyse le fichier sans produire de binaire. Il compte les instructions, affiche le pourcentage statique, puis nomme chaque site qui réclame le moteur ou bloque le build. Un rapport qui indique « 4 instructions analysées, 3 statiques, 1 site dynamique en SC2013 » vaut mieux qu’un benchmark.
Ce qui rend le projet crédible, c’est la méthode plutôt que la promesse. Chaque programme du corpus de test tourne à la fois sous Node et en binaire natif, et stdout, stderr et code de sortie doivent correspondre à l’octet près. Le corpus repasse aussi sous AddressSanitizer : une fuite ou un use-after-free casse le build. Le chiffre donne l’échelle — leur propre CLI, compilé en un seul exécutable, remplace 120 Mo de runtime et 181 Mo de node_modules. Il y a aussi une cible WASI, un mode bibliothèque pour iOS et Android, une FFI vers l’ABI C par manifeste JSON, et --emit=c pour lire le C généré quand le code ne fait pas ce qu’on attend.
Là, je tempère. Le projet est explicitement expérimental, la version npm est encore en 0.1.x, et la surface statique n’est pas totale. any sans --dynamic est une erreur de compilation, les objets sont des structs exacts, == entre deux objets reste interdit, et un index hors bornes sur un TypedArray termine le processus au lieu de renvoyer undefined. La compatibilité est réelle mais elle a une largeur : la page dédiée recense 3 662 lignes d’API Node.js, dont une partie reste non supportée. Enfin, macOS arm64 est la plateforme principale, et la compilation croisée vers Linux ou Windows passe par Zig.
Mon avis tient dans le rapport de couverture. La plupart des projets promettent « ça marche chez vous » ; celui-ci affiche ses limites, numérotées, avec le code d’erreur qui va avec. Je ne mettrais pas encore un service en production là-dessus, mais pour un CLI interne, un outil de build ou un binaire à distribuer, c’est le genre de projet qui mérite un essai sur un vrai dépôt.
Sources






