Using glob in Yarn/NPM script - node.js

I'm using a script to run multiple files with Node.js using a glob pattern to capture the files:
node build/**/*.spec.js
This seems to work fine, but when I put the same command in the scripts object as follows
"scripts": {
"test": "node build/**/*.spec.js"
}
and try to run it with yarn test or npm run test I get the following error:
Error: Cannot find module '/path/to/project/build/**/*.spec.js'
indicating that it's treating the glob pattern as a literal filename.
How can I achieve the glob behaviour in a Yarn/NPM script?

A couple of things to note here:
The behaviour of node build/**/*.js depends on your shell.
In zsh (the current default on macOS), it does what you expect; expanding the glob to all matching files and passing them to Node.
In bash and sh, it tries to run the literal file build/**/*.js with Node, which gives the "Cannot find module" error in your post.
The default shell used by npm run-script is, per the docs:
'/bin/sh' on POSIX systems
Even if you're using zsh, when you run the test script it uses sh. You can configure this with the script-shell option, e.g.:
in .npmrc (which you can update with npm config set script-shell=/bin/zsh); or
inline (by running npm --script-shell=/bin/zsh test).
However you get it running, note that only one of the matched files actually gets executed; the paths to the other two are passed as command line arguments. With three files foo.spec.js, bar.spec.js and baz.spec.js containing the following (adjusted to log each file's own name) in build/:
console.log(process.argv);
console.log("foo");
running the globbed version gives:
$ node build/**/*.spec.js
[
'<path/to>/bin/node',
'<path/to>/build/bar.spec.js',
'build/baz.spec.js',
'build/foo.spec.js'
]
bar

Related

running bash script with node, containing functions in script

Hi I am trying to run a bash script from the package.json in node.
(I know I could just use node instead of bash, but I already have written a bunch of code in bash and don't want to migrate immediately.) (Note I am on MacOS).
Inside the script I have various bash functions.
I have tried loading with various commands (code snippet of package.json):
{
"scripts": {
"start": "sh ./loadScripts.sh",
"serve": "bash ./loadScripts.sh",
"serve2": "source ./loadScripts.sh",
"serve3": "./loadScripts.sh",
},
}
However even though all of these load the script, when I try to use a function inside the script, say I try to run myFunction in the terminal after, then it says command not found.
However if I load the script directly in the terminal by typing
source ./loadScripts.sh it seems to work fine and I can use the functions I have defined in the script.
Why is this happening and how do I fix it?

How to fetch package version and date in npm script?

I have a package.json file and I have this script to create 2 separate files one for version and other one for date.
When i run the command, it generates the version-npm.txt with correct data holding the current version, but the version.txt file is generated with the exact script instead, it contains date +"%d/%m/%Y %T"
"scripts": {
"versions": "node -e \"console.log(require('./package.json').version);\" > ./public/version-npm.txt && echo `date +\"%d/%m/%Y %T\"` > ./public/version.txt"
}
I need this to be updated with the current date and time. is there any other way to it or can you help me fix it?
i tried writing the date as \"$(date)\" still the same, now i get \"$(date)\"
Why is it not working?
The way that you are obtaining the date, i.e. date +\"%d/%m/%Y %T\" or similarly using \"$(date)\" does work successfully on *nix platforms, whereby npm utilizes sh by default to run npm scripts.
However, the aforementioned method does not work on Windows because npm on that OS utilizes cmd as the default shell to run npm scripts - cmd simply does not understand the date command.
Solution
The following solution will work cross-platforms (Windows, Linux, MacOS etc) :
Firstly cd to your project directory and install the moment package by running the following command:
npm i -D moment
We'll utilize this package to obtain the date/time formatted as DD/MM/YYY HH:MM:SS.
Then redefine your versions script in the scripts section of your package.json as follows:
"scripts": {
"versions": "node -p \"process.env.npm_package_version\" > ./public/version-npm.txt && node -p \"require('moment')().format('MM/DD/YYYY HH:mm:ss')\" > ./public/version.txt"
}
Explanation:
The npm script (above) utilizes the nodejs command line option -p to evaluate and print the result of the following inline JavaScript:
process.env.npm_package_version
This essentially utilizes nodejs process.env to read the environment variable npm_package_version which npm creates. See my answer here for further explanation.
The version is then redirected (>) to the file using the same method as per your attempt:
> ./public/version-npm.txt
Note: You could continue to utilize your current, more verbose, solution to obtain the version from package.json if you prefer, i.e.
node -e \"console.log(require('./package.json').version);\"
Next we obtain the date. Again we utilize the nodejs command line option -p to evaluate and print the result of the following inline JavaScript:
require('moment')().format('MM/DD/YYYY HH:mm:ss')
The date value is redirected (>) to the file as follows:
> ./public/version.txt

Node, when I run package.json `bin` `command` , give me `syntax error near unexpected token `(' `

when I run package.json bin command , give me syntax error near unexpected token(' ` .
package.json:
"bin": {
"grabfilenames": "./index.js"
}
npm link:
/usr/local/bin/grabfilenames -> /usr/local/lib/node_modules/grabfilename/index.js
/usr/local/lib/node_modules/grabfilename -> /Users/dulin/workspace/grabfilename
when I run my cli:
grabfilenames -p /Users/dulin/workspace/learn-jquery
give me an error:
/usr/local/bin/grabfilenames: line 1: syntax error near unexpected token `('
/usr/local/bin/grabfilenames: line 1: `const fs = require('fs');'
How to solve it? Thanks!
The documentation states that:
On install, npm will symlink that file into prefix/bin for global installs, or ./node_modules/.bin/ for local installs.
This means that npm does nothing special to your file and expect it to be executable on unix. Your bin file can be a perl script, a compiled C program, a shell script, a Ruby script or even a node.js javascript app.
Therefore what causes your app to run is not npm. It is your OS. So your script must be executable (as I said, it can even be a compiled binary).
On unix, to automatically execute a script with the correct interpreter you need to have a sh-bang as the first line in the file. For node.js I generally use this line:
#! /usr/bin/env node
You can generally just use:
#! /whatever/path/to/node
but depending on the OS or even distro node.js may be installed at different locations. So /usr/bin/env is a program that loads your default environment variables which includes $PATH that will allow the shell to automatically find where node.js is installed.

node_modules/.bin/package.cmd file in node js

I am new to node js. I am trying to build a npm module and confused a bit with cmd file present in /node_modules/.bin folder with the name of the package locally.
I installed multiple packages as dependencies and found that cmd files are different.
jade.cmd
#IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\jade\bin\jade" %*
) ELSE (
#SETLOCAL
#SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\jade\bin\jade" %*
)
mocha-casperjs.cmd
#IF EXIST "%~dp0\/bin/sh.exe" (
"%~dp0\/bin/sh.exe" "%~dp0\..\mocha-casperjs\bin\mocha-casperjs" %*
) ELSE (
#SETLOCAL
#SET PATHEXT=%PATHEXT:;.JS;=;%
/bin/sh "%~dp0\..\mocha-casperjs\bin\mocha-casperjs" %*
)
My question is, if it is auto-generated by NPM, why npm creates 2 different files for 2 different packages. Is is something a user creates and tells NPM?
tl;dr
On Windows, npm creates the wrapper batch file (*.cmd) based on whatever shell/interpreter is specified in the script file's shebang line.
What you're seeing is npm's emulation of the Unix shebang mechanism for Windows, where the very 1st line of an executable plain-text file (script), if it starts with magic characters #!, tells the OS what interpreter/shell to pass the script to for execution (see this answer of mine for more information).
Since Windows doesn't support shebang lines, npm creates a wrapper *.cmd (batch) file, that explicitly invokes the "binary" file (the script file specified in the bin key of package.json) with whatever executable is specified in the script file's shebang line.
In other words: npm parses the script's shebang line to determine what shell/interpreter to invoke, and creates the wrapper batch script accordingly.
If we peek at ./bin/jade.js inside the jade package, we see #!/usr/bin/env node as the shebang line, which is why the jade.cmd wrapper file invokes node.exe.
This is the typical case: a script written in JavaScript that must be executed by the Node.js engine.
However, any other shell/interpreter may be specified too, but doing so only makes sense if a given shell/interpreter is also available on Windows; for node, that is a given, but, as the contents of mocha-casper.cmd shows, it makes no sense with the Unix default shell, /bin/sh (the ./bin/mocha-casperjs file from package mocha-casper.js contains shebang line #!/bin/sh).
The makers of the mocha-casperjs have chosen to roll their own Windows integration by re-implementing the Unix shell script for cmd.exe as mocha-casperjs.bat (also a batch file) - however, since npm is unaware of this, this batch file is not placed in the PATH (global) / not discoverable by name only (when calling CLIs in a project context).
More generally, for scripts to also work on Windows, it doesn't make sense to use shebang lines with absolute, POSIX-style paths - EXCEPT if the target shell/interpreter is specified by indirect invocation via /usr/bin/env, which signals to npm that it should be looked for in the PATH (again, see this answer of mine for more information).
(Additionally, the wrapper batch files also look in their own directory for the executable, but that is rarely helpful, given that npm wouldn't copy them there even if you explicitly added them as an entry in your package.json's bin key.)
As an aside: recent versions of npm also create extension-less wrapper scripts for Unix environments, notably Cygwin, and Bash on Ubuntu on Windows.
When creating your own packages, for CLIs ("binaries") - directly executable scripts that act like command-line utilities - that are part of an npm package to work on Windows too, you must:
Add a shebang line to your scripts - even if you only ever intend to run them on Windows.
Define that shebang line as #!/usr/bin/env <interpreter-executable-filename-without-extension>; typically - if your script is written in JavaScript and must be executed with node - use:
#!/usr/bin/env node
In your package.json file's bin key, define the script's key without extension., because .cmd is directly appended to the key name when the wrapper batch file is created (on Unix, a symlink is created named for the key as-is); e.g.:
"bin": { "foo": "./bin/fooj.js" }
Caveat: If the script pointed to by the bin key in package.json has extension .js but has no shebang line, the wrapper script will invoke the .js file directly, which will by default execute it with WSH (specifically, with JScript, its JavaScript engine) rather than Node.js, which won't work as intended - see this question for what symptoms you would see.
You can tell npm to make executable file provided with your package accessible from ./node_modules/.bin directory (in case of local install) or globally (when module is installed globally). You should place bin field into package.json and specify relative path to script. For example, jade package.json contains the following code:
"bin": {
"jade": "./bin/jade.js"
}
When installing jade package, npm makes this script (./bin/jade.js) accessible by generating, if neccessary, wrapper script (jade.cmd) which contents depend on current OS, shell, and the type of script you wish to make accessble. Jade uses .js script, and npm generates jade.cmd for your OS that will launch node and pass script name as argument. But mocha-casperjs uses shell scripts, so contents of generated mocha-casperjs.cmd are different — it starts sh.exe instead of node.
You can read about bin field of package.json here: https://docs.npmjs.com/files/package.json#bin

Why does passing arguments to the command in an env invocation not work?

I have a shell script to run node with some arguments like so:
#!/usr/bin/env node --harmony_proxies
...
This works fine under OS X but in Ubuntu it errors with:
/usr/bin/env: node --harmony_proxies: No such file or directory
Node is definitely installed and on the PATH since if I remove the --harmony_proxies flag it works fine. Is there some different way of passing arguments when using env in Ubuntu?
On Linux, the entire string following the interpreter name is passed as a single argument to the interpreter, and this string can include white space. [1] Thus, command line arguments are not split, and env command is trying to execute node --harmony_proxies file, which obviously could not be found. See here and here for more details.
Here is an alternative solution for you:
#!/bin/sh
exec node --harmony_proxies "$#"
Hope it helps. Good luck!
If the node command is installed in a fixed location, you can use it directly:
#!/usr/bin/node --harmony_proxies
But if you can't assume that node is installed in a particular location, go with one of the other answers.
If you don't want to modify the source, a wrapper alias might be the right solution.
Example from my .bashrc:
alias how2='/usr/bin/env node --no-deprecation "$(which how2)"'

Resources