Node.js shebang
JavaScript is an interpreted language and its source code needs to be fed to some interpreter to run. If you want to run a JavaScript file using Node.js, you normally run this command:
$ node yourfile.js
By typing the name of the interpreter (node
), you are explicitly telling the shell how to run your script.
But that knowledge can be put inside the script itself so it can be run directly as if it was a binary:
$ ./yourfile.js
This will work only if you have execution permission on that file (you can set that with chmod u+x yourfile.js
for example) and have set the right “shebang”.
Shebang
Shebang or hashbang (#!
) is the first line of the file which tells the OS which interpreter to use. It typically looks like this:
#!/absolute/path/to/the/interpreter [optional params]
Shebang is a OS feature and can be used to run any interpreted language: Python, Perl, etc. For Node.js it can (but often doesn’t) look like this:
#!/usr/bin/node
Node.js will happily ignore this as a comment only if it is the very first line of the file (it won’t work even if there’s an empty line or //comment
line before it). Browsers ignore it too (Chrome 74+, FF 67+).
Most people have a Node.js binary or symlink sitting at /usr/bin/node
. If Node.js is not at /usr/bin/node
, the OS will complain. For example bash would say bad interpreter: No such file or directory script won't execute
. But is there a way to tell the OS to run the script with Node.js no matter where it is installed?
#!node
doesn’t work because shebang requires an absolute path.
Say hello to env
env
is primarily intended to run a command in a modified environment. The emphasis here being “a command” because env
is almost always at /usr/bin/env
while “a command” can be anything that’s on the PATH
.
If instead of /usr/bin/node
we write/usr/bin/env node
, we’re telling OS to run env
and env
will run node
and node
will in turn execute the script.
The short answer
This is the most common shebang for Node scripts:
#!/usr/bin/env node
However, env
has a few other tricks up its sleeve that we can use.
Pass parameters to Node.js
Passing the -S
option to env
causes it to parse whatever comes after which opens up a new door: passing parameters to the command.
For example let’s say we would like to run node with a special flag to enable ESM modules when running the current file. We could use this shebang:
#!/usr/bin/env -S node --experimental-module
Another example: if we want to run another script before running the current script, we can use Node’s -r
option:
#!/usr/bin/env -S node -r ./my/other/file.js
Or to open up the inspection port:
#!/usr/bin/env -S node --inspect
Please note that if you run the script like node yourfile.js
, Node.js will not try to parse arguments from the shebang. It’ll just ignore it. It is the kernel which uses the shebang prior to running the file in order to figure out how to run it.
Set environment variables
Remember we said env
can run a command in a modified environment? That’s actually where it gets its name and it’s very powerful. Let’s say we want our script to run in production mode. We could set the NODE_ENV
environment variable:
#!/usr/bin/env -S NODE_ENV=production node
Without that, the NODE_ENV
would be undefined
or whatever is set at the user’s terminal when running the script.
Node.js respects a bunch of environment variables. For example we can use the NODE_OPTIONS
to pass some CLI flags like this:
#!/usr/bin/env -S NODE_OPTIONS=--experimental-modules node
Start with an empty environment
If we wanted the script to run without access to any environment variable at the user’s terminal, we could run it with the -i
flag which stands for “ignore environment”:
#!/usr/bin/env -S -i node
A mere -
implies -i
, so we can also write it like this:
#!/usr/bin/env -S - node
Force-disable DEBUG
Maybe we don’t want to clear all environment variables but block-list a few of them. An example is DEBUG
(if you’re using the popular debug package). Maybe we don’t want the users of the scripts to set the flag when running it as a script. Then we’d use the -u
flag which stands for unset environment variable.
#!/usr/bin/env -S -u=DEBUG - node
If the user runs the script as DEBUG=* ./yourfile.js
they cannot see any debugging info but you can still run it as DEBUG=* node ./yourfile.js
see the DEBUG output.
Lock the Node.js runtime version
Sometimes you want to lock the node
version that is used to run a script. Prior to NPM@3 we could use engineStrict
, but that feature is removed and now we can only set the engines
in package.json
which may or may not exist next to the script and depends on setting the engine-strict
config flag.
But there’s an easier way. Since node
is also an NPM package, and npx
allows running any NPM package, you can write:
#!/usr/bin/env -S npx node@6
This may try download the requested version of Node upon running the script (so it won’t work without internet connection if that particular version of node does not exist in the NPX cache).
Tip: you can check the node version using process.version
Run it with TypeScript
There’s no rule that says we have to run node
. Assuming typescript
and ts-node
are available globally (npm -i g typescript ts-node
), we could specify ts-node
as the interpreter:
#!/usr/bin/env ts-node
And let it run the file as a TypeScript program.
In any of these examples, the file can have the .js
extension or whatever else you prefer. It can even be without extentions!
Have I missed something? Do you have any tip you’d like to add? Please let me know in the comments or reach out on twitter: @alexewerlof
References
- Usage of the
env
command in Unix - The history of shebang in Unix and shebang on wikipedia
- Usage of
chmod
command in Unix and its man page - Why use
env
in shebang (stackexchange)? - Appropriate Node.js shebang (stackoverflow)?
- How does shebang work in Unix (stackoverflow)?
- List of environment variables recognized by Node.js
Did you like what you read? Follow me here or on LinkedIn. I write about technical leadership and web architecture. If you want to translate or republish this article, here’s a quick guide.