double commande in package.json don't work - npm

I have an app where I would like to compile all my handlebars template/partials
The command I execute is (npx cause is a local package) =>
Build Views
npx handlebars ./public/templates/views -f ./public/templates/view.js --extension 'hbs'
Build Template
npx handlebars ./public/templates/partials -f ./public/templates/partials.js --extension 'hbs'
It work if I run them independently
now, I tried to combine them,
"scripts": {
"hbs": "npx handlebars ./public/templates/views -f ./public/templates/view.js --extension 'hbs' && npx handlebars ./public/templates/partials -f ./public/templates/partials.js --extension 'hbs'",
},
but the command do not run, instead, I get prompted with the library !help like if the command is invalid.
Is there something I miss ?

Per comments on question, the single quotes are being incorrectly included into the --extension option for handlebars. The suggested format for the script is:
"scripts": {
"hbs": "handlebars ./public/templates/views -f ./public/templates/view.js --extension hbs && handlebars ./public/templates/partials -f ./public/templates/partials.js --extension hbs",
},

Related

Is there a way to get the name of the npm script passed to the command specified by that script?

With npm or yarn, is it possible for the script specified by an npm script to know the name of the npm script itself? For example:
"scripts": {
"foo": "echo Original command: $0",
"bar": "echo Original command: $0"
}
I'd like the result of those two scripts to be something like:
Original command: yarn run foo
Original command: yarn run bar
But all I actually get is: Original command: /bin/sh.
And in case it makes a difference, it's just the name of the script I need, not the yarn run part, so output like Original command: foo would be fine.
NPM adds the npm_lifecycle_event environment variable. It's similar to package.json vars.
*Nix (Linux, macOS, ... )
On *nix platforms npm utilizes sh as the default shell for running npm scripts, therefore your scripts can be defined as:
"scripts": {
"foo": "echo The script run was: $npm_lifecycle_event",
"bar": "echo The script run was: $npm_lifecycle_event"
}
Note: The dollar prefix $ to reference the variable.
Windows:
On Windows npm utilizes cmd.exe as the default shell for running npm scripts, therefore your scripts can be defined as:
"scripts": {
"foo": "echo The script run was: %npm_lifecycle_event%",
"bar": "echo The script run was: %npm_lifecycle_event%"
}
Note: The leading and trailing percentage sign % used to reference the variable.
Cross-platform (Linux, macOS, Windows, ... )
For cross-platform you can either:
Utilize cross-var to enable a single syntax, i.e. using the dollar sign prefix $ as per the *nix syntax.
Or, utilize the node.js command line option -p to evaluate and print the result of the following inline JavaScript:
"scripts": {
"foo": "node -e \"console.log('The script run was:', process.env.npm_lifecycle_event)\"",
"bar": "node -e \"console.log('The script run was:', process.env.npm_lifecycle_event)\""
}
Note In this example we:
Access the npm_lifecycle_event environment variable using the node.js process.env property.
Utilize console.log (instead of echo) to print the result to stdout

Output to command line within npm scripts?

I have a script in my package.json with the following code:
"scripts": {
"build": "postcss tailwind/tailwind.css -o css/cenic.tailwind.css",
"watch": "postcss tailwind/tailwind.css -o css/cenic.tailwind.css --watch"
}
It works fine. but how do I get it to output text to the command line, something like
script ran at {{ date(now) }}
In other words, I want to see a notification when a script has run.
Cross Platform (Windows/Linux/macOS...)
You can do the following to log the date/time when the script begins its task:
"build": "node -e \"console.log('script started at: %s', Date())\" && postcss tailwind/tailwind.css -o css/cenic.tailwind.css"
Explanation:
The part on the left side of the && operator that reads;
node -e \"console.log('script started at: %s', Date())\"
utilizes Node.js command line option -e to evaluate the inline JavaScript.
The inline script utilizes JavaScript's console.log(...) and Date().
The commands on the right side of the && operator are whatever command(s) you want to run, in your scenario it's the postcss command.
Variations:
To color the log you could add some ANSI/VT100 Control sequences. For instance:
"build": "node -e \"console.log('%sscript started at: %s%s', '\\x1b[42;30m', Date(), '\\x1b[0m' )\" && postcss tailwind/tailwind.css -o css/cenic.tailwind.css"
To log when the npm script completed instead of started you can switch the order of the commands. For example:
"build": "postcss tailwind/tailwind.css -o css/cenic.tailwind.css && node -e \"console.log('script completed at: %s', Date())\""
Nix platforms only (Linux/MacOS...)
If you only want a solution that runs on *nix platforms, then you can do the following instead:
"build": "echo \"script started at: $(date)\" && postcss tailwind/tailwind.css -o css/cenic.tailwind.css"
Explanation:
The part on the left side of the && operator that reads:
echo \"script started at: $(date)\"
utilizes the shells echo command to print the message to the command line.
Command substitution, i.e. the $(...) part, is utilized to obtain the output from the shells date command.
The commands on the right side of the && operator are whatever command(s) you want to run, in your scenario it's the postcss command.
If you want to apply visual styling to the echo command please refer to my answer here

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

Passing argument to the middle of an npm script

Title says it all. I want to be able to pass the argument to the middle of an npm script so that I may do the following.
$ npm run deploy -- <destination-path>
In package.json
"scripts": {
"deploy": "robocopy dist <destination-path> /E /NP"
}
Is this possible without using environment variables or npm's configuration variables?
Per Passing args into run-scripts #5518 it would appear that is it not possible to pass arguments to the middle of the script.
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.)
However, an alternative to this using the npm configuration block has been documented here. My implementation then looks like this:
"name": "foo"
"config": { "destination" : "deploy" },
"scripts": { "deploy": "robocopy dist %npm_package_config_destination% /E /NP" }
I can then override this on the command line and on my build server with:
npm run deploy --foo:destination=C:\path\to\deploy\dir
You can use an environment variable to set the destination path.
PATH=/path/to/file npm run deploy -- $PATH
or
export PATH=/path/to/file
npm run deploy -- $PATH
I have a different way to do that via shell.
If your npm command is:
"deploy": "robocopy dist ${1} /E /NP"
Where ${1} is the parameter you want to substitute.
Then wrap it in a function as follow:
"deploy": "func() { robocopy dist ${1} /E /NP";}; func"
then you can run a positional parameter substitution in shell as follow:
npm run deploy -- xyz
which would run
robocopy dist xyz /E /NP
And since this is a shell script, you can use default parameters as well:
"deploy": "func() { robocopy dist ${1:-xyz} /E /NP";}; func"
And you can use it as follows:
npm run deploy <==> robocopy dist xyz /E /NP
npm run deploy -- abc <==> robocopy dist abc /E /NP
You can make use of the arg structure of "sh -c". In my example below, I have to echo-feed the npm arg into a language parser. The argument for npm run foma <some word> will be in place of the $0:
"sayhello": "bash -c 'echo hello $0!"
A cross-platform solution I use is:
"arg-helper": "node -e \"process.stdout.write(require('child_process').execSync(process.argv[1].replace('$', process.argv[2] || '')))\"",
"sayhello": "npm run arg-helper \"echo hello $!\"
...
>npm run sayhello world
Levereging npm_config_* variables.
package.json
{
"scripts": {
"echoMyParam": "echo 'your param value is' $npm_config_foo"
}
}
Run
npm run echoMyParam --foo=bar
Result
your param value is bar
It's important to check the docs for other cases: https://docs.npmjs.com/using-npm/config

Unable to read parameters from package.json

I am unable to read parameters from package.json, so it's always $(directory).
and this is my command:
npm run migrate -- -directory "migration_dir"
package.json
{
"name": "XXXX",
"version": "0.1.0",
"description": "XXX",
"main": "app.js",
"scripts": {
"migrate": "cd $(directory)"
},
"keywords": [
"XXXX"
],
"author": "XXXX",
"license": "MIT"
}
Thank you.
The problem you may be facing is two-fold.
1. Arguments syntax for run-script
From npm docs:
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:
npm run test -- --grep="pattern"
Arguments after -- will be passed directly, in your case that is
$ cd $(directory) -directory "migration_dir"
To fix that, you have to change your script definition accordingly:
"scripts": {
"migrate": "cd"
}
And pass the directory with no -directory option:
$ npm run migrate -- "migration_dir"
This will execute
$ cd "migration_dir"
2. Scripts are executing in a subshell
The migrate script you posted is useless because all npm scripts are executed in subshells, and changing the working directory from inside a subshell doesn't make a difference when a subshell exits.
Although if your script does anything beyond cd that will work:
"scripts": {
"foo": "cd / && pwd"
}
Part of the script after && will see the effect of cd, but your shell won't.
$ npm run foo
> cd / && pwd
/
$ pwd
/your/original/path
However, you can't specify additional arguments using -- anymore (npm run foo -- arg will do cd / && pwd arg).
It's look like You want to read a ENV parameter. Bash notation is ${directory} not $(directory).
Now You can run migration
directory="migration_dir" npm run migrate
The output is
cd migration_dir
Pros of this solution is possibility of use complicated scripts.