Skip to content
This repository was archived by the owner on Aug 11, 2022. It is now read-only.

Passing args into run-scripts #5518

Merged
merged 1 commit into from
Jul 23, 2014
Merged

Passing args into run-scripts #5518

merged 1 commit into from
Jul 23, 2014

Conversation

bcoe
Copy link
Contributor

@bcoe bcoe commented Jun 20, 2014

this is the start of an approach for passing arguments into run-scripts that @isaacs and I have been talking about. It allows you to run:

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

The problem is that it breaks the ability to run a script in the context of a package, e.g., npm start <package> <package> <package>, npm run-script start <package>.

We will still have the ability to run commands in the context of another package, with with the syntax:

npm explore <pkg> -- run test, which seems like a reasonable compromise

thoughts

See #3494

@piranna
Copy link

piranna commented Jun 21, 2014

Why the "--"? Args should defined directly, specially since for default scripts they don't accept parameters:

npm test --foo=bar

@sudodoki
Copy link
Contributor

Why the "--"?

I assume to keep compatibility with current syntax, which is npm run [<pkg>] [command], plus this should solve the problem of passing an arbitrary vector of arguments to the script you're running (superset of the example you had). Say, for example, name of the build task followed by optional arguments.

@piranna
Copy link

piranna commented Jun 21, 2014

But for the cases where by default there are no arguments like npm test, it is really ugly and probably leads to error by newbies that would that's an error in the documentation...

npm test -- --foo=bar

@bcoe
Copy link
Contributor Author

bcoe commented Jun 22, 2014

since the npm test, or npm run-script goes through nopt without the --, you get behavior like this:

Benjamins-MacBook-Air:api benjamincoe$ npm test --help
Top hits for "test"
————————————————————————————————————————————————————————————————————————————————
npm help scripts                                                          test:8

Where flags such as --help, or --verbose are parsed by npm. This refactor which was proposed by @isaacs helps solve this issues. We lose:

npm run-script <script> <pkg> in favor of:

npm explore <pkg> -- npm run-script <script>.

But, we gain the ability to pass args to a script with:

npm run-script <script> [-- --help].

@rlidwka
Copy link
Contributor

rlidwka commented Jun 22, 2014

I smell issues with shell escaping; and what happens if you need to insert arguments in the middle of the shell command?

Instead of doing it, I suggest another approach. Bash can substitute $* character with commandline, so this would work:

scripts: {
    test: "mocha $* test/*.js"
}
$ npm test -- --grep foo
spawn("sh",
    ["-c", "mocha $* test/*.js", "unused arg for $0", "--grep", "foo"],
    conf)

// shell calls "mocha --grep foo test/*.js"

This would also allow parametrized scripts like:

scripts: {
    link: "/bin/ln -s $2 $1"
}

// usage: "npm run link <source> <destination>"

And it's much more generic.

@piranna
Copy link

piranna commented Jun 23, 2014

@bcoe that's because nopt allow to set the flags in any position, that leads to this kind of problems and inelegant solutions.
@rlidwka really like your solution :-)

@isaacs
Copy link
Contributor

isaacs commented Jun 23, 2014

We are not going to support passing args into the middle of the script, sorry. If you really need this, write your test command using literally any of the command line parsers that anyone uses. (Minimist, dashdash, nopt, and commander all support this just fine.)

As a matter of fact, this works fine with mocha, since mocha uses commander:

$ cat pizza.js
#!/usr/bin/env node

/**
 * Module dependencies.
 */

var program = require('commander');

program
  .version('0.0.1')
  .option('-p, --peppers', 'Add peppers')
  .option('-P, --pineapple', 'Add pineapple')
  .option('-b, --bbq', 'Add bbq sauce')
  .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
  .parse(process.argv);

console.log('you ordered a pizza with:');
if (program.peppers) console.log('  - peppers');
if (program.pineapple) console.log('  - pineapple');
if (program.bbq) console.log('  - bbq');
console.log('  - %s cheese', program.cheese);

console.log(program.args)

$ node pizza.js -p fo bar baz -b -c blerg -P
you ordered a pizza with:
  - peppers
  - pineapple
  - bbq
  - blerg cheese
[ 'fo', 'bar', 'baz' ]

@isaacs
Copy link
Contributor

isaacs commented Jun 23, 2014

The reason for the -- is that nopt parses command line flags. npm test is an npm command. As such, npm test --tmp=/path/to/my/tempdir will parse the tmp option from the cli flags, and then set it as npm_config_tmp=/path/to/my/tempdir in the environment. (For further reading, check out man 7 npm-scripts, man npm-config, man 7 npm-config and man 5 npmrc.)

Where this is going to break is in npm test some-module. You'll have to explicitly explore that module, and then run npm test as the command in there, as @bcoe showed above: npm explore foo -- npm test. This is a significant change, and will require a bump to the version number to reflect that.

@piranna
Copy link

piranna commented Jun 24, 2014

@isaacs, I don't like the design how npm manages arguments, but if it's the way to go, I think the -- is the only solution here. Thanks for the explanation.

@ELLIOTTCABLE
Copy link

Just want to throw in my -1 against forcing --.

I definitely understand the restrictions and reasoning; but we're already going to have to flag this as a breaking change. My two-cents: if we're going to break backwards with some of the flag-parsing, let's Do It Right and break backwards entirely, in such a way that the (really common.) npm test and npm run clean type stuff works as expected by a newcomer, instead of having to teach everybody for the rest of eternity that npm specifically requires some strange -- stuff.

So, on that note: let's figure out which breaking changes would need to be made to support npm passing not-understood flags (--flurgleburg, as opposed to oen that npm understands like --verbose) into the various commands in a generic way. (I would posit that it's unnecessary to 'fix' the npm test --verbose case; that's exactly the sort of situation where the -- seperator is commonly understood to be used. It's only requiring it in any situation that seems draconian and unintuitive, not in the obviously-ambiguous ones.)

The most obvious breaking change would be for people relying heavily on the --flurgleburg-to-npm_config_flurgleburg functionality. I'm just going to come right out and say that that seems inherently inferior in almost every way to passing the arguments through wholesale; there are plenty of argument-parsing solutions available in all sorts of scripting languages that could replace the dependance of existing scripts on environment-variables. The environment-variable functionality could further still be supported, under a new, guarded flag (--conf tmp=/path/to/my/tempdir, or perhaps --env for the same?). This is a net benefit in every situation I can fathom; as it gives the scripts (or rather, the scripts' authors) a ton more flexibility in what those flags mean to them, what sort of content they can hold, how booleans work, how order affects the parsing, and the matching-up of all those semantics to any underlying library or pre-existing expectations on the part of the user. ("Hey, let's make sure npm run mocha works as expected for previous users of mocha.")

Should you accept my arguments, I'd suggest a depreciation period, during which the new argument-passing functionality be only available under --, temporarily; with deprecation notices about --flurgleburg and npm_config_flurgleburg going out in the meantime. Authors would be given time to either A) update their documentation or process, if necessary, to use --conf flurgleburg=... instead of --flurgleburg=...; or B), re-write their scripts to use an argument parser of their own, instead of reading environment-variables.

(P.S. Obviously, this is controversial. Consider me to be playing 'advocatus diaboli' here.)

@piranna
Copy link

piranna commented Jun 24, 2014

+1 to @ELLIOTTCABLE comment, the ambiguity of --help and verbose on npm test is despreciable (or the developer is irresponsable by adding this flags to their tests knowing they will fail...), and only in such ambiguous cases, I find -- a good theat.

@domenic
Copy link
Contributor

domenic commented Jun 24, 2014

@isaacs: will we have to include -- for npm exec also? That kind of defeats the purpose of npm exec, but it seems like the same argument applies (e.g., setting the test directory with npm exec --tmp=/foo/bar ...).

@mgol
Copy link

mgol commented Jul 9, 2014

I like the approach. As far as I understand correctly for projects using Grunt all that'd be needed would be to specify:

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

and then run grunt commands via:

npm run grunt -- task:target

Since it's more ugly than simply:

grunt task:target

one can define an alias:

alias gr='npm run grunt --'

and write:

gr tast:target

Then, grunt-cli & friends can go away!

@piranna
Copy link

piranna commented Jul 9, 2014

npm run grunt -- task:target

It was not talked about to remove the "run" word and allow to use just npm grunt instead, like npm test?

@mgol
Copy link

mgol commented Jul 9, 2014

@piranna

It was not talked about to remove the "run" word and allow to use just npm grunt instead, like npm test?

This will not work in general since npm has its own commands and they'd collide. run is required for most commands with a couple of exceptions like test, start etc.

@piranna
Copy link

piranna commented Jul 9, 2014

I know they would collide, but the idea was to make them first class commands, like git-<whatever> ones that can all of them being called with git <whatever>.

@mgol
Copy link

mgol commented Jul 9, 2014

@piranna What exactly do you propose? To special-case grunt in npm because it's popular? This can quickly become a mess.

EDIT: besides, you'd still need to type -- to provide parameters with the current approach so it doesn't buy much.

@piranna
Copy link

piranna commented Jul 9, 2014

What I propose (and I believe was in discussion) is to allow all the entries on the scripts fields to be first class, not only test or pre-publish but also user custom ones: npm grunt, npm bower, npm upload, npm makeMeSandwich... Some of them would override npm ones, but would be on desire, and will make easier to use custom ones.

I know this is not related with the -- thing, but will not make things worse...

@mgol
Copy link

mgol commented Jul 9, 2014

@piranna

What I propose (and I believe was in discussion) is to allow all the entries on the scripts fields to be first class, not only test or pre-publish but also user custom ones: npm grunt, npm bower, npm upload, npm makeMeSandwich...

I'm afraid overriding npm install could have bad side effects. But maybe most commands don't.

After reading the rationale, I don't get the need for --. The only justification provided was that nopt parses arguments this way but why do we need to use nopt for custom commands? It seems as implementor convenience is trumping the usefulness here.

If we'd able to write npm grunt task:target, then even the alias wouldn't be so badly needed, that's a given.

@piranna
Copy link

piranna commented Jul 9, 2014

If we'd able to write npm grunt task:target, then even the alias wouldn't be so badly needed, that's a given.

+1, I believe this is the best interface.

"Si quieres viajar alrededor del mundo y ser invitado a hablar en un
monton de sitios diferentes, simplemente escribe un sistema operativo
Unix."
– Linus Tordvals, creador del sistema operativo Linux

@@ -69,13 +69,13 @@ runScript.completion = function (opts, cb) {

function runScript (args, cb) {
if (!args.length) return list(cb)
var pkgdir = args.length === 1 ? process.cwd()
: path.resolve(npm.dir, args[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is us removing the npm test foobar feature where it runs the tests for node_modules/foobar instead of top level.

This is a reasonably feature to remove and break back compat on.

@Raynos
Copy link
Contributor

Raynos commented Jul 21, 2014

Just reviewed this, this looks fine.

@othiym23
Copy link
Contributor

@bcoe What needs to happen to get this landed on the 1.5 / 2.0 branch? Anything I can do to help?

@Raynos
Copy link
Contributor

Raynos commented Jul 21, 2014

I rebased this on https://github.com/Raynos/npm/tree/run-args tests pass locally ( except that integration test that needs a local couch).

Would love to get this in & shipped for 1.5.0.

If you need more tests or documentation let me know and I'll jump in and help with that.

@@ -124,6 +124,11 @@ function run (pkg, wd, cmd, cb) {
}
log.verbose("run-script", cmds)
chain(cmds.map(function (c) {
// pass cli arguments afer -- to script.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: "after"

@domenic
Copy link
Contributor

domenic commented Jul 22, 2014

I still think requiring the -- is a mistake but something is definitely better than nothing, and we can ship npmexec to make it less awkward

@mgol
Copy link

mgol commented Jul 22, 2014

@domenic

I still think requiring the -- is a mistake but something is definitely better than nothing, and we can ship npmexec to make it less awkward

👍. The fact that a tool used requires a specific way of passing arguments is not a proper argument for applying this solution, a tool can be changed etc. Arguments should be about usage scenarios.

But if the decision to require -- is final then it's still way better than nothing.

@machineghost
Copy link

I just wanted to +1 (:+1: ) the evils of the extra --. I lost about half an hour trying the natural/logical syntax (ie. npm test --foo), before finally giving up and Googling my way to this thread.

In general well-designed software strives to make its users refer to the manual as infrequently as possible. But with the current extra -- syntax a user has to either:

  • stumble on to this thread, or
  • npm help package.json, then
    • npm help scripts, then
    • npm help run-scripts
    • (wasting the time to read through the non-helpful parts of those first two before you can even get to the third)

Removing the -- requirement will make the tool much more friendly to non-Node-experts, by making npm test --foo "just work." Kudos to anyone who can find the time to implement it.

@stefanmaric
Copy link

stefanmaric commented Jul 1, 2017

Maybe I'm a bit late.

We are not going to support passing args into the middle of the script, sorry.

I was looking to create parameterized npm scripts that interpolate args in arbitrary places, not only at the end. I came to a quick&dirty solution that I haven't seen anywhere else so I'm posting it here for future readers.

You just need to wrap your script in an immediately invoked function:

{
  "print": "f(){ echo Hello $1! && echo Bye $2! ;};f",
  "test": "npm run print -- world stranger"
}

You get:

$ npm test -s # -s is to suppress npm messages for this example
Hello world!
Bye stranger!

You can also use $* which refers to all arguments.

This of course won't work on Windows unless you're using the Windows Subsystem for Linux (WSL, aka Bash for Windows), but since it is portable sh it should work everywhere else.

I would use this for dead-simple scripts only. Otherwise, you're better set with a separated node or sh script file as @isaacs said.

@kolya-ay
Copy link

kolya-ay commented Jul 6, 2017

@stefanmaric, great hack! Exactly what I looked for, thank you

tombye added a commit to tombye/govuk_prototype_kit that referenced this pull request Mar 12, 2018
Since v2, NPM scripts allow you to pass arguments
through to the command run when you run it:

npm/npm#5518
tombye added a commit to tombye/govuk_prototype_kit that referenced this pull request Jun 26, 2018
Since v2, NPM scripts allow you to pass arguments
through to the command run when you run it:

npm/npm#5518
@KeeyanGhoreshi KeeyanGhoreshi mentioned this pull request Mar 26, 2019
16 tasks
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.