Propagate arguments to two commands combined with && - node.js

I have two commands in package.json combined with '&&':
"scripts": {
"someAction": "node dist/scripts/actionOne && node -r dist/scripts/actionTwo"
},
Is it possible to call this script from cli, passing arguments to both 'actionOne' and 'actionTwo' ?
When calling
npm run someAction -- firstArg, secondArg args are passed only to 'actionOne' script.
*Number of args expected by actionOne and actionTwo are identical.

After looking at the docs, it looks like npm-run-all would work with argument placeholders.
We can use placeholders to give the arguments preceded by -- to scripts.
$ npm-run-all build "start-server -- --port {1}" -- 8080
This is useful to pass through arguments from npm run command.
{
"scripts": {
"start": "npm-run-all build \"start-server -- --port {1}\" --"
}
}
$ npm run start 8080
> example#0.0.0 start /path/to/package.json
> npm-run-all build "start-server -- --port {1}" -- "8080"
So you could do something like this:
{
"scripts": {
"start": "npm-run-all dist/scripts/actionOne -- --arg {1} && dist/scripts/actionTwo -- --arg2 {2}"
}
}
Then:
npm run start arg1 arg2

If there is a real answer, I wanna know. But also, you can make a script like this
some-action.sh
set -e
dist/scripts/ActionOne $#
dist/scripts/ActionTwo $#
and then put this in your package.json
"scripts": {
"someAction": "bash some-action.sh"
},

Related

Check file exists before launch it in webpack npm scripts

I have a package.json like this:
...
"scripts": {
"dev": "webpack --config webpack.dev.config.js --mode development --progress --colors",
"postdev": "if (Test-Path \"./postdev.sh\" ) { echo \"file exists\"; ./postdev.sh }"
},
...
How can I check if file "postdev.sh" exists and then launch it in NPM-scripts section?
I run that command in the terminal and it goes correctly, but if I try to launch that npm-script it says "Unexpected appearance: "./postdev.sh"."
on macos or linux try this one for postdev:
"postdev": "test -f ./postdev.sh && echo 'file exisits' && ./postdev.sh",
Finnally found a solution (maybe it works only on Windows, but it is enough for me):
"postdev": "if exist postdev.sh ( postdev.sh )",
You can use path-exists-cli package, a cross-platform tool, to check if a file/directory exists and use && or || after to run the next command if exists or not, respectively:
{
"scripts": {
// other scripts...
"postdev": "path-exists ./postdev.sh && echo 'Exists' || echo 'Does not exists'"
}
}

Compare proccess.env.NODE_ENV with String

Why when I compare those two I get false
const production = process.env.NODE_ENV === 'production';
Even when I set process.env.NODE_ENV to production I still get false value.
Why?
Example:
package.json SCRIPTS:
"scripts": {
"start:prod": "set NODE_ENV=production && nodemon server.js"
}
VS CODE
const production = process.env.NODE_ENV === 'production';
console.log(process.env.NODE_ENV); // production
console.log(typeof process.env.NODE_ENV); // string
console.log(typeof 'production'); // string
console.log(production) // false
Why production returns false even though the values are exactly the same?
To set node environment in powershell use below:
$env:NODE_ENV = 'production'
drop the spaces before and after &&
"scripts": {
"start:prod": "set NODE_ENV=production&&nodemon server.js"
}
You should do something like this
"scripts": {
"start:prod": "NODE_ENV=production nodemon server.js"
}
set command is used to set or unset values of shell options and positional parameters and not the environment variables
I met the similar issue and it took me a couple of hours to realize this.
Your package.json has this script
"scripts": {
"start:prod": "set NODE_ENV=production && nodemon server.js"
}
However you should not that your comparison should return false becouse your env you set in the script contains space here production &&
To be able to return true you should remove that space and your script should look like this.
"scripts": {
"start:prod": "set NODE_ENV=production&& nodemon server.js"
}
Keep your comparison expression it is ok for it.
It works if you use includes instead of the equality operator
process.env.NODE_ENV.includes('production') // true

how to get returned value from npm-scripts?

In package.json, I have:
"scripts": {
"foo": "echo foo",
"bar": "npm run foo > result.txt"
}
and then if I run npm run bar, I will get the text in result.txt:
> kaze#0.0.55 foo D:\code\kaze
> echo foo
foo
That is not what I expected. What I expected is just:
foo
So, what is the problem with my npm scripts?
When you use >, all stdout of its left command execution would be written to result.txt, including descriptive information shown in the question. It has nothing to do with npm run bar.
If you just run npm run foo > result.txt in command line window, same result would be retrieved.
To only include foo in result.txt, npm option --silent can be used:
"scripts": {
"foo": "echo foo",
"bar": "npm run foo --silent > result.txt"
},

Access command line argument in scripts of package.json

I have created a command in package.json file
"create": "ng g component process.env.page --it false"
Now I want to access the passed page argument in the above command so that user can pass the component name to the npm command
I am running the above command as
npm run create --page login
and this runs the
ng g component process.env.page --it false
so new component is created with name process.
How can I access the passed page (login) in my script?
You can use the primitive process.argv or yargs which is a lot more powerful
here is a yargs example
const argv = require("yargs").argv;
let page = argv.page //get the page
The syntax of npm run is:
npm run <command> [-- <args>]
So you need to pass -- before your args. Your command should be as follows:
npm run create -- --page login
const minimist = require('minimist');
let args = minimist(process.argv.slice(2), {
default: {
port: 8080
},
});
run with
npm run start -- --port=8090
args contains
args: { _: [], port: 8090 }

How to use environment variables in package.json

Because we don't want sensitive data in the project code, including the package.json file, using environment variables would be a logical choice in my opinion.
Example package.json:
"dependencies": {
"accounting": "~0.4.0",
"async": "~1.4.2",
"my-private-module":"git+https://${BB_USER}:${BB_PASS}#bitbucket.org/foo/bar.git"
Is this possible?
The question is not if this is wise or not good, just if it's possible.
In case you use .env file, let's use grep or eval to get a value environment variable from the .env file.
Updated start2 as #Paul suggested:
"scripts": {
"start": "NODE_ENV=$(grep NODE_ENV .env | cut -d '=' -f2) some_script",
"start2": "eval $(grep '^NODE_ENV' .env) && some_script"
}
I have similar but different requirement. For me, I want to use environment variables in the scripts.
Instead of using the environment variables directly in package.json, I do:
"some-script": "./scripts/some-script.sh",
And in some-script.sh:
#!/bin/sh
npm run some-other-script -- --prop=$SOME_ENV_VAR
Here's how I managed to work around package.json to achieve the same purpose. It uses a script that reads from a custom section of package.json for URL modules, interpolates environment variables in them, and installs them with npm install --no-save (the --no-save could be omitted, depending on the usecase).
As a bonus: it tries to read the env variable from .env.json, which can be gitignore'd, and very useful for development.
Create a script that will read from a custom section of package.json
env-dependencies.js
const execSync = require('child_process').execSync
const pkg = require('./package.json')
if (!pkg.envDependencies) {
return process.exit(0)
}
let env = Object.assign({}, process.env)
if (typeof pkg.envDependencies.localJSON === 'string') {
try {
Object.assign(env, require(pkg.envDependencies.localJSON))
} catch (err) {
console.log(`Could not read or parse pkg.envDependencies.localJSON. Processing with env only.`)
}
}
if (typeof pkg.envDependencies.urls === 'undefined') {
console.log(`pkg.envDependencies.urls not found or empty. Passing.`)
process.exit(0)
}
if (
!Array.isArray(pkg.envDependencies.urls) ||
!(pkg.envDependencies.urls.every(url => typeof url === 'string'))
) {
throw new Error(`pkg.envDependencies.urls should have a signature of String[]`)
}
const parsed = pkg.envDependencies.urls
.map(url => url.replace(/\${([0-9a-zA-Z_]*)}/g, (_, varName) => {
if (typeof env[varName] === 'string') {
return env[varName]
} else {
throw new Error(`Could not read env variable ${varName} in url ${url}`)
}
}))
.join(' ')
try {
execSync('npm install --no-save ' + parsed, { stdio: [0, 1, 2] })
process.exit(0)
} catch (err) {
throw new Error('Could not install pkg.envDependencies. Are you sure the remote URLs all have a package.json?')
}
Add a "postinstall": "node env-dependencies.js" to your package.json, that way it will be run on every npm install
Add your private git repos to package.json using the URLs you want (note: they all must have a package.json at root!):
"envDependencies": {
"localJSON": "./.env.json",
"urls": [
"git+https://${GITHUB_PERSONAL_ACCESS_TOKEN}#github.com/user/repo#semver:^2.0.0"
]
},
(the semver specifier #semver:^2.0.0 can be omitted, but refers to a git tag, which can be very useful, as it makes your git server a fully-fledge package manager)
npm install
No, it's not possible. You should access the repo using git+ssh, and store a private key in ~/.ssh.
Your line then looks like:
"my-private-module":"git+ssh://git#bitbucket.org/foo/bar.git"
Which doesn't contain anything sensitive.
No it isn't possible as npm does not treat any string values as any kind of templates.
It may be better to just use git+ssh (if your provider supports it) with an ssh agent.
You can use environment values to inject in your package.json like this:
Any environment variables that start with npm_config_ will be interpreted as a configuration parameter. For example, putting npm_config_foo=bar in your environment will set the foo configuration parameter to bar. Any environment configurations that are not given a value will be given the value of true. Config values are case-insensitive, so NPM_CONFIG_FOO=bar will work the same.
https://docs.npmjs.com/misc/config#environment-variables
I had the same need and my solution was based on #Long Nguyen's response. This way, I can only rely on what's defined on the .env file.
.env
...
SKIP_PREFLIGHT_CHECK=true
...
package.json
...
"scripts": {
"test": "yarn cross-env $(grep SKIP_PREFLIGHT_CHECK ../../.env) react-app-rewired test --watchAll=false"
}
...
You can install package https://www.npmjs.com/package/env-cmd
and all your envs from .env file will be visible
ie:
./.env:
ENV1=THANKS
ENV2=FOR ALL
ENV3=THE FISH
Package.json:
"scripts": {
"test": "env-cmd pact-broker can-i-deploy --broker-token=${ENV1}"
}
or another example from your question:
"my-private-module":"env-cmd git+https://${BB_USER}:${BB_PASS}#bitbucket.org/foo/bar.git"
For complicated environment variables, you can use
https://stedolan.github.io/jq/
to access JSON file (env file at your case)
JSON file could be something like
{
"env" :
{
"username" : "1345345",
"Groups" : [],
"arraytest" : [
{
"yes" : "1",
"no" : "0"
}
]
}
}
so the script could be something like this to access yes value
"scripts": {
"yes": "jq [].arraytest[0].yes?"
}
If you're running node inside a Docker container
Use Docker Compose to inject the env variable
app:
environment:
- NODE_ENV=staging
Run your package.json script from your Dockerfile
CMD [ "npm", "run", "start" ]
Use echo or printenv
"scripts": {
"start": "node -r dotenv/config app.js dotenv_config_path=/run/secrets/$(echo $NODE_ENV)"
"start": "node -r dotenv/config app.js dotenv_config_path=/run/secrets/$(printenv NODE_ENV)"
}
Don't use this for sensitive env variables. It's a really good way to point to a Docker secrets file (like this example shows).

Resources