CLI Options

A reference for nstd commands, runtime options, and NODE_OPTIONS.

This is a reference for everything the nstd binary accepts on its command line: the commands, the options that go in front of a script, and what NODE_OPTIONS can carry.

#Commands

nstd script.js [args...]   # run a file
nstd run script.js [args...]  # the same, said explicitly
nstd -e 'code'              # evaluate a string
nstd --print-realm          # list the builtins this binary can serve
nstd -v                     # print the version
nstd -h                     # print usage
commanddoes
<file> [args...]run <file> as the program's entry point
run <file> [args...]the same, spelled out
-e, --eval <code>evaluate <code> as CommonJS source
--print-realmboot the realm and report what it can serve
-v, --versionprint the version triple and exit
-h, --helpprint usage and exit

#Options

Everything below goes before the file (or -e, or --print-realm) and becomes process.execArgv inside the program - the same list node publishes there, holding exactly what was typed on the command line.

optionwhat it does
--expose-internalslet require() reach the internal/* modules
--experimental-vfslet require() reach node:vfs (already on without it)
--experimental-require-modulelet require() load an ES module (already on without it)
--no-experimental-require-moduleturn off require() of an ES module
--experimental-stream-iterregister node:stream/iter and node:zlib/iter
--experimental-eventsourcedefine globalThis.EventSource
--no-experimental-websocketremove globalThis.WebSocket and globalThis.CloseEvent
--no-warningssilence process.emitWarning()
--expose-gcdefine globalThis.gc(), which runs a real garbage collection
--pending-deprecationemit the deprecation warnings that are off by default
--preserve-symlinksresolve a dependency to the symlink path, not the target it points to
--preserve-symlinks-mainthe same, for the entry point itself
-r, --require <file>load a CommonJS module before the entry point, repeatable

#-r, --require

Loads one CommonJS module before your program runs. Give it more than once, in either spelling, to load several in the order they were given:

nstd -r ./a.cjs --require=./b.cjs script.js

It is the only option here that a child process inherits: child_process.fork() and a process spawned as process.execPath both start fresh, with no wrapper around them, and the environment is the only thing either carries across - which is why NODE_OPTIONS matters for this one specifically. See Running Code.

#NODE_OPTIONS

nstd reads NODE_OPTIONS from the environment before it looks at the command line, and accepts the same set of options listed above in it:

NODE_OPTIONS='--require ./setup.cjs --no-warnings' nstd script.js

Quoting works the way it does for node: a space outside a quoted run separates options, "..." groups one option's text together, and \ escapes a character inside a quoted run. An option named in NODE_OPTIONS is in force but does not appear in process.execArgv - only what the command line itself said does, which is also how node keeps the two apart. A few things cannot go in NODE_OPTIONS at all, --eval among them, because they say what to run rather than how to run it; those are refused the same way an unknown option is.

#--expose-internals

Registers Node's own internal/* modules and lets require() reach them by name. This is a testing and debugging tool - ordinary programs never need it - mirroring node --expose-internals.

#--expose-gc

Defines globalThis.gc(), backed by a real garbage collection in the underlying engine. It is enough to reliably clear a WeakRef or fire a FinalizationRegistry, but it does not carry V8's full contract: passing { type: 'major-snapshot' } is refused, since there is no heap profiler here to write one.

#--experimental-eventsource and --no-experimental-websocket

These decide whether globalThis.EventSource, globalThis.WebSocket and globalThis.CloseEvent exist at all. Without --experimental-eventsource, a program that checks typeof EventSource finds nothing, the same as under node. --no-experimental-websocket is the opposite direction: WebSocket and CloseEvent exist by default and this flag removes them.

#Any other option is refused, not ignored

Only the options in the table above are accepted, and the rule for what belongs there is that nstd genuinely implements the option's effect - not that it merely recognizes the name. So an option like --permission or a V8 flag is treated exactly like a typo: printed to stderr as bad option: <name> and exits with status 9, the same status node uses for an option it does not know. Nothing is silently accepted and dropped.

This reads stricter than it is, and it is worth one honest paragraph on why it works in your favor. A flag that is accepted but does nothing is a worse failure mode than a flag that is refused: your program runs, appears to start fine, and then breaks two or three steps later on a symptom that has nothing obviously to do with the flag you passed. Refusing it up front means the mistake is reported against the actual cause, immediately, instead of against whatever code path happened to depend on the missing feature. If nstd accepts an option, it is because this binary genuinely does the thing that option asks for.