
# Running Code

> Run files, evaluate code, preload modules, and understand exit codes.

This page covers the everyday ways to run a program with `nstd`, what
`require()` and `import()` can load, how the filesystem behaves, and what
happens when a program exits or throws.

## Run a script

Pass the file you want to run. Anything after it is passed to your program as
arguments:

```sh
nstd script.js one two
```

`nstd run script.js` does exactly the same thing, if you prefer to spell it
out.

Just like Node, nstd decides whether a file is CommonJS or an ES module from
its extension and the nearest `package.json`:

- `.mjs` files run as ES modules.
- `.cjs` files run as CommonJS.
- `.js` files run as CommonJS, unless `package.json` has `"type": "module"`.

Inside your program, `process.argv` looks the same as under Node: the path to
the `nstd` binary, the full path to your script, then `one` and `two`.

## Evaluate code directly

Use `-e` (or `--eval`) to run a snippet of code instead of a file:

```sh
nstd -e 'console.log(require("os").hostname())'
```

Use `-p` (or `--print`) to also print the result:

```sh
nstd -p '1 + 1' # 2
```

As with `node -e`, there's no script path in `process.argv` here, so
`process.argv[1]` is the first argument after the code.

## Load a module first

Use `-r` (or `--require`) to load a CommonJS module before your program
starts. Repeat it to load several, in order:

```sh
nstd -r ./setup.cjs script.js
nstd -r ./a.cjs -r ./b.cjs script.js
```

A preloaded module runs in the same environment as your program, so if it
patches a global or configures a library, your program sees the change. If it
throws an error that nothing catches, your program never starts.

You can also pass `--require` through the `NODE_OPTIONS` environment
variable:

```sh
NODE_OPTIONS='--require ./setup.cjs' nstd script.js
```

This matters for child processes. A child started with
`child_process.fork()`, or by running `process.execPath`, doesn't inherit the
parent's command-line options, only its environment. `NODE_OPTIONS` is the one
way to make a preload reach it. See [CLI Options](/runtime/cli#node_options) for
everything `NODE_OPTIONS` can hold.

## Check what a build supports

`--print-realm` starts nstd's JavaScript environment and reports what's in it,
without running any program:

```sh
nstd --print-realm
```

```
nstd 0.0.1 (quickjs-ng ..., zig ...)
node v26.10.0 on linux/x64

67 builtin(s) registered, 0 unavailable
  + assert
  + buffer
  + fs
  ...
```

Each builtin module that loaded is marked with `+`. Any that failed are marked
with `-`, followed by the reason. It's the quickest way to check whether a
build has what your program needs.

## Loading modules with `require()` and `import()`

`require()` can load:

- the `node:` builtins compiled into the binary,
- your own files on disk, and
- packages in `node_modules`, resolved the same way Node does it, including
  `"exports"` and `"imports"` in `package.json`.

`import()` and `.mjs` files work too. That includes relative paths, packages,
`node:` builtins, importing CommonJS and JSON files, import attributes,
circular imports and top-level `await`. You can also:

- `require()` an ES module,
- call `import()` from a CommonJS file, with the path resolved relative to
  that file, and
- use named imports from a CommonJS file, such as
  `import { readFile } from "./some.cjs"`.

See [Node.js Compatibility](/runtime/compatibility) for what module loading
doesn't cover yet.

## The filesystem

nstd uses the real filesystem of the machine it runs on. There's no sandbox
and no virtual root. Relative paths resolve against the current working
directory, and `fs.watch()` and `fs.watchFile()` both work.

> [!WARNING]
> Treat a script run with `nstd` exactly like one run with `node`: it can
> read, write and delete anything the current user can.

## Exit codes and uncaught errors

When your program finishes without calling `process.exit()`, it exits with
`process.exitCode`. That defaults to `0`, and you can set it yourself, just as
in Node.

An uncaught exception stops the program: the error is printed and the exit
code is `1`. A `process.on("uncaughtException", ...)` listener (or
`process.setUncaughtExceptionCaptureCallback()`) can handle it and keep the
program running.

An unhandled promise rejection works the same way. A
`process.on("unhandledRejection", ...)` listener sees it first, and if nothing
handles it there, it stops the program like an uncaught exception.

These are the exit codes you can expect, all matching `node`:

| Exit code | When                                                                     |
| --------- | ------------------------------------------------------------------------ |
| `0`       | The program finished, or `--help`, `--version` or `--print-realm` ran.   |
| `1`       | An uncaught exception or unhandled rejection, or `process.exit(1)`.      |
| `9`       | An option on the command line or in `NODE_OPTIONS` was rejected.         |
| `70`      | nstd itself failed to start. This is a bug in nstd, not in your program. |

If the script file doesn't exist, nstd prints an error and exits with `1`
before running anything.
