Pass shell environment variable as argument in npm script - node.js

I'm trying to pass environment variables to npm scripts as arguments
with no success.
export ENVIRONMENT=test.proxy.json
npm run test
I'm trying to do something like this in package.json
npm run test --proxy-config-file $ENVIRONMENT

When you do this:
export ENVIRONMENT=test.proxy.json
npm run test
or this:
ENVIRONMENT=test.proxy.json npm run test
then you will pass the "test.proxy.json" string as a value of the environment variable named ENVIRONMENT.
If you want to pass arguments to npm scripts then you may need to use:
npm run test -- --proxy-config-file $ENVIRONMENT
Keep in mind that if you pass the argument to the npm script, it doesn't necessarily mean that it will be passed to other scripts that this script is executing. With environment variables it's the other way around - by default they should be passed from one script to the other but there is still no guarantee as the caller may decide what environment variables to pass, if any.
But it's hard to tell from your question what is your real problem here - the phrase "with no success" is too general to know what is the problem here.

Related

Scripts arguments starting with -- are removed

In node 14 I had the following script
scripts: {
"test": "node test.js",
}
Then I ran it with the following command npm run test -- --value '10' this gave the output
node test.js "--value" "10"
But when I switch to node 16 every argument that starts with -- gets removed
So whenI run npm run test -- --value '10' this gives the output
node test.js "10"
Where did "--value" go?
I'm not sure 100% about the root cause, but I can now explain the semi-root causes and ways to work around it.
It appears that while on my machine there is only an npm.cmd file in C:\Program Files\nodejs, on yours there is also an npm.ps1 file. It appears this file is created only in some install/upgrade paths, and your node upgrade seems to cause this file to be created.
That means that when you run npm in PowerShell, it is invoking an ExternalScript and not an Application. For this reason, semantics for calling cmdlets apply to the invocation and not semantics for calling native commands/applications.
These semantics include the use of -- to stop parsing further arguments. That means that in this case PowerShell will consume the -- token so it never arrives to npm! And in turn, npm now being called with npm run test --value 10 without -- will swallow --value because it's considered an argument to npm and not to the script you want to invoke.
Quote from the docs:
The end-of-parameters token (--)
The end-of-parameters token (--) indicates that all arguments following it are to be passed in their actual form as though double quotes were placed around them. For example, using -- you can output the string -InputObject without using quotes or having it interpreted as a parameter
It doesn't state here that it applies only to cmdlets and external scripts but my testing showed that that's the case.
There are four possible solutions I can think of:
Create an alias to manually point npm to npm.cmd instead of npm.ps1: Set-Alias -Name npm -Value npm.cmd
Rename/remove npm.ps1 to make sure PowerShell uses npm.cmd instead
Add another -- in front which is consumed by PowerShell to allow the following -- to be consumed by npm: npm run test -- -- --value 10 or npm -- run test -- --value 10
Quote the --: npm run test '--' --value 10

Set variable in npm script to env var from file

I'd like to set a variable FOO (that is not in my .env file) in an npm script command to the value of the environment variable BAR (which is in the .env file).
For example, my .env file:
BAR=1234
And my package.json npm script:
{
"scripts": {
"try": env-cmd FOO=$BAR src/myScript.js
}
}
...where I'd expect FOO to equal 1234. But running npm run try throws the error:
spawn FOO= ENOENT
error Command failed with signal "SIGINT".
Footnote: I'm open to libraries other than env-cmd.
You either need to use dotenv for in your code or use cross-env in the NPM script command.
I doesn't make much sense to set an environment variable from an environment variable in the script command itself as you are assuming it is already set. Running the npm script will not pick up anything from a .env file.

What happens under the hood when you call `npm run` to run a NPM script?

I would like to know more about how NPM scripts work.
For example:
package.json
"scripts": {
"build": "set NODE_ENV=production&& webpack --config webpack.config.js",
}
When I execute npm run build:
What happens? I know a Node process will be initiated somewhere and webpack binary file will be called, right? But what are the steps before that? Does that depend whether I'm on Windows, Linux, PowerShell or Git bash? How does that process relate to the OS and the CMD/CLI that is being used?
The npm source code helpfully has the whole run-script functionality separated into its own module and repository so you can review the source code if the documentation does not answer your questions.
Speaking of your questions:
I know a Node process will be initiated somewhere and webpack binary file will be called, right?
The webpack executable will be run. And since webpack is a Node.js script, it will be run with node.
If, however, your "build" value consisted of shell/CLI commands, those commands would be run. Node.js is not necessarily invoked.
But what are the steps before that?
The "steps before that" include certain lifecycle scripts that might also be defined. In particular, if there is a "prebuild" script, it will run before the "build" script.
Does that depend whether I'm on Windows, Linux, PowerShell or Git bash? How does that process relate to the OS and the CMD/CLI that is being used?
npm (and node) make efforts to make Windows and Linux experiences comparable. Differences occur for sure, but without more details, I'm not sure I'd want to speculate about what specifics might be of interest to you beyond that.
Powershell and GitBash: Again, npm will make efforts to smooth out differences, but I'm sure they come up. One thing to be aware of is that your PATH (and other environment variables) might be set differently and that may affect behavior. (It may especially impact which version of node gets executed, if you have more than one version installed.)
I am not a Windows expert, but I have seen a lot of npm scripts that assume a UNIX-like environment. So, if given the choice and all else being equal (which it never is), a bash-like environment is probably going to be a bit smoother.
The answer I was looking for was something like this:
When you call npm run, npm will initiate a shell to run those instructions.
And which shell npm will use is defined in your npm settings.
You can see your npm config by running:
>>> npm config ls
In the example above, npm will run the script on git-bash.
You can change the npm shell by calling:
npm config set script-shell "C:\\Program Files\\git\\bin\\bash.exe"

I need to add dependency in my package.json and load module , after taking user input while running npm install

I need to add a dependency in my package.json and load module, by taking user input in command prompt while running npm install.
Is it possible to do so.
As you haven't provided much information about what exactly you try to accomplish, it's difficult to advise you or give a concrete example. Anyhow, in package.json, you can override the default behavior of npm scripts with the scripts field:
"scripts": {
"install": "./scripts/install.sh",
}
As someone else has noted before, you cannot call npm install in your custom script, though, as this would lead to infinite recursion. Thus, rather provide a preinstall script, which npm runs before install.
For use cases other than adding dependencies based on user input, I recommend to check how to set the environment variables and configurations, which the user then could override with npm config set project-name:config-name config-value.

Timing/performance metrics of npm scripts

Is there a way to measure the performance of an npm scripts similar to the way time-grunt works?
I am moving some of my critical build tasks to use npm instead of Grunt as writing my own build script is more flexible than using some of the Grunt plugins like grunt-browserify for example.
I have tried using console.time() but it finishes before the script is done, I assume because the methods are asynchronous. I also tried running the npm script as a Grunt task like this:
grunt.registerTask('collectify', function () {
grunt.util.spawn({
cmd: 'npm',
args: ['run', 'collectify:app']
});
});
But the output is different than if I run npm run collectify:app from the command line, perhaps because of pwd issues.
Coloured bars would be nice but at the very least I'd like to see the time in numbers.
Have you tried prepending the time command before your npm run command ?
So if your command is:
npm run collectify:app
It becomes:
time npm run collectify:app
And it'll output 3 lines e.g.
real 0m11.580s
user 0m7.400s
sys 0m1.304s
Let me know if it helps!
Your best option is likely pre[foo] and post[foo] scripts.
So if I have a NPM script called "foobar" then I can create a script called "preboofar" and "postfoobar" and they will be executed automatically before and after "foobar" is executed.
So in "pre" you can touch a file with a timestamp and in "post" you can read that file and calculate the difference.

Resources