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 thebin
key ofpackage.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 thejade
package, we see#!/usr/bin/env node
as the shebang line, which is why thejade.cmd
wrapper file invokesnode.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 ofmocha-casper.cmd
shows, it makes no sense with the Unix default shell,/bin/sh
(the./bin/mocha-casperjs
file from packagemocha-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 forcmd.exe
asmocha-casperjs.bat
(also a batch file) - however, sincenpm
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 tonpm
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 yourpackage.json
'sbin
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 withnode
- use:#!/usr/bin/env node
In your
package.json
file'sbin
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 inpackage.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 placebin
field intopackage.json
and specify relative path to script. For example, jadepackage.json
contains the following code: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 generatesjade.cmd
for your OS that will launch node and pass script name as argument. But mocha-casperjs uses shell scripts, so contents of generatedmocha-casperjs.cmd
are different — it startssh.exe
instead of node.You can read about
bin
field ofpackage.json
here: https://docs.npmjs.com/files/package.json#bin