The all-in-one JavaScript toolkit that augments Node.js instead of trying to replace it
A TypeScript-first toolchain for Node.js. Run TypeScript files, package.json scripts, and local CLIs on the node and package manager you already have. No new runtime, no lock-in.
$ nub index.ts # TypeScript-first Node.js runtime$ nub run dev # 24× faster pnpm run$ nubx prisma generate # 19× faster npx$ nub install # 2.5× faster pnpm install$ nub watch src/server.ts # native watch mode$ nub pm shim # built-in Corepack-style shims$ nub node install 26 # Node version manager
The toolchain
An all-in-one toolkit for Node.js
One Rust binary to run your files and scripts, install dependencies, and manage Node itself.
A TypeScript-first Node.js
Run .ts, .tsx, and .jsx on stock Node with full support for tsconfig.json, .env loading, and modern syntax and Web APIs.
# run a TypeScript file$ nub index.ts# restart on changes$ nub watch src/server.ts
A TypeScript-first Node.js
Nub adds support for TypeScript, JSX, decorators, .env files, YAML/TOML imports, and modern syntax and APIs on top of stock Node. Flag-for-flag compatible with node. Powered by Rust and oxc.
Architecture
Transpiles in Rust, runs on real Node
Nub transpiles your code in memory with oxc (compiled into a native Node addon) and runs the output on the stock node binary. There’s no Nub runtime, just real Node. Runs on Node.js 18 LTS and newer.
$ nub app.ts# oxc transpiles in memory, then stock node runs itrunning on node v26.4.0
TypeScript-first
Full TypeScript support, not just type stripping
Recent versions of Node support type stripping, which erases annotations but rejects non-erasable syntax. Nub’s load hook transpiles each file through its native addon instead, so enums, parameter properties, and extensionless imports that Node doesn’t allow all just work.
import { Model } from "./base" // extensionless → ./base.ts
enum Status { Draft, Sent, Paid }
class Invoice extends Model {
constructor(public status = Status.Draft) {} // parameter property
}tsconfig
Respects your tsconfig.json
Nub resolves your tsconfig.json (including "extends") and feeds its paths into Node’s own resolver through a module.registerHooks() resolve hook. No more tsconfig-paths or disagreement between Node.js and your editor.
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@db": ["src/db/index.ts"]
}
}
}Environment
Loads .env files automatically
Nub reads .env, .env.local, and .env.[NODE_ENV] and injects them before Node starts. No dotenv required. Automatic var expansion via ${VAR} just like Vite and Next.js.
# .env
APP=acme
DATABASE_URL=postgres://localhost/${APP}_dev
# No dotenv. No cross-env. No import "dotenv/config".
$ nub server.tsModern syntax
Decorators, JSX, and using
Nub supports decorators and JSX, transpiling it according to your tsconfig.json settings. Full support for emitDecoratorMetadata and explicit resource management, no build step required.
await using db = await connect() // disposed at scope end
@sealed // legacy decorator
class User {}
const view = <Hello name="world" /> // JSX in .tsxLoaders
Import JSON, YAML, and TOML
Import .yml, .yaml, .toml, .json5, and .jsonc files directly. A module.registerHooks() load hook routes them through fast Rust parsers in Nub’s native addon, resolving each import to a plain JavaScript object. (Oh, .txt works too)
import config from "./config.yaml" // parsed object
import flags from "./feature.jsonc" // comments stripped
import pkg from "./Cargo.toml" // parsed object
import prompt from "./prompt.txt" // string
const { host, port } = config // destructure fieldsAuto-restart
A dependency-aware watch mode
Powered by node --watch, Nub’s watch command watches for changes to your entrypoint or any file transitively imported. It also adds TypeScript/JSX sourcemap support and watches your package.json, tsconfigs, and .env files.
$ nub watch src/server.tsListening on http://localhost:3000↺ src/db.ts changed — restartingListening on http://localhost:3000
Node version management
Auto-installs Node, on demand
Nub reads your .node-version, .nvmrc, or engines/devEngines pin and runs your code on exactly that version. If it isn’t on your machine, Nub downloads it from nodejs.org, verifies the checksum, and installs it on the fly — replacing nvm and fnm. You can also manage versions manually.
$ echo 26 > .node-version$ nub hello.tsUsing Node.js 26.4.0 (resolved from .node-version)Installed in 9.8sHello world!
Performance
Negligible overhead over plain Node
Nub transpiles each file in memory through its native Rust addon, then runs it on the real node binary. Its own startup is a few milliseconds of Rust, dwarfed by Node’s, so a .ts file starts up on par with plain node and about 2.9× faster than tsx, which loads esbuild and its loader hooks on every run.
run a TypeScript file · macOS
Compatibility
Node-compatible, because it is Node
Your code is transpiled and executed with the stock node binary, so it runs on real Node, not a reimplementation. That’s where the compatibility comes from.
Deno’s Node-compat corpus, scored against stock Node. Nub’s misses come from auto-enabling experimental features and loading native addons.
View benchmark repo
truly drop-in
Flag-for-flag compatible with node
Nub is actually a drop-in replacement for node. Every V8 and Node flag, NODE_OPTIONS, argv, exit codes, and signals behave identically — Nub forwards them straight to the real node it runs. Swap node for nub in any script, Dockerfile, or CI step; nothing else changes.
$ NODE_OPTIONS='--enable-source-maps' nub \ --max-old-space-size=8192 \ --import ./instrument.js \ app.ts --port 3000
No Nub-specific APIs
Zero lock-in
Nub is not a runtime. Your code is run using stock node. Nub simply transpiles your code, polyfills missing global APIs, sets some flags, and makes additive modifications to Node’s module resolution to improve TypeScript support.
- No Nub global
- No nub:* module namespace
- No @nub/* npm scope
- No "nub" field in package.json
- No nub-named lockfile
Forward compatibility
Modern APIs and syntax, fully supported
Nub polyfills APIs like Temporal and Worker, adds support for new ECMAScript syntax like using, and unflags all experimental Node.js features.
A 24× faster pnpm run
A drop-in for npm run and pnpm run with lifecycle hooks, npm_* env vars, and arg forwarding, without the JS startup these Node-based tools pay on every call.
Performance
Run package.json scripts at the speed of Rust
Whereas scripts run with npm run or pnpm run feel perceptibly laggy — they’re Node.js programs, so each call cold-loads the package manager’s own JavaScript (config, workspace probe, the works) before your script runs — Nub’s runner is a Rust binary with no startup of its own.
script dispatch · warm · 50 runs · macOS
Drop-in for pnpm run
Flag-for-flag compatible with pnpm run
Nub accepts pnpm run’s flags with the same spelling and semantics, down to the obscure recursive ones. Swap pnpm for nub and your CI scripts run unchanged, only faster.
$ nub run build # plain script run$ nub run test -- --coverage # pass args through$ nub -r --if-present lint # skip packages without it$ nub -r --parallel --no-bail test # all at once, collect all results$ nub -r --resume-from @org/api --stream build # CI restart, streamed
Workspaces
Monorepo-friendly
Nub implements pnpm’s --filter grammar and -r, reading workspaces from package.json#workspaces or pnpm-workspace.yaml. Your existing filter commands work unchanged.
$ nub -r run build # every package, topo-ordered$ nub --filter @org/api dev # one package$ nub --filter ...@org/web build # + its deps$ nub --filter "[main]" test # changed since main
A 19× faster npx
The nubx command resolves node_modules/.bin in Rust and execs the binary directly — no Node process in the wrapper. A drop-in for npx and pnpm dlx: it runs a local bin, or fetches an uninstalled one from the registry.
Performance
Makes commands feel instantaneous
When invoking native CLIs like esbuild, npx itself (written in JS) adds a noticeable 200ms of cold-start latency, even when running a CLI command that’s instantaneous. Nub walks node_modules/.bin and execs the binary directly.
esbuild --version · macOS
Drop-in for pnpm exec
Flag-for-flag compatible with pnpm exec
The nubx and nub exec commands take pnpm exec’s flags, and nub dlx matches pnpm dlx, shell mode included. Swap pnpm for nub and the command you already know runs.
$ nub exec -r tsc --build # across the workspace$ nub exec --parallel vitest # every package at once$ nub dlx -p cowsay -c 'cowsay hi | tr a-z A-Z' # dlx shell mode
Resolution
Works with any package manager
Nub resolves a locally-installed CLI from node_modules/.bin regardless of which package manager put it there — so you get Nub's performance without switching package managers.
$ nubx eslint . # member's .bin first$ nubx prisma generate # then workspace root$ nubx tsc --noEmit # then ancestors$ nubx --node some-cli # run under plain Node
A 2.5× faster pnpm
A pnpm-compatible package manager, built in. It reads the lockfile your project already has — pnpm, npm, or bun — writes the same format back, and is hardened against supply-chain attacks out of the box. Powered by the aube engine.
Meta package manager
Change package managers, keep your lockfile.
Nub autodetects your current manager and updates your existing lockfile in place. No migration needed. Verified roundtrip compatibility for package-lock.json, pnpm-lock.yaml, and bun.lock.
$ nub install # npm package-lock.json → in place$ nub install # pnpm pnpm-lock.yaml → in place$ nub install # bun bun.lock → in place
Drop-in for pnpm
Drop-in pnpm compatibility
Nub’s install and add accept pnpm’s flags with the same spelling and semantics, down to advanced features like the workspace catalog. Swap pnpm for nub and your install commands run unchanged.
# exact pin · devDeps · workspace catalog$ nub add -E -D --save-catalog react$ nub install --frozen-lockfile --prefer-offline --node-linker hoisted
Install speed
Ultrafast installs
Like pnpm, Nub keeps package files in a global content-addressed store and links them into node_modules. Nub embeds aube, a highly optimized Rust-based resolver and linker.
warm frozen install · create-t3-app · 222 deps · macOS
Config compatibility
Mirrors your package manager's config rules
Nub supports all the listed configuration mechanisms, but toggles them on and off based on the conventions of your project’s inferred package manager. There is no Nub-specific configuration file.
| config field | npm | pnpm | yarn | bun | nub |
|---|---|---|---|---|---|
| workspaces | ● | ● | ● | ● | ● |
| overrides | ● | ○ | ○ | ● | ● |
| resolutions | ○ | ● | ● | ● | ● |
| catalog: | ○ | ● | ○ | ● | ● |
| packageExtensions | ○ | ● | ● | ○ | ● |
| allowBuilds | ○ | ● | ○ | ○ | ● |
| trustedDependencies | ○ | ○ | ○ | ● | ● |
| .npmrc | ● | ● | ● | ● | ● |
Supply-chain safe by default
Hardened against supply-chain attacks
The defenses are on out of the box, no config required. Nub treats dependency build scripts as deny-by-default, queries OSV for malicious-package advisories on every fresh resolve, refuses a version whose publish trust evidence weakened against an earlier release, and holds back releases younger than minimumReleaseAge(24h, matching pnpm) so a freshly-compromised version isn’t pulled in.
$ nub add @ledgerhq/connect-kitError: refusing to add malicious package(s):- @ledgerhq/connect-kit (MAL-2023-8697:https://osv.dev/vulnerability/MAL-2023-8697)❌ code=ERR_NUB_MALICIOUS_PACKAGE$ nub install # a version that lost its provenanceError: trust downgrade for nanoid@3.3.14 (trustPolicy=no-downgrade): earlier published version 3.3.7 hadprovenance attestation but this version has no trust evidence❌ code=ERR_NUB_TRUST_DOWNGRADE
Deny-by-default build scripts
Build scripts don’t run until you allow them
Install-time postinstall scripts are where most supply-chain payloads land. Nub skips them by default, names what it skipped, and waits for nub approve-builds. A curated default-trust floor can vouch for a package only after it clears provenance, advisory, and cooling gates.
$ nub installWARN ignored build scripts for 1 package(s): esbuild@0.21.5.Run `nub approve-builds` to review and enable them.code=WARN_NUB_IGNORED_BUILD_SCRIPTS$ nub approve-builds # review + allow, once