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:

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:

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

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

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:

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:

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

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 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 codeWhen
0The program finished, or --help, --version or --print-realm ran.
1An uncaught exception or unhandled rejection, or process.exit(1).
9An option on the command line or in NODE_OPTIONS was rejected.
70nstd 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.