Skip to content

/flags/npm/npm-run-double-dash-arguments

npm run script -- --flag: pass arguments to an npm script

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · 11.19.0updated
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 --

bash
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 extra

Output

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

bash
npm init -y >/dev/null
npm pkg set scripts.env='node -e "console.log(process.env.npm_config_port)"'
npm run -s env --port=4000

Output

4000

Relying on that is fragile; pass arguments after -- instead. Recent npm versions also warn about it:

bash
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

CommandWhat the script receives
npm run build -- --watchbuild command + --watch
npm test -- -t "login"test command + -t login (npm test is npm run test)
npm start -- --port 8080start command + --port 8080
npm run build --watchnothing 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.js passes them to node dist/index.js, not to tsc. 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 --flag passes flags to the tool directly; the separator is only needed for npm run, npm test, npm start and npm 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.

Sources