743

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?

4
  • 20
    A lot of instructions I read say things like npm install niftycommand and then niftycommand. But this will never work unless you have ./node_modules/.bin in your path, will it? Commented Sep 20, 2016 at 5:03
  • 3
    There is a very good writeup here: firstdoit.com/… — Basically it recommends you to put your coffee command into the npm scripts section, like "build": "coffee -co target/directory source/directoy", so you can run npm run build` from the terminal afterwards.
    – Benny Code
    Commented Jan 22, 2017 at 21:27
  • @BennyNeugebauer indeed, that's what I've been doing lately instead of messing with PATH Commented Feb 2, 2017 at 17:36
  • 25
    Use npx which comes with npm 5.2.0 medium.com/@maybekatz/…
    – onmyway133
    Commented Mar 14, 2018 at 13:18

26 Answers 26

744

For npm ⩾5.2.0 (update)

As Seyeong Jeong points out in their answer below, since npm 5.2.0 you can use npx, which is more convenient:

npx [command]

For older versions npm <5.2.0 (old answer)

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
18
  • 23
    you can even go one step further and alias coffee="npm-exec coffee"
    – regular
    Commented Mar 18, 2013 at 6:12
  • 7
    The output changes when you cd into another project. It does not change when you cd within a project. npm bin searches the chain of 'ancestor directories' to the cwd for a node_modules directory. This is exactly the desired behavior if you specifically want to use the binaries of modules listed in the project's package.json.
    – regular
    Commented Oct 3, 2013 at 9:12
  • 15
    oh gosh! do I really have to do something like that in order to have my local modules working? it's quite impracticable to explain it to a team! there's nothing a little bit more straightforward?
    – Alexian
    Commented Apr 24, 2015 at 17:12
  • 18
    You can always use npm scripts since they always search the local binaries first. You can set up aliases to each of your binaries there or just use generic names like "build".
    – Joe Zim
    Commented Sep 22, 2015 at 1:47
  • 7
    This answer is outdated. Either use npx or yarn.
    – k0pernikus
    Commented Jan 26, 2018 at 13:38
631

Nice example

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
7
  • 2
    I dont like installing 3rd party global npm packages while npm and package.json provides nearly same functionality.
    – guneysus
    Commented Mar 28, 2018 at 6:27
  • If "Path must be a string. Received undefined" message appears, here is a fix: github.com/zkat/npx/issues/144#issuecomment-391031816 Commented Aug 2, 2018 at 15:56
  • 9
    This answer is good. But I just want to say npx is lame. It should have been npm run or npm exec or something. Commented Oct 25, 2018 at 14:15
  • 1
    Strangely this answer includes one (and only one?) paragraph verbatim -- ha, the image used in this answer is also a dupe -- from this blog post. Can't tell who was first, though I have my guesses. ;^D Either way, that blog provides a lot of context.
    – ruffin
    Commented May 25, 2021 at 13:58
  • 1
    @WilliamEntriken It is in fact npm exec; npx is just a shortcut to that. A safe version of this command when you want to run some command in the current project is npm exec --no packagename -- [options] (or npx --no packagename -- [options]). The --no will prevent it from magically installing packagename if it isn't already installed, thus avoiding security risks from typo-squatting etc.
    – jbg
    Commented Mar 25, 2023 at 3:56
123

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
4
  • 5
    I like this simple and generic solution. Makes an alias seem unnecessary. Commented May 31, 2015 at 22:13
  • Seems to be the next best solution that is elegant and more secure than having to do export PATH="./node_modules/.bin:$PATH"
    – jontsai
    Commented Sep 9, 2015 at 23:16
  • 1
    @inf3rno the command is $(npm bin)/jasmine, not node $(npm bin)/jasmine (you probably figured it out but clarifying for others).
    – jassa
    Commented Aug 19, 2016 at 19:32
  • 6
    Not a bad solution, but it doesn't run on a standard windows command line with $. Putting it in the package.json scripts section is a better approach I feel, since it's more compatible. Commented Sep 30, 2016 at 22:26
86

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.

6
  • 5
    I found this approach to be more uniform when dealing with multiple developers on a project - it avoids the need to configure anything locally... you just npm install then you have access to your dev dependencies. The only minor down side is you need to npm run eslint (or whatever). You can create a script called "start" that runs gulp so that you only need to type npm start to start your dev server. Pretty cool stuff and no bash goodness, so your windows friends still like you. :)
    – jpoveda
    Commented Sep 21, 2015 at 21:28
  • 1
    adding an alias to put $(npm bin) on your path is clever, but the fact that this will work for people without local config wins my heart Commented Mar 25, 2016 at 12:37
  • 12
    this needs more upvotes! Pass args to your scripts after -- like: npm run learnyounode -- --normal-switches --watch -d *.js
    – ptim
    Commented Jun 17, 2016 at 8:53
  • I also find this the best solution. There is an in-depth explanation here: lostechies.com/derickbailey/2012/04/24/…
    – adampasz
    Commented Jul 24, 2016 at 4:41
  • 1
    This is what I usually go for, but for some reasons, on an Ubuntu device, npm run ts-node isn't working for me. I'll just have to ressort to npx.
    – Clockwork
    Commented Jul 3, 2019 at 9:09
65

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

3
  • This doesn't work while passing more than 1 argument. E.g. npm-exec gulp <some_task>
    – OK999
    Commented Oct 4, 2016 at 15:55
  • @OK9999 I'm sure some minor modification will allow passing arguments (because when you pass it here, it's coming in quoted in ""); What I suggest is copy paste the gulp file from bin to your project root (some modifications needed of the file, but it will just work without writing new code etc) Commented Oct 11, 2016 at 18:45
  • Yes, i ended up doing so. The node_modules folder have to be in the folder where the gulpfile exist
    – OK999
    Commented Oct 11, 2016 at 18:53
45

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).

5
  • 1
    that's probably the best solution. I also created a bash script called "watch" in my project: ./node_modules/.bin/coffee --output lib/ --compile --bare --watch src Commented Mar 13, 2012 at 18:27
  • 75
    Danger, Will Robinson! Use of relative paths in your $PATH opens up a security hole the size of a planet, especially if you put them right up front as the first item. If the directory you're in is writable by everyone (say somewhere in /tmp), any process or user can hijack your session by putting malicious versions of ordinary commands (like ls, cp, etc.) there. These may spawn 'invisible' sub-shells capturing your passwords, etcetera.
    – ack
    Commented Feb 28, 2014 at 20:40
  • will only work in the root and no other places. the alias npm-exec='PATH=$(npm bin):$PATH' is slicker.
    – oligofren
    Commented May 5, 2015 at 16:21
  • 1
    How bad is this if you don't put it as the first thing in your PATH, but the last (using the $(npm bin) form)? so they can't overwrite your existing stuff, and you would have been trusting the executables in the npm bin directory already regardless of the PATH var; would the threat model be that a) someone malicious gets access to your file system, b) they add executables with names that are close to those system tools, and c) you mistype? Trying to understand scenarios that make this bad, given that you're already trusting foreign executables when using npm-installed programs.
    – osdiab
    Commented Jul 15, 2016 at 1:24
  • You can do shell tricks with an alias and you can path manually and this "works" but it is not quite ideal. Commented Dec 18, 2017 at 19:42
43

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
1
29

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.

1
  • "The implementation is now [nearly] equal to the newly introduced npm exec command" There are some weird backwards compat things in there that make your link to the docs potentially very useful.
    – ruffin
    Commented Aug 19, 2022 at 18:35
25

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
23

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>
1
  • 3
    I like this! I simply named my function n
    – jontsai
    Commented Sep 9, 2015 at 23:20
12

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"
  }
}
1
  • 1
    This gets ugly when you want to pass arguments to webpack. For example npm run webpack --is thos --possible
    – Mustafa
    Commented Nov 16, 2022 at 2:22
6

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.

6

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.

4

Answer 2023:

Old Solution: $(npm bin)/package

The correct answer used to be $(npm bin)/package-name until npm removed it in version 9 (around August 2022).

New Solution 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!

TL;DR

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:*"
}
2

Same @regular 's accepted solution, but Fish shell flavour

if not contains (npm bin) $PATH
    set PATH (npm bin) $PATH
end
1

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
1

You can also use direnv and change the $PATH variable only in your working folder.

$ cat .envrc
> export PATH=$(npm bin):$PATH
0
1

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.

2
  • 1
    Use $(npm bin) instead of hardcoding ./node_modules/.bin.
    – bfontaine
    Commented Jan 25, 2017 at 17:27
  • Hmm, $(npm bin) seems too slow to use with each cd command. I've restore the code and added a note for it. Commented Jan 26, 2017 at 2:28
1

For Windows use this:

/* cmd into "node_modules" folder */
"%CD%\.bin\grunt" --version
0

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>'
}
1
  • 2
    this setenv script isn't necessary for package.json scripts. npm already prepends the local node_modules/.bin directory to path for you when executing npm run {scripts}.
    – jasonkarns
    Commented Feb 6, 2015 at 19:39
0

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"
1
  • Adding npm bin to end of $PATH may not execute what user expects: basically another executable but more probably a globally installed package with another version !
    – LoganMzz
    Commented Nov 8, 2017 at 13:46
0

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.

0

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.

0

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.

-1

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

2
  • It's suggested to mention if any linked material is created by you.
    – m4heshd
    Commented May 22, 2021 at 13:49
  • I just tried lpx, it doesn't work due to the file using the wrong (Windows) line-endings on the shebang line. Commented Dec 14, 2021 at 20:48
-11

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.

4
  • yeah, I got that far as I stated in my question. how do I specifically call the one in my project besides ./node_modules/.bin/coffee? Commented Mar 13, 2012 at 9:47
  • If you've run npm install with the package.json in your project's main folder, you should have a ./node_modules/.bin/coffee folder in this folder. Using ./node_modules/coffee-script/bin/coffee will run the local version of coffee while just running coffee will run the global installation. If you have another version of coffee installed in another path within this project folder, you can access it using ./path/to/this/installation/coffee.
    – patalmypal
    Commented Mar 13, 2012 at 10:42
  • This did not work for me. I am trying to use "svgo", and it only works when installed globally. I have tried npm install svgo as well as npm install with package.json. Both methods installed "successfully", but the "svgo" command is still not available. Commented Apr 5, 2013 at 4:37
  • 1
    Grunt uses this in a clever way, and IMHO so should other packages. First you install the grunt-cli package globally, then in your project directory install any (modified) version of the grunt package, then when you run grunt, it will use this local version.
    – ack
    Commented Feb 28, 2014 at 21:00

Not the answer you're looking for? Browse other questions tagged or ask your own question.