12k
All articles

Présentation de Nub, une boîte à outils tout-en-un pour Node.js

Nub est une boîte à outils Rust pour Node.js qui exécute TypeScript, scripts, installations et versions Node sur Node natif, avec lockfiles et sécurité.

OpenReplay Team
OpenReplay Team
Présentation de Nub, une boîte à outils tout-en-un pour Node.js

Nub est une boîte à outils en ligne de commande écrite en Rust pour Node.js. Elle transpile le TypeScript, exécute les scripts de package.json, installe les dépendances et provisionne les versions de Node, puis confie l’exécution au binaire node standard que votre projet épingle déjà. Elle complète Node au lieu de le remplacer, et c’est là toute la différence avec Bun ou Deno.

La plupart des équipes qui ont comparé Bun et Deno à Node n’ont jamais dépassé la première question : on ne remplace pas le runtime d’un service en production parce que l’expérience développeur y est plus agréable. Nub emprunte l’autre voie, en se présentant comme une boîte à outils en Rust qui laisse votre Node, votre lockfile et votre gestionnaire de paquets là où ils sont. Voici ce que cela vous apporte, et ce qu’il en coûte de l’essayer.

Points clés

  • Nub est une CLI en Rust qui superpose l’exécution TypeScript, le lancement de scripts, l’installation de paquets et la gestion des versions de Node au binaire node standard : aucun nouveau runtime à qualifier.
  • La prise en charge native de TypeScript par Node se contente de supprimer les annotations et rejette tout ce qui nécessite du code généré, comme les enums, les propriétés de paramètre ou un namespace contenant du code exécutable ; le loader de Nub, lui, compile ces formes.
  • L’installateur de Nub adopte les conventions de pnpm et lit et écrit sur place les lockfiles npm, pnpm et bun existants, les lockfiles yarn étant en lecture seule.
  • Les protections à l’installation ne demandent aucune configuration : les scripts de build des dépendances restent bloqués jusqu’à votre approbation, chaque nouvelle résolution est vérifiée auprès d’OSV, et un délai de 24 heures après publication écarte les versions toutes fraîches.
  • Il n’y a ni API spécifique à Nub ni lockfile Nub, et nub.jsonc est facultatif : désinstaller Nub ramène le projet à du Node standard.

Qu’est-ce que Nub, et qu’est-ce que ce n’est pas ?

Nub n’est pas un quatrième runtime. C’est un binaire unique qui se place devant Node, effectue le travail qui requiert aujourd’hui tsx, nvm, npx et un gestionnaire de paquets, puis exécute le véritable Node. Sa page d’accueil expose clairement le mécanisme : oxc compile vos fichiers en mémoire depuis un addon natif, et le binaire node standard exécute le résultat. Il n’y a aucun runtime distinct en dessous, et le lanceur de fichiers accepte les mêmes options que node.

Rien ne change du côté de votre cible de déploiement. La version de V8, l’ABI C++ contre laquelle vos modules natifs ont été compilés, la surface process sur laquelle votre instrumentation se greffe : tout cela reste le Node que vous livriez déjà. Le mode augmenté requiert Node 18.19 ou plus récent (Node 18 LTS), sur macOS, Linux et Windows, chacun en x64 et arm64.

Le projet est jeune. Le paquet npm @nubjs/nub est publié sous licence MIT et encore en pré-1.0, sur la branche 0.9.x à la dernière version en date, avec des sorties fréquentes.

Comment Nub exécute-t-il du TypeScript sans étape de build ?

La prise en charge native de TypeScript par Node supprime les types au lieu de les compiler. Les annotations sont remplacées par des espaces, et tout ce qui exigerait de générer du JavaScript est rejeté. La documentation de Node énumère les cas : les enums, les namespaces contenant du code exécutable, les propriétés de paramètre et les alias import = déclenchent tous ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX ; les décorateurs échouent à l’analyse ; et comme Node n’ouvre jamais tsconfig.json, les alias paths ne sont pas appliqués. Un mode de transformation plus complet était autrefois accessible derrière --experimental-transform-types, mais Node a supprimé cette option en version 26 : l’effacement est donc désormais la seule voie intégrée.

Or ces exclusions correspondent exactement à la syntaxe dont est fait un code NestJS ou TypeORM. Prenons un fichier utilisant un enum, une propriété de paramètre et un import relatif sans extension :

// invoice.ts
import { Model } from "./base"

enum Status { Draft, Sent, Paid }

export class Invoice extends Model {
  constructor(public status: Status = Status.Draft) {
    super()
  }
}

Avec un simple node invoice.ts, l’enum et la propriété de paramètre ne sont pas effaçables et l’import n’a pas d’extension. Avec nub invoice.ts, ce même fichier s’exécute tel quel. Nub confie chaque fichier à son addon natif pour compilation, ce qui explique que l’enum, la propriété de paramètre et l’import sans extension fonctionnent tous. Il parcourt également votre tsconfig.json ainsi que toute configuration dont ce fichier extends, puis transmet les alias paths au résolveur natif de Node via un hook de résolution module.registerHooks().

Les décorateurs ne sont pris en charge que dans une seule variante. L’article de lancement évoque les décorateurs legacy experimentalDecorators, la forme pour laquelle NestJS, TypeORM et Angular sont écrits, accompagnés d’emitDecoratorMetadata. Les décorateurs Stage 3, que TypeScript 5 utilise par défaut, sont rejetés, car la transformation reste une lacune ouverte dans oxc. Le lanceur émet bien des source maps inline, de sorte que les traces de pile pointent vers votre source et non vers la sortie générée. Ce dernier détail n’est pas cosmétique : du TypeScript transpilé qui perd ses source maps produit des traces portant sur du code que personne n’a écrit, source récurrente de temps de diagnostic gaspillé.

Quelles commandes Nub remplace-t-il ?

Le binaire unique de Nub couvre un travail aujourd’hui réparti sur toute une étagère d’outils. La correspondance documentée est directe :

Commande NubRemplace
nub <file>node, tsx, ts-node, dotenv-cli
nub run <script>npm run, pnpm run, yarn run
nubxnpx, pnpm dlx, pnpm exec, yarn dlx
nub installnpm, pnpm, yarn
nub watchnodemon, node --watch, tsx watch
nub nodenvm, fnm, n, volta
nub pmcorepack

Ce tableau ne couvre pas toute la surface. Le README évoque aussi nubr, une commande unique qui exécutera un fichier, un script de package.json ou un binaire de node_modules/.bin, en les essayant dans cet ordre. Elle est distribuée séparément sous le nom @nubjs/runner pour les projets qui ne peuvent pas installer de binaire.

La propriété importante est que ces éléments sont indépendants. Adopter le lanceur de fichiers ne vous oblige pas à adopter l’installateur, et faire passer un script dev de tsx watch src/server.ts à nub watch src/server.ts laisse package.json sous la forme d’un manifeste normal compatible npm. Le projet publie ses propres benchmarks à l’appui de ses affirmations de performance : 24× plus rapide que pnpm run pour le lancement de scripts, 19× plus rapide que npx pour l’exécution de binaires, et 18× plus rapide que pnpm install. Les mesures comparées du README indiquent 14,7 ms pour le lancement d’un script contre 329,9 ms pour npm, et 171 ms pour une installation figée à chaud contre 3193 ms pour pnpm, le tout mesuré sur macOS. Un second benchmark d’installation, réalisé avec hyperfine sur un runner ubuntu-latest face à une arborescence de 1 168 paquets, rapporte 346 ms pour Nub contre 3453 ms pour pnpm.

Le gestionnaire de paquets : conventions pnpm et préservation des lockfiles

L’installateur de Nub n’introduit aucun format de lockfile. Il détermine quel gestionnaire de paquets le projet utilise déjà, à partir de package.json#packageManager ou du lockfile qu’il trouve, puis fonctionne en mode compatibilité en respectant les fichiers de configuration et les variables d’environnement de cet outil. La CLI elle-même adopte les conventions de pnpm : nub install, nub add -E -D react, nub remove, nub update et nub ci se comportent comme la mémoire musculaire s’y attend.

Concernant les lockfiles précisément : ceux de npm, pnpm et bun sont lus et écrits sur place, et ceux de yarn sont en lecture seule. Rien n’est converti, et aucun second lockfile n’apparaît dans le diff. Pour une équipe sous pnpm, c’est la question qui détermine si l’outil est évaluable ou non.

Résolution des versions de Node sans nvm

nub node détermine la version de Node attendue par un projet et la provisionne à la demande. La version provient de .node-version, .nvmrc ou package.json#engines, et une version manquante est téléchargée et mise en cache automatiquement, des verbes explicites restant disponibles : nub node install 26, nub node ls, nub node pin 26 et nub node uninstall 22. Tout cela sans hooks de shell ni réécriture de votre PATH — précisément la partie de nvm qui a tendance à casser en CI et dans les shells non interactifs.

Des valeurs par défaut orientées supply chain, et l’absence de verrouillage

Les protections de Nub à l’installation sont actives sans aucune configuration. Quatre d’entre elles sont documentées. Les scripts de build d’une dépendance ne s’exécutent pas tant que vous n’avez pas approuvé ce paquet. Chaque nouvelle résolution est vérifiée auprès d’OSV pour détecter les versions malveillantes connues. Une version qui a perdu la preuve de confiance de publication qu’une version antérieure portait est refusée d’emblée. Et minimumReleaseAge vaut 24 heures par défaut, la même fenêtre que celle de pnpm, de sorte qu’une version publiée il y a quelques minutes ne peut pas atteindre votre arborescence. L’article de lancement ajoute qu’une dépendance transitive se résolvant vers une URL git+, file: ou une archive tar brute est refusée plutôt que récupérée silencieusement. Si vous avez déjà construit une posture de défense contre les attaques sur la supply chain npm, il s’agit de cette même checklist, mais par défaut plutôt que sous la forme d’un .npmrc à maintenir.

La réversibilité constitue l’autre moitié de l’argument. Nub n’ajoute aucune API à importer, n’écrit aucun lockfile qui lui soit propre, et traite nub.jsonc comme une configuration facultative et non comme une obligation. Désinstallez le binaire et le projet tourne sur du Node standard avec l’outillage qu’il avait auparavant, puisque le code source n’a jamais fait référence à Nub.

Qui devrait essayer Nub, et qui devrait s’abstenir ?

Essayez-le si vous exécutez du TypeScript via tsx ou ts-node, si vous conservez nvm pour épingler les versions et si vous préférez ne pas passer un trimestre à qualifier un nouveau runtime pour en sortir. Commencez par le lanceur de fichiers sur un seul service, laissez l’installateur de côté, et voyez si les frictions de build de la catégorie « enums et décorateurs » disparaissent. Passez votre chemin, pour l’instant, s’il vous faut une chaîne d’outils figée et sans surprise pour un processus de livraison réglementé : un projet en pré-1.0 qui publie des versions à quelques jours d’intervalle n’en est pas encore là. Le coût pour en avoir le cœur net se résume à npm install -g @nubjs/nub et une commande sur un fichier que vous possédez déjà.

FAQ

Comment exécuter un fichier via Nub sans aucune augmentation ?

Utilisez le mode compatibilité : passez --node pour une invocation unique, ou définissez NODE_COMPAT à 1, true ou yes pour couvrir l'ensemble de l'arborescence de processus. Dans ce mode, Nub n'applique strictement rien : pas de hook de chargement, pas de préchargement, pas d'injection d'options et pas de chargement de .env. Il détermine tout de même quel Node le projet épingle et l'installe si nécessaire, de sorte que votre code s'exécute en Node pur sur la bonne version. C'est utile pour distinguer un bug de Nub d'un bug de Node.

Quelles plateformes et quelles versions de Node Nub prend-il en charge ?

Nub distribue des binaires Rust précompilés pour Linux, macOS et Windows, en x64 et arm64, et récupère à l'installation l'addon N-API correspondant à votre plateforme. Les modes augmentés nécessitent Node 18.19 ou plus récent, car c'est là qu'apparaît pour la première fois l'API de hook de loader sur laquelle repose la transpilation à l'import. Sur toute version antérieure, une commande augmentée s'interrompt avec une erreur qui indique la version plancher et vous oriente vers le mode compatibilité.

Pourquoi une installation échoue-t-elle avec ERR_NUB_ALLOW_BUILDS_RENAMED ?

Nub 0.9.0 a renommé la liste d'autorisation de builds de premier niveau dans package.json, de allowBuilds en allowScripts, pour correspondre à l'emplacement que lit npm 12. Un projet qui conserve une map allowBuilds à la racine est refusé avec cette erreur plutôt que simplement averti : le correctif consiste donc à renommer la clé. Un allowBuilds pnpm est un réglage différent, laissé intact, qu'il se trouve dans pnpm-workspace.yaml ou sous package.json#pnpm.

Puis-je utiliser nubx sans changer de gestionnaire de paquets ?

Oui. nubx trouve une CLI installée localement dans node_modules/.bin quel que soit l'outil qui l'y a placée, il fonctionne donc sur un projet installé par npm, pnpm, yarn ou bun sans rien migrer. Il accepte les options de pnpm exec sous les mêmes noms, et nub dlx reproduit pnpm dlx jusqu'au mode shell, de sorte que les lignes de commande dont vous disposez déjà sont directement transposables.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.