How to make bin script available for npm package installed locally - node.js

I read on the npm documentation that you can't use bin scripts of locally installed packages.
So, how gulp can be launched as bin command when installed locally?
What's making it available when locally installed, I reviewed the gulp package.json and the bin scripts, I don't found any answer.

From NPMJS documentation:
To use this, supply a bin field in your package.json which is a map of command name to local file name. On install, npm will symlink that file into prefix/bin for global installs, or ./node_modules/.bin/ for local installs.
So, your locally installed packages binaries will be executable like this
./bin/node_modules/.bin/the_binary
This is if you want to launch the binary directly. Or, as specified in the scripts part of the documentation:
In addition to the shell's pre-existing PATH, npm run adds node_modules/.bin to the PATH provided to scripts.
Thus, you can simply write a wrapper script like
scripts: {
"build": "the_binary"
}
and call your script like this
npm run build
Bonus
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"

You can use lpx https://www.npmjs.com/package/lpx to
run a binary found in the local node_modules/.bin folder
run a binary found in the node_modules/.bin of a workspace root from anywhere in the workspace
lpx does not download any package if the binary is not found locally (ie not like npx)
Example : lpx tsc -b -w will run tsc -b -w with the local typescript package

Related

What's the difference between "npm install -g" (no args) and "npm link" to create CLI command?

To make a local node.js package work as a global commandline tool, I first add this to package.json
"bin": {
"myCommand": "./index.js"
}
According to the npm docs, I then have two options:
Within that folder, run npm install -g. According to the node docs:
npm install (in package directory, no arguments) ... In global mode
... installs the current package context (ie, the current working
directory) as a global package.
OR
Within that folder, run npm link. The node docs say
npm link in a package folder will create a symlink in the global
folder ... will also link any bins in the package to
{prefix}/bin/{name}
Both of these routes lets me run myCommand anywhere in the terminal while local edits are reflected in the tool without reinstalling/linking.
For local CLI tool purposes, these commands seem functionally identical. Is this true? Why would I want to use npm install -g (no args) when npm link seems functionally identical, plus affording extra capability?
Note: this question is purely about a local node project, nothing to do with anything in a remote package registry

Locally-installed cli NPM project that is easy to execute

I'm building a cli node module. I would like people to be able to npm install it and use it right away with a command like npm my-project --arg=foo. It's meant for local project CLI use as a dev tool, not global installation.
It seems like the standard is to use bin in the package.json, but I don't understand some things about it:
When should I use bin and when should I use scripts?
How to I run the command in the including project? npm my-project doesn't do it.
Here is what I am doing now in package.json:
{
"name": "my-project",
"bin": "./cli.js"
}
And I can run it locally:
node cli.js --arg=foo
But when I npm-install my-project somewhere else, I don't know how to run the script it puts in bin (npm run my-project doesn't work), or if I'm using this correctly.
Let's start by explaining the difference between bin and scripts: the former you use if you want to provide a command line tool, the latter you use if you want to provide an additional command to npm (with some caveats though, see below).
In your situation, I think you want to use bin. However, instead of the user using npm my-project --arg=foo, they will use my-project --arg=foo, provided that your script is called my-project. To make that happen, your package.json will contain something like this:
"bin" : "./bin/my-project"
During installation, this will copy ./bin/my-project to a "bin" directory (usually /usr/local/bin on Unix-like OS'es). During development, you can call it as node bin/my-project, or even just ./bin/my-project, provided that it has correct permissions and "shebang".
EDIT: so I forgot that npm will use the package name, and not the name of the file in ./bin, as the executable name (if bin is a string). If your package is called my-project, and you install the package (you need to use the -g flag before npm will install the executable), it will create an executable called my-project, regardless of where the bin property points to.
In other words:
package.json:
"name" : "my-project"
"bin" : "./cli.js"
npm install -g:
copies ./cli.js to /usr/local/bin/my-project and sets executable permissions
END EDIT
FWIW, storing CLI tools in ./bin is convention, but not mandatory.
The scripts directive is useful for more internal purposes. For instance, you can use it to run a test suite, or linters, or pre/post install scripts.
Lastly, there are various modules available to help with command line parsing. I like docopt, but other often-used modules are commander or nomnom. You already mentioned yargs.

'istanbul' is not recognized as an internal or external command

I have just added the istanbul test coverage module to my node.js application, but when I run (from command prompt):
> instanbul cover myTests.js
as mentioned in the documentation, I get:
'istanbul' is not recognized as an internal or external command
I am under Windows 7. The npm installation returned no error. I see nothing in the documentation about extra installation steps.
What am I missing?
The problem I see with this is when you move the project to a different server. Istanbul will need to be installed again globally. This defeats the npm model of package management. Another way to achieve this is to:
npm install --save-dev istanbul
Modify package.json script entries:
scripts: {
"coverage": "node ./node_modules/.bin/istanbul ...."
}
Then call it using:
npm coverage
A shortcut for your local machine is also to set your environment path to include node executables:
export PATH=$PATH:./node_modules/.bin
In which case, any executables installed to your package will be available on the command line automatically because of path checking.
For a module to be runnable from anywhere, you need to install it globally (with -g option).

What is the purpose of .bin folder in node_modules?

Today one colleague explained me how to create nodejs projects and I notice that in ./node_modules there is an invisible folder named .bin. I must said that I discovered this after adding to the project "bootcamp" and "bower" tools. What's .bin purpose? What is it created for?
That is a folder where binaries (executables) from your node modules are located.
NPM site states:
Executables When in global mode, executables are linked into
{prefix}/bin on Unix, or directly into {prefix} on Windows.
When in local mode, executables are linked into ./node_modules/.bin so
that they can be made available to scripts run through npm. (For
example, so that a test runner will be in the path when you run npm
test.)
The directory node_modules/.bin is where the binaries of the modules used by your project are stored, normally using symbolic links to the respective binaries in the corresponding module's directory. For example, that is how I see the binary standard from the npm package standard (JavaScript style guide, linter, and formatter)
$ ls node_modules/.bin/standard -l
lrwxrwxrwx 1 jfolpf jfolpf 22 jul 17 08:29 standard -> ../standard/bin/cmd.js
When I run node_modules/.bin/standard I am indeed running node_modules/standard/bin/cmd.js from the npm package standard. This symbolic link was created upon the installation of the package, that is, upon npm install standard
These binaries also allow you to use modules directly from npm scripts. For example, you may not have installed standard globally with npm install standard -g, which means that you cannot run standard directly from your terminal on your module's main directory.
But you can write an npm start or npm test script by adding the following, respectively, to your package.json:
"scripts": {
"start": "standard src/*.js",
"test": "standard src/*.js && node myTest.js"
}
and this is completely correct given you have standard as the project dependency. Even though the module is not global and not usable by the operating system directly, npm can look for the bin folder for the given standard module name and trigger the compiled binary. So indeed, npm runs such a script :
"start": "node_modules/.bin/standard src/*.js",

How to run a node.js app from name

How do you create a console app that you can run by a name instead of using node and a .js file to start the app?
Instead of...
$ node myapp.js --out ./folder/
Can I...
$ myapp --out ./folder/
You can use the bin option in your package.json file to make your script executable. From the documentation for package.json:
A lot of packages have one or more executable files that they'd like
to install into the PATH. npm makes this pretty easy (in fact, it uses
this feature to install the "npm" executable.)
To use this, supply a bin field in your package.json which is a map of
command name to local file name. On install, npm will symlink that
file into prefix/bin for global installs, or ./node_modules/.bin/ for
local installs.
Click the link and search for bin: https://www.npmjs.org/doc/json.html for more info.

Resources