5

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?

prabhat
  • 620
  • 5
  • 18

2 Answers2

6

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
1

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

Victor
  • 65
  • 2
  • 7
  • bin in package.json tells npm where package executable is. But when you install a package, npm install it in the node_modules/jade (jade is just an example) and add a cmd file in node_modules/.bin/jade.cmd which points to the node_modules/jade/bin/jade. – prabhat Jun 02 '16 at 09:32
  • @prabhat, yes, you are right. I wrote about generation of cmd file npm places into node_modules/.bin directory. You cannot manually change contents of this cmd file, it is autogenerated. Is it answers your question, or i did not understand you? – Victor Jun 02 '16 at 09:45
  • If npm generates it, why it is different for 2 packages, is my question. I cannot find anything on google about it so I asked here. – prabhat Jun 02 '16 at 10:15
  • 1
    @prabhat, i wrote about it. It is because jade package.json specifies javascript file (which runs through node.exe) in bin option, and mocha-casperjs specifies shell script which runs through sh.exe command interpreter. Because of it, npm generates one script for jade (which starts node) and different one for mocha-casperjs (which starts sh.exe). If mocha-casperjs author specifed js file in bin option, npm would generate exactly the same cmd file as the one generated for jade (except for the file names, of course). – Victor Jun 02 '16 at 10:47