Use an npm script as "bin" - node.js

I have a command line application written in TypeScript with some npm scripts defined in package.json.
"scripts": {
"start": "ts-node src/index.ts",
"start-args": "ts-node src/index.ts -- some args"
},
I would like to link and alias the TypeScript file so that I can call the program easily, so I am looking for something like a "bin" key in the package.json file.
"scripts": {
"start": "ts-node ./src/index.ts",
"start-args": "ts-node src/index.ts -- some args"
},
"bin": {
"foobar": "./src/index.ts",
"bazqux": "./src/index.ts some args"
}
ts-node is installed locally.
However, since TypeScript is not natively supported by node, just putting a shebang on ./src/index.ts won't work.
I would also like to be able to create an aliased command with default arguments, like bazqux above. When I link or install the package as global, I can run "foobar" globally as if I run "npm run start" inside the repository; or run "bazqux" globally as it's "npm run start-args".
How to achieve this?

Update: The issue described below has been fixed in ts-node#8.9.0 thanks to my report. --script-mode now resolves symlinks before looking for the tsconfig.json file. The correct shebang to use is #!/usr/bin/env ts-node-script, which is now also documented. Make sure you have the newest version installed globally with npm -g install ts-node. (I'm not sure whether you also/still need TypeScript installed globally for this. If yes: npm -g install typescript.)
Outdated: For those who also still stumble across this problem, I documented my current approach in detail in this issue. In short, many modules (including those of Node.js itself) require that you use the --esModuleInterop option for the TypeScript compiler. (You can specify this in your tsconfig.json file.) In order to be able to use your command from anywhere and not just from within your package directory, you have to use the undocumented --script-mode option for ts-node or the undocumented ts-node-script command, which does the same.
In other words, your shebang should either be #!/usr/bin/env -S ts-node --script-mode or #!/usr/bin/env ts-node-script. (The former uses -S to pass arguments to the specified interpreter. If this doesn't work on your platform, you can try to hack around this limitation.)
If you want to use the bin functionality of npm (e.g. by running npm link from the package directory), the above doesn't work because --script-mode does not (yet?) follow symbolic links. To work around this, you can use #!/usr/bin/env -S ts-node --project /usr/local/lib/node_modules/<your-project>/tsconfig.json on the first line of your script. This is not ideal, though, as it breaks platform independence. The only other option I see at the moment is to use a bash script instead and call your script from there.

It will work, if ts-node is in path variable use
#!/usr/bin/env ts-node
console.log("Hello World");
Checkout repo:
https://github.com/deepakshrma/ts-cli
Try out:
$ npm i -g https://git#github.com:deepakshrma/ts-cli.git
$ ts-test

Related

Why ts-node index.ts doesn't work, but ts-node through npm run works? [duplicate]

How do I use a local version of a module in node.js. For example, in my app, I installed coffee-script:
npm install coffee-script
This installs it in ./node_modules and the coffee command is in ./node_modules/.bin/coffee. Is there a way to run this command when I'm in my project's main folder? I guess I'm looking for something similar to bundle exec in bundler. Basically, I'd like to specify a version of coffee-script that everyone involved with the project should use.
I know I can add the -g flag to install it globally so coffee works fine anywhere, but what if I wanted to have different versions of coffee per project?
UPDATE: As Seyeong Jeong points out in their answer below, since npm 5.2.0 you can use npx [command], which is more convenient.
OLD ANSWER for versions before 5.2.0:
The problem with putting
./node_modules/.bin
into your PATH is that it only works when your current working directory is the root of your project directory structure (i.e. the location of node_modules)
Independent of what your working directory is, you can get the path of locally installed binaries with
npm bin
To execute a locally installed coffee binary independent of where you are in the project directory hierarchy you can use this bash construct
PATH=$(npm bin):$PATH coffee
I aliased this to npm-exec
alias npm-exec='PATH=$(npm bin):$PATH'
So, now I can
npm-exec coffee
to run the correct copy of coffee no matter of where I am
$ pwd
/Users/regular/project1
$ npm-exec which coffee
/Users/regular/project1/node_modules/.bin/coffee
$ cd lib/
$ npm-exec which coffee
/Users/regular/project1/node_modules/.bin/coffee
$ cd ~/project2
$ npm-exec which coffee
/Users/regular/project2/node_modules/.bin/coffee
You don't have to manipulate $PATH anymore!
From npm#5.2.0, npm ships with npx package which lets you run commands from a local node_modules/.bin or from a central cache.
Simply run:
$ npx [options] <command>[#version] [command-arg]...
By default, npx will check whether <command> exists in $PATH, or in the local project binaries, and execute that.
Calling npx <command> when <command> isn't already in your $PATH will automatically install a package with that name from the NPM registry for you, and invoke it. When it's done, the installed package won’t be anywhere in your globals, so you won’t have to worry about pollution in the long-term. You can prevent this behaviour by providing --no-install option.
For npm < 5.2.0, you can install npx package manually by running the following command:
$ npm install -g npx
Use the npm bin command to get the node modules /bin directory of your project
$ $(npm bin)/<binary-name> [args]
e.g.
$ $(npm bin)/bower install
Use npm run[-script] <script name>
After using npm to install the bin package to your local ./node_modules directory, modify package.json to add <script name> like this:
$ npm install --save learnyounode
$ edit packages.json
>>> in packages.json
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"learnyounode": "learnyounode"
},
...
$ npm run learnyounode
It would be nice if npm install had a --add-script option or something or if npm run would work without adding to the scripts block.
update: If you're on the recent npm (version >5.2)
You can use:
npx <command>
npx looks for command in .bin directory of your node_modules
old answer:
For Windows
Store the following in a file called npm-exec.bat and add it to your %PATH%
#echo off
set cmd="npm bin"
FOR /F "tokens=*" %%i IN (' %cmd% ') DO SET modules=%%i
"%modules%"\%*
Usage
Then you can use it like
npm-exec <command> <arg0> <arg1> ...
For example
To execute wdio installed in local node_modules directory, do:
npm-exec wdio wdio.conf.js
i.e. it will run .\node_modules\.bin\wdio wdio.conf.js
Update: I no longer recommend this method, both for the mentioned security reasons and not the least the newer npm bin command. Original answer below:
As you have found out, any locally installed binaries are in ./node_modules/.bin. In order to always run binaries in this directory rather than globally available binaries, if present, I suggest you put ./node_modules/.bin first in your path:
export PATH="./node_modules/.bin:$PATH"
If you put this in your ~/.profile, coffee will always be ./node_modules/.bin/coffee if available, otherwise /usr/local/bin/coffee (or whatever prefix you are installing node modules under).
Use npm-run.
From the readme:
npm-run
Find & run local executables from node_modules
Any executable available to an npm lifecycle script is available to npm-run.
Usage
$ npm install mocha # mocha installed in ./node_modules
$ npm-run mocha test/* # uses locally installed mocha executable
Installation
$ npm install -g npm-run
TL;DR: Use npm exec with npm#>=7.
The npx command which was mentioned in other answers has been completely rewritten in npm#7 which ships by default with node#15 and can be installed on node#>=10. The implementation is now equal to the newly introduced npm exec command, which is similar but not equal to the previous npx command implementation.
One difference is e.g. that it always interactively asks if a dependency should be downloaded when it is not already installed (can also be overwritten with the params --yes or --no).
Here is an example for npm exec. The double dashes (--) separates the npm exec params from the actual command params:
npm exec --no -- jest --coverage
See also the updated, official documentation to npm exec.
If you want to keep npm, then npx should do what you need.
If switching to yarn (a npm replacement by facebook) is an option for you, then you can call:
yarn yourCmd
scripts inside the package.json will take precedence, if none is found it will look inside the ./node_modules/.bin/ folder.
It also outputs what it ran:
$ yarn tsc
yarn tsc v0.27.5
$ "/home/philipp/rate-pipeline/node_modules/.bin/tsc"
So you don't have to setup scripts for each command in your package.json.
If you had a script defined at .scripts inside your package.json:
"tsc": "tsc" // each command defined in the scripts will be executed from `./node_modules/.bin/` first
yarn tsc would be equivalent to yarn run tsc or npm run tsc:
yarn tsc
yarn tsc v0.27.5
$ tsc
The PATH solution has the issue that if $(npm bin) is placed in your .profile/.bashrc/etc it is evaluated once and is forever set to whichever directory the path was first evaluated in. If instead you modify the current path then every time you run the script your path will grow.
To get around these issues, I create a function and used that. It doesn't modify your environment and is simple to use:
function npm-exec {
$(npm bin)/$#
}
This can then be used like this without making any changes to your environment:
npm-exec r.js <args>
I prefer to not rely on shell aliases or another package.
Adding a simple line to scripts section of your package.json, you can run local npm commands like
npm run webpack
package.json
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"webpack": "webpack"
},
"devDependencies": {
"webpack": "^4.1.1",
"webpack-cli": "^2.0.11"
}
}
If you want your PATH variable to correctly update based on your current working directory, add this to the end of your .bashrc-equivalent (or after anything that defines PATH):
__OLD_PATH=$PATH
function updatePATHForNPM() {
export PATH=$(npm bin):$__OLD_PATH
}
function node-mode() {
PROMPT_COMMAND=updatePATHForNPM
}
function node-mode-off() {
unset PROMPT_COMMAND
PATH=$__OLD_PATH
}
# Uncomment to enable node-mode by default:
# node-mode
This may add a short delay every time the bash prompt gets rendered (depending on the size of your project, most likely), so it's disabled by default.
You can enable and disable it within your terminal by running node-mode and node-mode-off, respectively.
I've always used the same approach as #guneysus to solve this problem, which is creating a script in the package.json file and use it running npm run script-name.
However, in the recent months I've been using npx and I love it.
For example, I downloaded an Angular project and I didn't want to install the Angular CLI globally. So, with npx installed, instead of using the global angular cli command (if I had installed it) like this:
ng serve
I can do this from the console:
npx ng serve
Here's an article I wrote about NPX and that goes deeper into it.
Same #regular 's accepted solution, but Fish shell flavour
if not contains (npm bin) $PATH
set PATH (npm bin) $PATH
end
zxc is like "bundle exec" for nodejs. It is similar to using PATH=$(npm bin):$PATH:
$ npm install -g zxc
$ npm install gulp
$ zxc which gulp
/home/nathan/code/project1/node_modules/.bin/gulp
You can also use direnv and change the $PATH variable only in your working folder.
$ cat .envrc
> export PATH=$(npm bin):$PATH
Add this script to your .bashrc. Then you can call coffee or anyhting locally. This is handy for your laptop, but don't use it on your server.
DEFAULT_PATH=$PATH;
add_local_node_modules_to_path(){
NODE_MODULES='./node_modules/.bin';
if [ -d $NODE_MODULES ]; then
PATH=$DEFAULT_PATH:$NODE_MODULES;
else
PATH=$DEFAULT_PATH;
fi
}
cd () {
builtin cd "$#";
add_local_node_modules_to_path;
}
add_local_node_modules_to_path;
note: this script makes aliase of cd command, and after each call of cd it checks node_modules/.bin and add it to your $PATH.
note2: you can change the third line to NODE_MODULES=$(npm bin);. But that would make cd command too slow.
For Windows use this:
/* cmd into "node_modules" folder */
"%CD%\.bin\grunt" --version
I encountered the same problem and I don't particularly like using aliases (as regular's suggested), and if you don't like them too then here's another workaround that I use, you first have to create a tiny executable bash script, say setenv.sh:
#!/bin/sh
# Add your local node_modules bin to the path
export PATH="$(npm bin):$PATH"
# execute the rest of the command
exec "$#"
and then you can then use any executables in your local /bin using this command:
./setenv.sh <command>
./setenv.sh 6to5-node server.js
./setenv.sh grunt
If you're using scripts in package.json then:
...,
scripts: {
'start': './setenv.sh <command>'
}
I'd love to know if this is an insecure/bad idea, but after thinking about it a bit I don't see an issue here:
Modifying Linus's insecure solution to add it to the end, using npm bin to find the directory, and making the script only call npm bin when a package.json is present in a parent (for speed), this is what I came up with for zsh:
find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
precmd() {
if [ "$(find-up package.json)" != "" ]; then
new_bin=$(npm bin)
if [ "$NODE_MODULES_PATH" != "$new_bin" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}:$new_bin
export NODE_MODULES_PATH=$new_bin
fi
else
if [ "$NODE_MODULES_PATH" != "" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}
export NODE_MODULES_PATH=""
fi
fi
}
For bash, instead of using the precmd hook, you can use the $PROMPT_COMMAND variable (I haven't tested this but you get the idea):
__add-node-to-path() {
if [ "$(find-up package.json)" != "" ]; then
new_bin=$(npm bin)
if [ "$NODE_MODULES_PATH" != "$new_bin" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}:$new_bin
export NODE_MODULES_PATH=$new_bin
fi
else
if [ "$NODE_MODULES_PATH" != "" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}
export NODE_MODULES_PATH=""
fi
fi
}
export PROMPT_COMMAND="__add-node-to-path"
I am a Windows user and this is what worked for me:
// First set some variable - i.e. replace is with "xo"
D:\project\root> set xo="./node_modules/.bin/"
// Next, work with it
D:\project\root> %xo%/bower install
Good Luck.
In case you are using fish shell and do not want to add to $path for security reason. We can add the below function to run local node executables.
### run executables in node_module/.bin directory
function n
set -l npmbin (npm bin)
set -l argvCount (count $argv)
switch $argvCount
case 0
echo please specify the local node executable as 1st argument
case 1
# for one argument, we can eval directly
eval $npmbin/$argv
case '*'
set --local executable $argv[1]
# for 2 or more arguments we cannot append directly after the $npmbin/ since the fish will apply each array element after the the start string: $npmbin/arg1 $npmbin/arg2...
# This is just how fish interoperate array.
set --erase argv[1]
eval $npmbin/$executable $argv
end
end
Now you can run thing like:
n coffee
or more arguments like:
n browser-sync --version
Note, if you are bash user, then #Bob9630 answers is the way to go by leveraging bash's $#, which is not available in fishshell.
I propose a new solution I have developed (05/2021)
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
Include coffee-script in package.json with the specific version required in each project, typically like this:
"dependencies":{
"coffee-script": ">= 1.2.0"
Then run npm install to install dependencies in each project. This will install the specified version of coffee-script which will be accessible locally to each project.

Setting environment variables package.json in Windows 10

UPDATE: As it is explained in the question, this is not a duplicate because I have already tried adding the set keyword before the environment variable and that did not solve the problem.
I am in the process of learning node and typing examples from a book. The first examples deal with showing how the "http" module works and how to create a server to listen to requests. At some point the book asks to add the following line to the scripts section of the package.json file:
"server": "SERVERPORT=3002 node ./fiboserver"
When I try to run the example with npm run server I get the following error message:
'SERVERPORT' is not recognized as an internal or external command
I haven't been able to find any answer on the internet, at most I found that I could try:
"server": "set SERVERPORT=3002 node ./fiboserver"
But that doesn't help either, the only difference is that instead of the error message I get the command prompt again so apparently the server is never run.
I believe the author used a Linux machine, I am using a Windows 10 laptop.
I am really committed to learn Node and my line of work is on Windows environments. I believe that setting environment variables on package.json is important so I could really use some help in figuring this out.
Thank you.
Make it cross-platform by using cross-env:
"server": "cross-env SERVERPORT=3002 node ./fiboserver"
On Windows you have to separate the command of setting a variable from the one which runs the server with the && operator.
That being said, you have to do something like this:
"server": "set SERVERPORT=3002 && node ./fiboserver"
I've gone through the same problem and used one of the following methods.
Method 1
If I run (without using the npm wrapper script)
HOST=0.0.0.0 PORT=8000 ./node_modules/.bin/react-scripts start
it works fine. As Quentin says,
Must be something to do with how npm shells out then
To fix it, I've gone to package.json and changed the "start" script to
"start": "./node_modules/.bin/react-scripts start",
Then npm start works fine.
Method 2
Use the cross-env package.
For that install it using the following command
npm i cross-env
then go to package.json and change it to
"start": "cross-env ./node_modules/.bin/react-scripts start",
And then running npm start will also work fine:
You can set bash as package.json scripts runner and it's will work in windows and linux.
Just set it once:
for yarn yarn config set script-shell /bin/bash
for npm npm config set script-shell /bin/bash
Or "C:\\Program Files\\git\\bin\\bash.exe" instead /bin/bash
It's will allow you to run npm script cross-platform:
"server": "SERVERPORT=3002 node ./fiboserver"

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.

How do you install and run Mocha, the Node.js testing module? Getting "mocha: command not found" after install

I'm having trouble getting Mocha to work as expected, and I'd love to say as documented, but there (appears) to not be much documentation on actually getting the thing running.
I've installed it using npm (both globally and locally), and each time when I run it I get:
$ mocha
mocha: command not found
Ok, so I figured it's not in my PATH, so I tried running it directly,
$ ./node_modules/mocha/bin/mocha
execvp(): No such file or directory
Finally, I tried hitting the other bin file, and got,
$ ./node_modules/mocha/bin/_mocha
path.existsSync is deprecated. It is now called `fs.existsSync`.
.
✔ 1 tests complete (1ms)
How can I just execute my tests with a single command? Vows seems to let you, but I've heard Mocha is the better choice, I just can't seem to get it working correctly.
And any thoughts on the error I got above in my third attempt?
Edit:
I'm running,
Ubuntu 11.10 64-bit
Node.js 0.7.5
npm 1.1.8
mocha 0.14.1
should 0.6.0
since npm 5.2.0, there's a new command "npx" included with npm that makes this much simpler, if you run:
npx mocha <args>
Note: the optional args are forwarded to the command being executed (mocha in this case)
this will automatically pick the executable "mocha" command from your locally installed mocha (always add it as a dev dependency to ensure the correct one is always used by you and everyone else).
Be careful though that if you didn't install mocha, this command will automatically fetch and use latest version, which is great for some tools (like scaffolders for example), but might not be the most recommendable for certain dependencies where you might want to pin to a specific version.
You can read more on npx here
Now, if instead of invoking mocha directly, you want to define a custom npm script, an alias that might invoke other npm binaries...
you don't want your library tests to fail depending on the machine setup (mocha as global, global mocha version, etc), the way to use the local mocha that works cross-platform is:
node node_modules/.bin/mocha
npm puts aliases to all the binaries in your dependencies on that special folder.
Finally, npm will add node_modules/.bin to the PATH automatically when running an npm script, so in your package.json you can do just:
"scripts": {
"test": "mocha"
}
and invoke it with
npm test
After further reading, and confirmation from Linus G Thiel above, I found I simply had to,
Downgrade to Node.js 0.6.12
And either,
Install Mocha as global
Add ./node_modules/.bin to my PATH
To run Mocha with mocha command from your terminal you need to install mocha globally on this machine:
npm install --global mocha
Then cd to your projectFolder/test and run mocha yourTestFileName.js
If you want to make mocha available inside your package.json as a development dependency:
npm install --save-dev mocha
Then add mocha to your scripts inside package.json.
"scripts": {
"test": "mocha"
},
Then run npm test inside your terminal.
While installing the node modules for mocha I had tried the below commands
npm install
npm install mocha
npm install --save-dev mocha
npm install mocha -g # to install it globally also
and on running or executing the mocha test I was trying
mocha test
npm run test
mocha test test\index.test.js
npm test
but I was getting the below error as:
'Mocha' is not recognized as internal or external command
So , after trying everything it came out to be just set the path to environment variables under the System Variables as:
C:\Program Files\nodejs\
and it worked :)
For windows :
Package.json
"scripts": {
"start": "nodemon app.js",
"test": "mocha"
},
then run the command
npm run test
Late answer but I think will work.
Install mocha globally
npm install --global mocha
If you have already installed mocha then set the path to bin

How to use executables from a package installed locally in node_modules?

How do I use a local version of a module in node.js. For example, in my app, I installed coffee-script:
npm install coffee-script
This installs it in ./node_modules and the coffee command is in ./node_modules/.bin/coffee. Is there a way to run this command when I'm in my project's main folder? I guess I'm looking for something similar to bundle exec in bundler. Basically, I'd like to specify a version of coffee-script that everyone involved with the project should use.
I know I can add the -g flag to install it globally so coffee works fine anywhere, but what if I wanted to have different versions of coffee per project?
UPDATE: As Seyeong Jeong points out in their answer below, since npm 5.2.0 you can use npx [command], which is more convenient.
OLD ANSWER for versions before 5.2.0:
The problem with putting
./node_modules/.bin
into your PATH is that it only works when your current working directory is the root of your project directory structure (i.e. the location of node_modules)
Independent of what your working directory is, you can get the path of locally installed binaries with
npm bin
To execute a locally installed coffee binary independent of where you are in the project directory hierarchy you can use this bash construct
PATH=$(npm bin):$PATH coffee
I aliased this to npm-exec
alias npm-exec='PATH=$(npm bin):$PATH'
So, now I can
npm-exec coffee
to run the correct copy of coffee no matter of where I am
$ pwd
/Users/regular/project1
$ npm-exec which coffee
/Users/regular/project1/node_modules/.bin/coffee
$ cd lib/
$ npm-exec which coffee
/Users/regular/project1/node_modules/.bin/coffee
$ cd ~/project2
$ npm-exec which coffee
/Users/regular/project2/node_modules/.bin/coffee
You don't have to manipulate $PATH anymore!
From npm#5.2.0, npm ships with npx package which lets you run commands from a local node_modules/.bin or from a central cache.
Simply run:
$ npx [options] <command>[#version] [command-arg]...
By default, npx will check whether <command> exists in $PATH, or in the local project binaries, and execute that.
Calling npx <command> when <command> isn't already in your $PATH will automatically install a package with that name from the NPM registry for you, and invoke it. When it's done, the installed package won’t be anywhere in your globals, so you won’t have to worry about pollution in the long-term. You can prevent this behaviour by providing --no-install option.
For npm < 5.2.0, you can install npx package manually by running the following command:
$ npm install -g npx
Use the npm bin command to get the node modules /bin directory of your project
$ $(npm bin)/<binary-name> [args]
e.g.
$ $(npm bin)/bower install
Use npm run[-script] <script name>
After using npm to install the bin package to your local ./node_modules directory, modify package.json to add <script name> like this:
$ npm install --save learnyounode
$ edit packages.json
>>> in packages.json
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"learnyounode": "learnyounode"
},
...
$ npm run learnyounode
It would be nice if npm install had a --add-script option or something or if npm run would work without adding to the scripts block.
update: If you're on the recent npm (version >5.2)
You can use:
npx <command>
npx looks for command in .bin directory of your node_modules
old answer:
For Windows
Store the following in a file called npm-exec.bat and add it to your %PATH%
#echo off
set cmd="npm bin"
FOR /F "tokens=*" %%i IN (' %cmd% ') DO SET modules=%%i
"%modules%"\%*
Usage
Then you can use it like
npm-exec <command> <arg0> <arg1> ...
For example
To execute wdio installed in local node_modules directory, do:
npm-exec wdio wdio.conf.js
i.e. it will run .\node_modules\.bin\wdio wdio.conf.js
Update: I no longer recommend this method, both for the mentioned security reasons and not the least the newer npm bin command. Original answer below:
As you have found out, any locally installed binaries are in ./node_modules/.bin. In order to always run binaries in this directory rather than globally available binaries, if present, I suggest you put ./node_modules/.bin first in your path:
export PATH="./node_modules/.bin:$PATH"
If you put this in your ~/.profile, coffee will always be ./node_modules/.bin/coffee if available, otherwise /usr/local/bin/coffee (or whatever prefix you are installing node modules under).
Use npm-run.
From the readme:
npm-run
Find & run local executables from node_modules
Any executable available to an npm lifecycle script is available to npm-run.
Usage
$ npm install mocha # mocha installed in ./node_modules
$ npm-run mocha test/* # uses locally installed mocha executable
Installation
$ npm install -g npm-run
TL;DR: Use npm exec with npm#>=7.
The npx command which was mentioned in other answers has been completely rewritten in npm#7 which ships by default with node#15 and can be installed on node#>=10. The implementation is now equal to the newly introduced npm exec command, which is similar but not equal to the previous npx command implementation.
One difference is e.g. that it always interactively asks if a dependency should be downloaded when it is not already installed (can also be overwritten with the params --yes or --no).
Here is an example for npm exec. The double dashes (--) separates the npm exec params from the actual command params:
npm exec --no -- jest --coverage
See also the updated, official documentation to npm exec.
If you want to keep npm, then npx should do what you need.
If switching to yarn (a npm replacement by facebook) is an option for you, then you can call:
yarn yourCmd
scripts inside the package.json will take precedence, if none is found it will look inside the ./node_modules/.bin/ folder.
It also outputs what it ran:
$ yarn tsc
yarn tsc v0.27.5
$ "/home/philipp/rate-pipeline/node_modules/.bin/tsc"
So you don't have to setup scripts for each command in your package.json.
If you had a script defined at .scripts inside your package.json:
"tsc": "tsc" // each command defined in the scripts will be executed from `./node_modules/.bin/` first
yarn tsc would be equivalent to yarn run tsc or npm run tsc:
yarn tsc
yarn tsc v0.27.5
$ tsc
The PATH solution has the issue that if $(npm bin) is placed in your .profile/.bashrc/etc it is evaluated once and is forever set to whichever directory the path was first evaluated in. If instead you modify the current path then every time you run the script your path will grow.
To get around these issues, I create a function and used that. It doesn't modify your environment and is simple to use:
function npm-exec {
$(npm bin)/$#
}
This can then be used like this without making any changes to your environment:
npm-exec r.js <args>
I prefer to not rely on shell aliases or another package.
Adding a simple line to scripts section of your package.json, you can run local npm commands like
npm run webpack
package.json
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"webpack": "webpack"
},
"devDependencies": {
"webpack": "^4.1.1",
"webpack-cli": "^2.0.11"
}
}
If you want your PATH variable to correctly update based on your current working directory, add this to the end of your .bashrc-equivalent (or after anything that defines PATH):
__OLD_PATH=$PATH
function updatePATHForNPM() {
export PATH=$(npm bin):$__OLD_PATH
}
function node-mode() {
PROMPT_COMMAND=updatePATHForNPM
}
function node-mode-off() {
unset PROMPT_COMMAND
PATH=$__OLD_PATH
}
# Uncomment to enable node-mode by default:
# node-mode
This may add a short delay every time the bash prompt gets rendered (depending on the size of your project, most likely), so it's disabled by default.
You can enable and disable it within your terminal by running node-mode and node-mode-off, respectively.
I've always used the same approach as #guneysus to solve this problem, which is creating a script in the package.json file and use it running npm run script-name.
However, in the recent months I've been using npx and I love it.
For example, I downloaded an Angular project and I didn't want to install the Angular CLI globally. So, with npx installed, instead of using the global angular cli command (if I had installed it) like this:
ng serve
I can do this from the console:
npx ng serve
Here's an article I wrote about NPX and that goes deeper into it.
Same #regular 's accepted solution, but Fish shell flavour
if not contains (npm bin) $PATH
set PATH (npm bin) $PATH
end
zxc is like "bundle exec" for nodejs. It is similar to using PATH=$(npm bin):$PATH:
$ npm install -g zxc
$ npm install gulp
$ zxc which gulp
/home/nathan/code/project1/node_modules/.bin/gulp
You can also use direnv and change the $PATH variable only in your working folder.
$ cat .envrc
> export PATH=$(npm bin):$PATH
Add this script to your .bashrc. Then you can call coffee or anyhting locally. This is handy for your laptop, but don't use it on your server.
DEFAULT_PATH=$PATH;
add_local_node_modules_to_path(){
NODE_MODULES='./node_modules/.bin';
if [ -d $NODE_MODULES ]; then
PATH=$DEFAULT_PATH:$NODE_MODULES;
else
PATH=$DEFAULT_PATH;
fi
}
cd () {
builtin cd "$#";
add_local_node_modules_to_path;
}
add_local_node_modules_to_path;
note: this script makes aliase of cd command, and after each call of cd it checks node_modules/.bin and add it to your $PATH.
note2: you can change the third line to NODE_MODULES=$(npm bin);. But that would make cd command too slow.
For Windows use this:
/* cmd into "node_modules" folder */
"%CD%\.bin\grunt" --version
I encountered the same problem and I don't particularly like using aliases (as regular's suggested), and if you don't like them too then here's another workaround that I use, you first have to create a tiny executable bash script, say setenv.sh:
#!/bin/sh
# Add your local node_modules bin to the path
export PATH="$(npm bin):$PATH"
# execute the rest of the command
exec "$#"
and then you can then use any executables in your local /bin using this command:
./setenv.sh <command>
./setenv.sh 6to5-node server.js
./setenv.sh grunt
If you're using scripts in package.json then:
...,
scripts: {
'start': './setenv.sh <command>'
}
I'd love to know if this is an insecure/bad idea, but after thinking about it a bit I don't see an issue here:
Modifying Linus's insecure solution to add it to the end, using npm bin to find the directory, and making the script only call npm bin when a package.json is present in a parent (for speed), this is what I came up with for zsh:
find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
precmd() {
if [ "$(find-up package.json)" != "" ]; then
new_bin=$(npm bin)
if [ "$NODE_MODULES_PATH" != "$new_bin" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}:$new_bin
export NODE_MODULES_PATH=$new_bin
fi
else
if [ "$NODE_MODULES_PATH" != "" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}
export NODE_MODULES_PATH=""
fi
fi
}
For bash, instead of using the precmd hook, you can use the $PROMPT_COMMAND variable (I haven't tested this but you get the idea):
__add-node-to-path() {
if [ "$(find-up package.json)" != "" ]; then
new_bin=$(npm bin)
if [ "$NODE_MODULES_PATH" != "$new_bin" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}:$new_bin
export NODE_MODULES_PATH=$new_bin
fi
else
if [ "$NODE_MODULES_PATH" != "" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}
export NODE_MODULES_PATH=""
fi
fi
}
export PROMPT_COMMAND="__add-node-to-path"
I am a Windows user and this is what worked for me:
// First set some variable - i.e. replace is with "xo"
D:\project\root> set xo="./node_modules/.bin/"
// Next, work with it
D:\project\root> %xo%/bower install
Good Luck.
In case you are using fish shell and do not want to add to $path for security reason. We can add the below function to run local node executables.
### run executables in node_module/.bin directory
function n
set -l npmbin (npm bin)
set -l argvCount (count $argv)
switch $argvCount
case 0
echo please specify the local node executable as 1st argument
case 1
# for one argument, we can eval directly
eval $npmbin/$argv
case '*'
set --local executable $argv[1]
# for 2 or more arguments we cannot append directly after the $npmbin/ since the fish will apply each array element after the the start string: $npmbin/arg1 $npmbin/arg2...
# This is just how fish interoperate array.
set --erase argv[1]
eval $npmbin/$executable $argv
end
end
Now you can run thing like:
n coffee
or more arguments like:
n browser-sync --version
Note, if you are bash user, then #Bob9630 answers is the way to go by leveraging bash's $#, which is not available in fishshell.
I propose a new solution I have developed (05/2021)
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
Include coffee-script in package.json with the specific version required in each project, typically like this:
"dependencies":{
"coffee-script": ">= 1.2.0"
Then run npm install to install dependencies in each project. This will install the specified version of coffee-script which will be accessible locally to each project.

Resources