Output to command line within npm scripts? - npm

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

Related

npm - Pass command line arguments to different part other than tail/end of script [duplicate]

This question already has answers here:
Pass command line args to npm scripts in package.json
(4 answers)
Closed 2 years ago.
I have the following script in my package.json:
{
"build-dist": "tsc -p src/main/ts/tsconfig.json --outDir build/dist/npm-fs && cp LICENSE.txt README.md build/dist/npm-fs && node scripts/build/build-dist.mjs"
}
My script at scripts/build/build-dist.mjs requires a command line argument to be passed. Would it be possible to have this custom script at the beginning of this script chain and still receive and process passed command line arguments?
{
"build-dist": "node scripts/build/build-dist.mjs && tsc -p src/main/ts/tsconfig.json --outDir build/dist/npm-fs && cp LICENSE.txt README.md build/dist/npm-fs"
}
It requires (and validates input) and throws an error if validation fails and since it requires a command line argument it has to be the last script called to correctly consume command line arguments. As a result, the first two scripts in the chain
tsc -p src/main/ts/tsconfig.json --outDir build/dist/npm-fs && cp LICENSE.txt README.md build/dist/npm-fs
are always executed regardless of whether validation fails or not. I'd much rather have fast-fail behavior and have execution completely halt if that script throws an error.
This question is essentially a duplicate of this. I'm hoping there's a new feature now available or maybe someone has figured out a more elegant solution.
There is no built-in feature for that. A third-party tools may be of help.
Here is a cross-platform solution:
{
"devDependencies": {
"run-z": "^1.6.2",
"shx": "^0.3.3"
},
"scripts": {
"valid": "run-z --then node scripts/build/build-dist.mjs",
"tsc": "run-z valid --then tsc -p src/main/ts/tsconfig.json --outDir build/dist/npm-fs",
"copy": "run-z valid --then shx cp LICENSE.txt README.md build/dist/npm-fs",
"build-dist": "run-z tsc,copy"
}
}
Then run it like this:
npm run build-dist -- +valid <your options here>
Note that tsc and copy tasks would run in parallel to each other, while both require the valid task to complete first.

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

Adding a delay to npm's concurrently

I am using NPM's concurrently to run:
concurrently "npm run watch-sass" "npm run watch"
"scripts": {
"test": "nps test",
"watch-sass": "multi-sass -w -d static/css/pages -o static/css/pages -n -d static/css/common -o static/css/common -n -d static/css/components -o static/css/components",
"watch": "nps webpack.build.development.watch",
...
},
However, as soon as watch-sass completes a single file, watch runs and the files do not have a chance to be included in the build.
I need to insert some kind of delay after 'watch-sass' so that 'watch' will not activate until either (1) a delay everytime, or (2) a way to know when ALL scss files have been compiled.

npm tslint don't give error

Hey all got a issue with npm and tslint I was hoping you could help me with.
Ok here comes my situation and code:
package.json
"scripts": {
"lint": "tslint -c tslint.json 'src/app/**/*.ts'",
"pretest": "npm run lint ",
"test": "echo 'No test are made'",
...
},
When I run command npm test this is the output:
input terminal
$ npm test
output terminal
> keoom#1.0.0 pretest c:\Users\Hjorth\Documents\github\keoom-angular
> npm run lint
> keoom#1.0.0 lint c:\Users\Hjorth\Documents\github\keoom-angular
> tslint -c tslint.json 'src/app/**/*.ts'
> keoom#1.0.0 test c:\Users\Hjorth\Documents\github\keoom-angular
> echo 'No test are made'
'No test are made'
If I only run command tslint -c tslint.json 'src/app/**/*.ts' I on the other hand see the linting error.
input terminal
$ tslint -c tslint.json 'src/app/**/*.ts'
output terminal
src/app/app.component.ts[1, 27]: " should be '
So as you can see there is a linting error in my code, but if I am not running the script directly It will not show. What I am expecting is this:
When I run npm test the script pretest will run, and that script will run the script lint, it will then exit with exit 0 before test script is run as it will find the linting error.
Anyone that can be of assistance.
It's the quotes around the file spec in the tslint command that are the problem:
"lint": "tslint -c tslint.json 'src/app/**/*.ts'"
^ ^
If you remove those, you should see the expected error reported by tslint.

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