/flags/npm/npm-run-double-dash-arguments
npm run script -- --flag: pass arguments to an npm script
- tool
- npm
- command
- npm run
- flag
- --
- default
- no extra arguments
Everything after -- in npm run <script> -- <args> is appended to the script's command line. npm run test -- --watch runs jest --watch when the script is jest. Without the --, npm parses --watch as one of its own configuration options, and the script does not receive it.
With and without --
npm init -y >/dev/null
echo 'console.log("argv:", JSON.stringify(process.argv.slice(2)));' > show.js
npm pkg set scripts.show="node show.js"
echo '--- with --'
npm run -s show -- --port 3000 extra
echo '--- without --'
npm run -s show --port 3000 extraOutput
--- with -- argv: ["--port","3000","extra"] --- without -- argv: ["3000","extra"]
Without --, npm consumed --port as a config setting (npm_config_port) and passed only the positional arguments. -s (--silent) hides npm's > name@version script header lines.
Reading the swallowed flag
A flag that npm takes as config is still visible to the script as an environment variable, which some scripts use on purpose:
npm init -y >/dev/null
npm pkg set scripts.env='node -e "console.log(process.env.npm_config_port)"'
npm run -s env --port=4000Output
4000
Relying on that is fragile; pass arguments after -- instead. Recent npm versions also warn about it:
npm init -y >/dev/null
echo 'console.log("argv:", JSON.stringify(process.argv.slice(2)));' > show.js
npm pkg set scripts.show="node show.js" scripts.preshow="node show.js"
echo '--- without --: npm warnings'
npm run show --port 3000 2>&1 | grep '^npm warn'
echo '--- with --: what preshow and show receive'
npm run show -- --port 3000 | grep '^argv'Output
--- without --: npm warnings npm warn "3000" is being parsed as a normal command line argument. npm warn Unknown cli config "--port". This will stop working in the next major version of npm. --- with --: what preshow and show receive argv: [] argv: ["--port","3000"]
The second part also shows that arguments reach only the script you named: the preshow script that npm runs first gets an empty list, and so would a postshow script.
Variants
| Command | What the script receives |
|---|---|
| npm run build -- --watch | build command + --watch |
| npm test -- -t "login" | test command + -t login (npm test is npm run test) |
| npm start -- --port 8080 | start command + --port 8080 |
| npm run build --watch | nothing extra; npm sets npm_config_watch |
How the arguments reach the script
npm does not parse what comes after --; it appends each argument to the script's command string and runs the result in a shell (sh on Linux and macOS, cmd.exe on Windows, configurable with script-shell). Two consequences:
- The arguments go to the end of the command. A script like
tsc && node dist/index.jspasses them tonode dist/index.js, not totsc. Split it into two scripts if each part needs its own flags. - Quoting starts in your own shell: an argument with spaces must be quoted in the terminal, as in
-t "login page". npm then escapes each argument for the script shell, so it arrives as one argument.
Pitfalls
- Missing script. A typo in the script name fails with Missing script before any argument is passed.
- Flags that npm knows. Without
--, a flag that is also an npm option (--silent,--workspace,--global) changes npm's own behaviour instead of reaching the script. - npx is different.
npx tool --flagpasses flags to the tool directly; the separator is only needed fornpm run,npm test,npm startandnpm exec.
FAQ
Do I need -- with npm test and npm start?
Yes. npm test and npm start are shortcuts for npm run test and npm run start, so flags meant for the script still go after --, as in npm test -- --watch.
Why does npm warn Unknown cli config?
You passed a flag such as --port before --, so npm read it as one of its own settings and did not recognise it. Move the flag after --; npm says this form will stop working in the next major version.
How do I pass arguments to a script in one workspace?
Put the workspace option before the separator: npm run build -w packages/web -- --watch. Everything after -- goes to the script in each selected workspace.