if-else on arguments in npm run script
Asked Answered
G

1

6

I would like to call different other scripts, depending on whether a paramter is given or not:

"paramtest": "if [ -z $1 ]; then echo Foo $1; else echo Bar; fi",

npm run paramtest

should give "Bar".

npm run paramtest -- whatever

should give "Foo whatever".

However in practice I only get: (The parameter is added to the whole line, not 'passed in')

> if [ -z $1 ]; then echo Foo; else echo Bar; fi "whatever
  sh: 1: Syntax error: word unexpected

What can I do better?

Essentially I am after running full test suite / only individual test with the same command...

"test" : "if [ -z $1 ]; then mocha ./test/**/*.test.js; else mocha $1
Griseous answered 18/7, 2018 at 11:52 Comment(3)
any luck with this?Keegan
Not so far.....Griseous
scripty is a handy module for moving npm scripts into dedicated files; I find it easier then cramming bash into JSON strings. Bonus: using the shebang will let you write your npm scripts in Node.Compilation
W
4

Wrapping it in a shell function should do the trick:

"test": "f() { if [ $# -eq 0 ]; then mocha './test/**/*.test.js'; else mocha -- \"$@\"; fi; }; f"

Note that I changed the if condition and the else branch slightly so you can specify multiple file arguments if necessary.

A more succinct method:

"test": "f() { mocha -- \"${@:-./test/**/*.test.js}\"; }; f"

Using a shell function this way might look familiar, as the same technique is often used for git aliases.


Detailed Explanation

Let's use this script for demonstration:

"scripts": {
    "myscript": "if [ \"$1\" = one ]; then printf %s\\\\n \"$@\"; else echo false; fi"
}

Here if the first argument is "one", we print all the arguments, and otherwise we print "false". We are of course assuming that npm run-script is using an sh-like shell, and not, e.g., Windows' cmd.exe.

I can't see anything in the npm documentation specifically detailing how arguments are passed to the script, so let's take a look at the source code (npm v6.14.7 at the time of writing). It seems that the script is joined with its arguments here and is then executed here. Essentially, npm run myscript -- one two three becomes

sh -c 'if [ "$1" = one ]; then printf %s\\n "$@"; else echo false; fi "one" "two" "three"'

Our arguments one two three are simply quote-escaped and concatenated to the script command. In terms of the shell grammar, this means that they are ending up as arguments to fi. sh of course rejects this because fi is just a builtin to end if and takes no arguments.

Our goal is something more like

sh -c 'if [ "$1" = one ]; then printf %s\\n "$@"; else echo false; fi' sh "one" "two" "three"

Here one, two, and three are arguments to sh itself and thus become the argument variables $1, $2, and $3 in the given script. npm doesn't let us do this directly, but we can accomplish the same thing by wrapping our script in a shell function:

"scripts": {
    "myscript": "f() { if [ \"$1\" = one ]; then printf %s\\\\n \"$@\"; else echo false; fi; }; f"
}

The script here ends with an invocation of the function, so npm will end up concatenating the arguments to this invocation, ultimately calling the function as f "one" "two" "three":

sh -c 'f() { if [ "$1" = one ]; then printf %s\\n "$@"; else echo false; fi; }; f "one" "two" "three"'
Windsail answered 25/7, 2020 at 22:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.