← Blog
The Nub Team

Nub 0.9.2

Renames the standalone loader package to @nubjs/runner and gives it nubr, one command that runs a TypeScript file, a package.json script, or an installed bin.

Nub 0.9.2 renames the standalone loader package to @nubjs/runner and gives it a command. nubr runs a TypeScript file, a package.json script, or an installed bin, on plain Node, with no Nub binary anywhere.

Nub is an all-in-one toolkit for Node.js written in Rust. The nub command is flag-for-flag compatible with node, while adding full support for TypeScript, JSX, tsconfig.json, .env loading, and modern Web and ECMAScript APIs. It also includes a fast script runner (nub run), package runner (nubx), Node version manager (nub node), and pnpm-compatible package manager.

Important

The standalone loader package moved. @nubjs/loader and its eight platform packages are now @nubjs/runner and @nubjs/runner-*. The old packages stay on the registry at 0.9.1 and stop receiving updates, so switch the dependency and any --import flag over. Nothing else about the preload changed — the entry points, the hooks and the resolution behavior are the same.

The unified runner

The standalone package puts Nub's TypeScript runtime on plain Node as an ordinary npm package — about 5 MB, with no package manager, no registry client and no network code in it. Until now it was a preload only: something you passed to node --import. It now ships a command too.

npm install --save-dev @nubjs/runner

The full CLI splits running things across three verbs. A package that ships a single bin has to unify them, so nubr covers all three:

nubr app.ts        # a file
nubr build         # a package.json script
nubr vitest run    # an installed node_modules/.bin entry

Names resolve most-specific-first — a file, then a script, then a bin. A script wins over a bin of the same name, matching npm, because a script usually wraps the bin it is named after. A directory comes last, so a build/ directory cannot shadow the build script.

Scripts run through the same shell npm uses, with node_modules/.bin on the path and pre/post hooks honored, and every Node process a script starts inherits the TypeScript support. An installed bin gets the same environment a script does; nothing is fetched from the registry, so the tool has to be installed already.

A file run re-execs Node with --import rather than arming the hooks in the current process. That costs about 30 ms and buys exactness: Module.runMain routes the entry through the CommonJS loader, which cannot load an ES module below Node 22.15, and a worker thread inherits its preload from the option store rather than from process.execArgv. Re-execing makes the command identical to the documented preload form by construction instead of by enumeration.

The preload is unchanged and still the right tool when the node invocation is not yours to change:

node --import @nubjs/runner app.ts           # one run
NODE_OPTIONS="--import @nubjs/runner" vitest # a tool that spawns node

The unified runner page documents the command, the preload, and the deployment shape. (#840)

Windows lifecycle scripts

A dependency's install script that assumes a POSIX shell now gets one on Windows: dependency lifecycle scripts run through the bundled busybox sh rather than cmd.exe. (#940)

The full release notes list every change in this release.

Get started

View repo