
# 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

```sh
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
```

| command                | does                                        |
| ---------------------- | ------------------------------------------- |
| `<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-realm`        | boot the realm and report what it can serve |
| `-v`, `--version`      | print the version triple and exit           |
| `-h`, `--help`         | print 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.

| option                             | what it does                                                          |
| ---------------------------------- | --------------------------------------------------------------------- |
| `--expose-internals`               | let `require()` reach the `internal/*` modules                        |
| `--experimental-vfs`               | let `require()` reach `node:vfs` (already on without it)              |
| `--experimental-require-module`    | let `require()` load an ES module (already on without it)             |
| `--no-experimental-require-module` | turn off `require()` of an ES module                                  |
| `--experimental-stream-iter`       | register `node:stream/iter` and `node:zlib/iter`                      |
| `--experimental-eventsource`       | define `globalThis.EventSource`                                       |
| `--no-experimental-websocket`      | remove `globalThis.WebSocket` and `globalThis.CloseEvent`             |
| `--no-warnings`                    | silence `process.emitWarning()`                                       |
| `--expose-gc`                      | define `globalThis.gc()`, which runs a real garbage collection        |
| `--pending-deprecation`            | emit the deprecation warnings that are off by default                 |
| `--preserve-symlinks`              | resolve a dependency to the symlink path, not the target it points to |
| `--preserve-symlinks-main`         | the 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:

```sh
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](/guide/usage#loading-a-module-first).

## `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:

```sh
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.
