Passing multiple named arguments to package.json script - npm

I have a webpack config with currently three parameters, which works when I invoke it in the following way:
webpack --env.p1 = "p1_value" --env.p2 = "p2_value" --env.p3 = "p3_value"
Now I want to encapsulate that webpack command in the following package.json script:
"scripts": {
"prod": "webpack --env.p1 --env.p2 --env.p3"
}
How do I have to change that script such that I can invoke it from the CLI in the following way
npm run prod p1="p1_value" p2="p2_value" p3="p3_value"
(where the named parameters are indispensible, because I need to be able to work with default values inside the webpack config?)

You can pass any arguments provided to the npm command through to webpack using the placeholder ${#:1}.
package.json
"scripts": {
"prod": "webpack ${#:1}"
}
From the command line, add parameters to be passed through using -- as separator, like so:
npm run prod -- --env.p1="p1_value" --env.p2="p2_value" --env.p3="p3_value"

Related

package.json - how to set default value of variable in windows

In Linux you can use the following syntax to set the default value of a variable in the package.json file: try=${myEnvVariable:"defaultValue"}
How do you do this using the syntax for window %varaibelName% ? This does not work:
try=%myEnvVariable:"defaultValue"%
You can define a script in the scripts section of the package.json like this
"scripts": {
"set-myenv": "npm config set myEnvVariable <yourDefuaultValue>"
}
To use this in another script, you can chain scripts with &&
"scripts": {
"set-myenv-and-execute": "npm config set myEnvVariable <yourDefuaultValue> && another command --some-option %npm_config_myEnvVariable%"
}
Here, the second script (after &&) has the --some-option option set with the default value of myEnvVariable through %npm_config_myEnvVariable%
Then, you can execute this script in the command line either by providing a value to myEnvVariable
> npm run set-myenv-and-execute --myEnvVariable=someValue
or without providing a value
> npm run set-myenv-and-execute
Here it uses the default value.

Pass git commit message to npm script and append to predefined string

In an NPM project, I'd like to have a commit for each build version. This will allow me to go back to the current build version, fix a bug, without having to go through all the QA of a new version.
We can commit using npm scripts like this (see this answer):
package.json
"scripts": {
"git": "git add . && git commit -m",
}
Then invoke the script by running:
npm run git -- "Message of the commit"
I'd like to automate it to run after npm run build. For this purpose we can create a new command.
package.json
"scripts": {
"buildAndCommit": "npm run build && git add . && git commit -m",
}
This could be run using npm run buildAndCommit -- "commit for a new build"
The only thing left is that I'd like to identify this commit as one that could be linked to a commit. Is it possible to start the message automatically with "BUILD -" and to add to that the unique message which is passed in the command line? Something like:
package.json
"scripts": {
"buildAndCommit": "npm run build && git add . && git commit -'Build' + $uniqueMessageFromTheCommandLine`",
}
If it is not possible to template the string in package.json, how could I achieve it using a command line script? (Powershell is my command line tool).
Running on *nix platforms
On a *nix platform npm utilizes sh by default to execute the npm script(s). In this scenario you can simply use a shell function and reference the git message argument passed via the CLI using the $1 positional parameter.
Your npm script would be redefined like this:
"scripts": {
"build": "...",
"buildAndCommit": "func() { npm run build && git add . && git commit -m \"BUILD - $1\"; }; func"
}
Cross platform
Unfortunately, via Windows Powershell the solution is not quite as simple and terse.
When using Powershell npm utilizes cmd by default to execute npm script(s). Likewise npm utilizes cmd by default via other Windows consoles too, such as Command Prompt.
One way to achieve your requirement is to invoke a node.js via your npm script. The following provides two different methods that are essentially the same. Either will run successfully cross-platform (in your case via Powershell).
Method A - Using a separate node.js script
Create the following node.js script. Let's name the file script.js and save it in the root of the project directory, i.e. in the same directory where package.json resides.
script.js
const execSync = require('child_process').execSync;
const mssg = 'BUILD - ' + process.argv[2];
execSync('npm run build && git add . && git commit -m \"' + mssg + '\"', { stdio:[0, 1, 2] });
Explanation
The node.js builtin process.argv captures the argument at index two, i.e. the git commit message, that was provided via the CLI. The git commit message is concatenated with with the substring BUILD - to form the desired commit message. The resultant string is assigned to the variable mssg.
We then utilize the node.js builtin execSync() to execute your given npm script. As you can see, the value of the mssg variable is used as the git commit message.
The stdio option is utilized to ensure the correct configuration of the pipes, i.e. stdin, stdout, 'stderr', are established between the parent and child process.
Define your npm script named buildAndCommit as follows:
package.json
"scripts": {
"build": "...",
"buildAndCommit": "node script"
}
Above node invokes script.js.
Method B - Inline the node.js script in npm script
Alternatively, the aforementioned node.js script (i.e. script.js) can be provided inline in your npm script instead - therefore negating the use of a separate .js file.
package.json
"scripts": {
"build": "...",
"buildAndCommit": "node -e \"const mssg = 'BUILD - ' + process.argv[1]; require('child_process').execSync('npm run build && git add . && git commit -m \\\"' + mssg + '\\\"', { stdio:[0, 1, 2] })\""
}
This utilizes the same code from Method A albeit it slightly refactored. The notable differences are:
The nodejs command line option -e is utilized to evaluate the inline JavaScript.
process.argv this time will capture the argument, i.e. the git commit message, at index one in the array of arguments.
Additional escaping of the double quotes is necessary, i.e. \\\"
Running the npm script
Using either Method A or Method B run the command via your CLI as desired: For instance:
$ npm run buildAndCommit -- "commit for a new build"
This will produce the following git commit message:
BUILD - commit for a new build

Assign a random number (UUID) value to a environment variable in npm script

I'd like to add a UUID argument when calling my npm script. Each time the script is called, I'd like to generate a new number. It should look like:
"build": "cross-env UUID=unique_number ng build"
The only thing I need is generating the unique_number here. I tried to use the uuid package but I don't know how to fire it in the script and pass the number as the argument.
tl;dr As you're question shows the use of cross-var I've assumed a cross-platform solution is required. In which case refer to the Solution A. However refer to either Solution B or C if my assumption is incorrect.
Solution A: Cross Platform (Windows/Linux/macOS...)
Fo a cross platform solution, (i.e. one that runs successfully on Windows, Linux, and macOS...), you'll need to utilize nodejs to achieve your requirement. There are a couple of different ways to approach this as described in the following two sub-sections titled:
Using an external nodejs (.js) file
Inlining your JavaScript in package.json.
Note both approaches are effectively the same
Using an external nodejs (.js) file
Create a nodejs utility script. Let's name the file run-ng-build.js and save it in the root of your project directory, i.e. in the same directory where package.json currently resides:
run-ng-build.js
const uuid = require('uuid/v1');
const execSync = require('child_process').execSync;
process.env.UUID = uuid();
execSync('ng build', { stdio: [0, 1, 2]} );
In the scripts section of your package.json replace your current build script with the following:
package.json
"scripts": {
"build": "node run-ng-build"
}
Explanation:
In run-ng-build.js we require the uuid package and the nodejs built-in execSync().
To create the environment variable named UUID we utilize the nodejs builtin process.env, and assign a uuid value to it by invoking uuid().
We then invoke the ng build command using execSync.
The options.stdio option configures the pipes between the parent and child process - [0, 1, 2] effectively inherit's stdin, stdout, and stderr.
Inlining your JavaScript in package.json.
Alternatively, you can inline your nodejs/JavaScript code in the scripts section of your package.json.
In the scripts section of your package.json replace your current build script with the following instead:
package.json
"scripts": {
"build": "node -e \"process.env.UUID = require('uuid/v1')(); require('child_process').execSync('ng build', { stdio: [0, 1, 2]} );\""
}
Explanation:
This is effectively the same as the aforementioned solution that utilized a separate .js file, however the use of a separate nodejs script/file is now redundant.
The nodejs command line option -e is utilized to evaluate the inline JavaScript.
Important The cross-env package is redundant utilizing either of the two aforementioned solutions. To uninstall it run: npm un -D cross-env via your CLI.
Solution B: *Nix platforms only (Linux/MacOS...)
For *nix platforms only it becomes very terse, you can just define your build script in package.json as follows:
package.json
"scripts": {
"build": "cross-env UUID=$(uuid) ng build"
}
This utilizes a Bash feature known as command substitution, i.e. $(uuid). However, if *nix is the only platform you need to support, then cross-env is really not necessary. Use the built-in export feature instead. For instance:
package.json
"scripts": {
"build": "export UUID=$(uuid) && ng build"
}
Solution C: Windows only (cmd.exe)
On Windows (only) running via Command Prompt or PowerShell you can do the following:
package.json
"scripts": {
"build": "FOR /F %U IN ('uuid') DO cross-env UUID=%~U node -e \"process.env.UUID = require('uuid/v1')(); require('child_process').execSync('ng buuld', { stdio: [0, 1, 2] });\""
}
This is similar to the first example shown in Solution B however command substitution is achieved (very) differently in cmd.exe. See this answer for further explanation.

Pass parameter to npm script and then gulp-task [duplicate]

I have package.json scripts with the following structure:
"scripts": {
"watch:build": "tsc --watch",
"watch:server": "nodemon ./src/app.js --watch 'app'",
"build": "tsc && gulp do_something",
"start": "npm-run-all clean build --parallel watch:build",
"watch:server --print-label"
}
I would like to start the application as npm run start with_argument
and pass it to build script, to perform actions in the gulp task based on that argument.
I read a lot of tutorial and how to articles, but without result. It is possible somehow to pass argument from one script to another(which starts a gulp task).
Thanks in advance!
npm-run-all provides a its own custom mechanism for handling arguments by utilizing placeholders in npm-scripts, as stated in the Argument Placeholders section of its documentation found here.
npm-script:
Given your current npm-script named start you'll need to redefine it as follows:
"scripts": {
...
"start": "npm-run-all clean \"build -- {#}\" --parallel watch:build --"
...
}
Notes:
-- {#} must be added after build.1
build -- {#} must be wrapped in escaped double quotes \"...\"
-- must also be added after last script invocation, namely: watch:build
gulpfile.js
To obtain the arguments passed via the CLI inside your gulpfile.js you''ll need to utilize nodes process.argv
For the purpose of demonstration lets say our gulpfile.js is as follows:
var gulp = require('gulp');
var args = process.argv.splice(3, process.argv.length - 3);
gulp.task('doSomething', function() {
// For testing purposes...
if (args.indexOf('--foo') > -1) {
console.log('--foo was passed via the CLI.')
}
if (args.indexOf('--quux') > -1) {
console.log('--quux was passed via the CLI.')
}
});
Notes:
The first three items in nodes process.argv are:
The path to the executable running the JavaScript file.
The path of the JavaScript file being executed.
The name of the gulp task, i.e. doSomething
However, we're only interested in the elements from the fourth item in the array onwards - as these will be the arguments passed via the CLI. The line which reads:
var args = process.argv.splice(3, process.argv.length - 3);
creates an args variable and assigns an Array containing each argument passed via the CLI, i.e. we omit the first three aforementioned items in point 1 above using the Arrays splice() method.
Running your start script:
You invoke your start script via your CLI as follows:
$ npm start -- --foo --quux
Note You must provide the -- which precedes npm start before providing your own arguments.
Output:
Using the contrived gulpfile.js above, in combination with your current scripts defined in your package.json, and of course the necessary changes made to your start script. When you run:
$ npm start -- --foo --quux
you will see the following printed to the console:
--foo was passed via the CLI.
--quux was passed via the CLI.
Running:
$ npm start -- --quux
you will see just the following printed to the console:
--quux was passed via the CLI.
And of course, running:
$ npm start
Does not print either of the messages defined in gulpfile.js.
Footnotes:
1 -- {#} can be replaced with -- {1} if you only intend to pass one argument. However, -- {#} handles multiple arguments, so it's fine to use it for one argument too.

npm run: how to pass parameter and replace placeholder by param in script call

I want to to define a scripts entry in my package.json to simplify building for several environments.
In the script execution I need to replace $1 (or whatever syntax I need for a placeholder) by a parameter I would pass to npm run build-script like for example --env=prod or even simpler, --prod. How can I do that? Other questions and answers I found here didn't help me solve the problem.
"scripts": {
"build-for": "ng build --output-path=../../web/admin-v2 --env=$1 --base-href=\"/admin-v2/\""
}
I often resort to creating a utility node script for this kind of scenario and invoke it via the scripts section of package.json.
build-for.js
var nodeCLI = require('shelljs-nodecli');
var env = '--env=foo'; // <-- Default env flag/value when no arg provided.
if (process.argv.indexOf('--prod') !== -1) {
env = '--env=prod';
}
// Check for other possible env values
if (process.argv.indexOf('--quux') !== -1) {
env = '--env=quux';
}
// Run the ng build command
nodeCLI.exec('ng', 'build', '--output-path=../../web/admin-v2', env, '--base-href=\"/admin-v2/\"');
build-for.js utilizes nodes process.argv to ascertain the argument/flag passed via the CLI and then invokes the ng command (the one currently defined in your package.json) using shelljs-nodecli.
npm i -D shelljs-nodecli
Lets assume that build-for.js is saved to a hidden folder named .scripts in your projects root directory; then your scripts section of package.json will be defined as follows:
package.json
{
...
"scripts": {
"build-for": "node ./.scripts/build-for"
},
...
}
Running the script
Invoke the npm script by running:
npm run build-for -- --prod
Note the special -- before the argument --prod must be included as explained here
As of npm#2.0.0, you can use custom arguments when executing scripts. The special option -- is used by getopt to delimit the end of the options. npm will pass all the arguments after the -- directly to your script:
Given the logic curently in the build-for.js - when no arguments are passed, for example:
npm run build-for
...the env flag will be set to --env=foo
Running the following:
npm run build-for -- --quux
...will result in the env flag will be set to --env=quux
Caveat
I haven't fully tested build-for.js, so you may find that you don't need to escape the double-quotes in this part '--base-href=\"/admin-v2/\"' of the following command (nodeCLI may handle that for you.):
// Run the ng build command
nodeCLI.exec('ng', 'build', '--output-path=../../web/admin-v2', env, '--base-href=\"/admin-v2/\"');
Not exactly what you're looking for but you can use environment variables & provide them inline:
package.json script:
"<script>": "echo ${ENV1} ${ENV2}"
run like so:
ENV1=a ENV2=b npm run <script>
$ npm run <script>
a b