Sending command line arguments to npm script

The scripts portion of my package.json currently looks like this:

"scripts": {
    "start": "node ./script.js server"
}

...which means I can run npm start to start the server. So far so good.

However, I would like to be able to run something like npm start 8080 and have the argument(s) passed to script.js (e.g. npm start 8080 => node ./script.js server 8080). Is this possible?

Edit 2014.10.30: It's possible to pass args to npm run as of npm 2.0.0

The syntax is as follows:

npm run [command] [-- <args>]

Note the necessary --. So if you have in package.json

"scripts": {
    "grunt": "grunt"
}

Then the equivalent of

grunt task:target

run via npm would be

npm run grunt -- task:target


Edit 2013.10.03: It's not currently possible directly. But there's a related GitHub issue opened on npm to implement the behavior you're asking for. Seems the consensus is to have this implemented, but it depends on another issue being solved before.


Original answer: As a some kind of workaround (though not very handy), you can do as follows:

Say your package name from package.json is myPackage and you have also

"scripts": {
    "start": "node ./script.js server"
}

Then add in package.json:

"config": {
    "myPort": "8080"
}

And in your script.js:

// defaulting to 8080 in case if script invoked not via "npm run-script" but directly
var port = process.env.npm_package_config_myPort || 8080

That way, by default npm start will use 8080. You can however configure it (the value will be stored by npm in its internal storage):

npm config set myPackage:myPort 9090

Then, when invoking npm start, 9090 will be used (the default from package.json gets overridden).

You asked to be able to run something like npm start 8080. This is possible without needing to modify script.js or configuration files as follows.

For example, in your "scripts" JSON value, include--

"start": "node ./script.js server $PORT"

And then from the command-line:

$ PORT=8080 npm start

I have confirmed that this works using bash and npm 1.4.23. Note that this work-around does not require GitHub npm issue #3494 to be resolved.

You could also do that:

In package.json:

"scripts": {
    "cool": "./cool.js"
}

In cool.js:

 console.log({ myVar: process.env.npm_config_myVar });

In CLI:

npm --myVar=something run-script cool

Should output:

{ myVar: 'something' }

From what I see, people use package.json scripts when they would like to run script in simpler way. For example, to use nodemon that installed in local node_modules, we can't call nodemon directly from the cli, but we can call it by using ./node_modules/nodemon/nodemon.js. So, to simplify this long typing, we can put this...


    ...

    scripts: {
      'start': 'nodemon app.js'
    }

    ...

... then call npm start to use 'nodemon' which has app.js as the first argument.

What I'm trying to say, if you just want to start your server with the node command, I don't think you need to use scripts. Typing npm start or node app.js has the same effort.

But if you do want to use nodemon, and want to pass a dynamic argument, don't use script either. Try to use symlink instead.

For example using migration with sequelize. I create a symlink...

ln -s node_modules/sequelize/bin/sequelize sequelize

... And I can pass any arguement when I call it ...

./sequlize -h /* show help */

./sequelize -m /* upgrade migration */

./sequelize -m -u /* downgrade migration */

etc...

At this point, using symlink is the best way I could figure out, but I don't really think it's the best practice.

I also hope for your opinion to my answer.

Minimist to the rescue in only 2 lines (hurray)!

var argv = require('minimist')(process.argv.slice(2));
var port = argv.port || process.env.PORT || 8080;

// package.json snippet
"scripts": {
   "start": "node ./javascript/server.js --port 8081"
}

Check their homepage or npm page.

Minimist is the package in which Optimist was built on, and according to Optimist page, Optimist is being discontinued in favour of Minimist instead.

You could put it as a bin:

"bin": {
    "start": "./script.js server"
}

Then you would run start 8080 to start your server. Although if you do it this way, you probably should give it a better name than start.

Yes, just use optimist in your app.js (or server.js file). Like:

var argv = require('optimist')
          .describe('port')
          .alias('p', 'port')
          .default(8080)
          .argv;

Or require that a port be passed in:

var argv = require('optimist')
          .describe('port')
          .alias('p', 'port')
          .demand('p')
          .argv;

And then access it with,

var port = argv.port;

Then make sure you include either -p or --port in your start script (if you include demand - if you set default you can leave it off):

"scripts": {
    "start": "node ./script.js server -p 8080"
}

You might have to adjust the "server" bit here. Not sure what you're doing with that. I always run node app or node server but this looks like you're calling node script with a server parameter. If that's what's happening, you can add that to argv as well.

var argv = require('optimist')
          .describe('port')
          .alias('p', 'port')
          .default(8080)
          .describe('command')
          .alias('c', 'command')
          .default('server')
          .argv;

This doesn't really answer your question but you could always use environment variables instead:

"scripts": {
    "start": "PORT=3000 node server.js"
}

Then in your server.js file:

var port = process.env.PORT || 3000;

npm 2.x support cli args

Command

npm run-script start -- --foo=3

Package.json

"start": "node ./index.js"

Index.js

console.log('process.argv', process.argv);

If you want to pass arguments to the middle of an npm script, as opposed to just having them appended to the end, then inline environment variables seem to work nicely:

"scripts": {
  "dev": "BABEL_ARGS=-w npm run build && cd lib/server && nodemon index.js",
  "start": "npm run build && node lib/server/index.js"
  "build": "mkdir -p lib && babel $BABEL_ARGS -s inline --stage 0 src -d lib",
},

Here, npm run dev passes the -w watch flag to babel, but npm run start just runs a regular build once.