nub.

Watch mode

Restart-on-change for files and scripts, driven by the resolved dependency graph plus your .env*, tsconfig.json, and package.json — no glob hygiene required.

Watch mode (nub watch) runs your entry point and restarts it whenever something it actually depends on changes. The engine underneath is Node's own --watch, with Nub-side glue that reports the in-memory-transpiled dependency graph — plus the off-graph files that still invalidate a process — back to Node's watcher. The result is a watcher that Just Works for TypeScript projects, with no glob list to maintain.

This page is a cookbook. For how nub runs files in the first place, see Running files; for scripts, Running scripts.

Watch and restart a file

Point nub watch at an entry file. It runs the file, then restarts the process on any change to the file or anything in its resolved import graph.

nub watch src/server.ts

TypeScript, JSX, .env* loading, tsconfig.json paths — everything nub <file> gives you is active here too, because nub watch runs the same augmented Node underneath.

--watch

Flag and subcommand are aliases: nub --watch <file> dispatches to the exact same code path as nub watch <file> — same defaults, same behavior. The flag form exists so node --watch script.ts muscle memory lands in Nub's improved experience without surprise.

nub --watch src/server.ts

Watching a script

The watcher takes a file, not a script: point nub watch at the entry your dev script runs.

nub watch src/server.ts

A --watch placed after a script name is forwarded to the script like any other argument — nub run test --watch runs your test tool's own watch mode, it does not invoke Nub's watcher. See nub run → Forward arguments.

Preserve output

Nub preserves terminal output by default and prints a restart banner, rather than clearing the screen. The previous run's error message is usually the most important thing on screen, so wiping it on every restart is the wrong default.

nub watch src/server.ts
# ── restart [14:32:08] ──

If you prefer Node's clear-on-restart behavior, opt in:

nub watch --clear src/server.ts

What triggers a restart

The watch set is loader-instrumented, not glob-based: nub watch only watches files that were actually loaded as part of the run, plus a small set of off-graph files that invalidate the process when they change. Concretely, a restart fires on a change to:

  • Any file in the resolved dependency graph of the entry — including .ts / .tsx files transpiled in-memory through module.registerHooks() that Node never sees on disk.
  • Your .env* files in their normal precedence (.env, .env.local, .env.<NODE_ENV>, …) — these are read once at boot and aren't in any import graph, so Nub reports them to the watcher explicitly.
  • The tsconfig.json extends chain — editing a tsconfig (for example, changing a paths mapping) restarts the process even though tsconfig isn't imported by anything.
  • package.json.

This is why a plain node --watch silently goes stale on a tsconfig edit, and why Nub reports those off-graph files over Node's internal WATCH_REPORT_DEPENDENCIES channel.

What doesn't trigger a restart

Because the watch set is the import graph rather than a directory glob, files that nothing in the run actually loads are simply not watched. A rebuild that emits dist/foo.js doesn't trigger a restart unless something in the run imported dist/foo.js.

nub watch src/server.ts
# editing dist/** or coverage/** does not restart —
# they aren't in the loaded dependency graph

There is no ignore list to maintain. Most of the ignore / restart-loop-guard hygiene that glob-based watchers require is unnecessary in a loader-instrumented model.

Plain Node --watch

Nub's watch mode is intentionally not byte-for-byte Node — it adds the banner, preserve-output default, and off-graph reporting described above. When you want vanilla Node --watch with no Nub-side glue at all, type node --watch directly in your shell.

node --watch script.js

Nub's PATH shim is per-invocation: it only affects descendants of an active nub ... call, so the node in your shell is always your real Node. There is no --node compat flag on nub watch — for strict-Node watch behavior, this is the path. See the --node flag for where compat mode does and doesn't apply.

  • Running files — how nub <file> executes TypeScript and JSX.
  • Running scriptsnub run and its workspace surface.
  • nubx — running local CLIs from node_modules/.bin.
  • FAQ — short answers across the whole surface.

On this page