Reputation: 56948
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?
Upvotes: 751
Views: 501471
Reputation: 23542
To get the globale bin folder and add it to your path this worked for me.
npmbin=$(npm -g root)/../../bin
export PATH="$PATH:$npmbin"
Since npm bin
is no more, you can use npm root
to get the bin folder.
Upvotes: 0
Reputation: 8011
As Seyeong Jeong points out in their answer below, since npm 5.2.0 you can use npx
, which is more convenient:
npx [command]
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
Upvotes: 753
Reputation: 29987
$(npm bin)/package
The correct answer used to be $(npm bin)/package-name
until npm removed it in version 9 (around August 2022).
npm exec -- package
The new solution that Node recommends is npx
or npm exec
which are slightly different. I tried following that doc, but the only format that worked perfectly for me was npm exec -- package-name -x ...
which would properly convey the parameters to the package-name
.
Weirdly, using npx
(with or without --
) was prompting me to install run
npm package, which is an 8-year-old irrelevant package!
Upgrading from Node 16 to Node 20, in my package.json
I needed to replace
{
"build-prod": "npm run clean && $(npm bin)/npm-run-all -s _build:*"
}
with
{
"build-prod": "npm run clean && npm exec -- npm-run-all -s _build:*"
}
Upvotes: 4
Reputation: 20191
No longer works - removed: https://github.com/npm/cli/pull/6289
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
Upvotes: 124
Reputation: 7
I propose a new solution I have developed (05/2021)
You can use lpx https://www.npmjs.com/package/lpx to
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
Upvotes: -1
Reputation: 16069
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
.
Upvotes: 31
Reputation: 19039
You can use:
npx <command>
npx
looks for command in .bin
directory of your node_modules
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%"\%*
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
Upvotes: 66
Reputation: 6502
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"
}
}
Upvotes: 12
Reputation: 2915
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.
Upvotes: 6
Reputation: 7414
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.
Upvotes: 86
Reputation: 11028
You don't have to manipulate $PATH
anymore!
From [email protected], 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
Upvotes: 632
Reputation: 66500
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
Upvotes: 25
Reputation: 10194
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
.
Upvotes: 0
Reputation: 781
For Windows use this:
/* cmd into "node_modules" folder */
"%CD%\.bin\grunt" --version
Upvotes: 1
Reputation: 1308
Same @regular 's accepted solution, but Fish shell flavour
if not contains (npm bin) $PATH
set PATH (npm bin) $PATH
end
Upvotes: 2
Reputation: 23737
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.
Upvotes: 0
Reputation: 1091
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.
Upvotes: 1
Reputation: 1589
You can also use direnv and change the $PATH variable only in your working folder.
$ cat .envrc
> export PATH=$(npm bin):$PATH
Upvotes: 1
Reputation: 2961
Use npm-run
.
From the readme:
Any executable available to an npm lifecycle script is available to npm-run
.
$ npm install mocha # mocha installed in ./node_modules
$ npm-run mocha test/* # uses locally installed mocha executable
$ npm install -g npm-run
Upvotes: 43
Reputation: 2160
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"
Upvotes: 0
Reputation: 5329
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
Upvotes: 1
Reputation: 39223
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).
Upvotes: 46
Reputation: 953
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>
Upvotes: 23
Reputation: 5791
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>'
}
Upvotes: 0
Reputation: 9996
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.
Upvotes: 6
Reputation: 6712
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.
Upvotes: -11