CircleCI not caching my globally installed node module? - node.js

I am trying to cache a command line tool needed for my build process. The tool is made out of NodeJS. The build succeeds, but I need it to run faster.
The relevant parts of my circle.yml look like this :
dependencies:
post:
- npm -g list
- if [ $(npm -g list | grep -c starrynight) -lt 1 ]; then npm install -g starrynight; else echo "StarryNight seems to be cached"; fi
test:
override:
- npm -g list
- starrynight run-tests --framework nightwatch
The second npm -g list shows starrynight available for use, but the first one shows that it is not being cached.
echo $(npm prefix -g)
. . . gets me . . .
/home/ubuntu/nvm/v0.10.33
. . . so I am assuming CircleCI doesn't cache anything installed globally into nvm.
Nothing I have tried gets me my message, "StarryNight seems to be cached".
How can I cache starrynight?

Ok, I figured this out. Thanks to Hirokuni Kim of CircleCI for pointing me in the right direction.
The relevant bits of the new circle.yml looks like this :
machine:
node:
version: 0.10.33
dependencies:
cache_directories:
- ~/nvm/v0.10.33/lib/node_modules/starrynight
- ~/nvm/v0.10.33/bin/starrynight
pre:
- if [ ! -e ~/nvm/v0.10.33/bin/starrynight ]; then npm install -g starrynight; else echo "Starrynight seems to be cached"; fi;
Hirokuni suggested caching ~/nvm but cache retrieval took as long as the build, since it restores every available version of nodejs.
I had tried previously to cache just ~/nvm/v0.10.33/lib/node_modules/starrynight on its own, without realizing that the sister 'directory' bin/starrynight is actually an essential symlink to the entry point of the module.
My working assumption is that NodeJS modules run from the command line through a series of symbolic references, probably as follows. . .
npm install -g starrynight creates two new artifacts:
an environment alias for npm named starrynight
a symlink in the ${prefix}/bin directory, which points to the entry point file, starrynight.js specified with the bin key in package.json.
When the user types starrynight as a CLI command the shell interprets it as an alias for npm and executes it. npm examines $0, gets starrynight, and starts up nodejs with the symlink ${prefix}/bin/starrynight as the module to execute. That symlink refers to ~/nvm/v0.10.33/lib/node_modules/starrynight where the real action takes place.
In short, it is necessary to cache both ${prefix}/lib/node_modules/xxx and ${prefix}/bin/xxx

The path in the accepted answer doesn't seem to work anymore. I've checked on the build instance that now global npm packages are in
/opt/circleci/nodejs/<version>
so I've added to the circle.yml the following:
- "/opt/circleci/nodejs/v4.3.2/lib/node_modules"
- "/opt/circleci/nodejs/v4.3.2/bin"

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.

How to test an `npm publish` result, without actually publishing to NPM?

One common problem I have, is that sometimes my .npmignore file is too aggressive, and I ignore files that I actually will to include in the NPM tarball.
My question is - is there a way to test the results of NPM publish, without actually publishing to NPM?
I am thinking something like this. Assuming I have a local NPM package with package name "foo"
set -e;
local proj="bar";
local path_to_foo="."
mkdir -p "$HOME/.local.npm"
npm --tarball -o "$HOME/.local.npm" # made up command, but you get the idea
(
cd "$HOME/.temp_projects"
rm -rf "$proj"
mkdir "$proj"
cd "$proj"
npm init -f
npm install "$path_to_foo"
)
copy_test_stuff -o "$HOME/.temp_projects/bar"
cd "$HOME/.temp_projects/bar"
npm test
I don't think this will work. Because whatever we include in the NPM publish tarball, might not have enough to do the full test. But maybe if we copy all the test files (including fixtures, etc) when we do copy_test_stuff, it might work?
Simply run
npm publish --dry-run
or, with tarball generation in the current directory
npm pack
In npm 6 and up, these will display what files are going to be uploaded.
I'll elaborate my comment I posted earler, (thanks Alexander Mills).
I'm a verdaccio contributor, so, I closely follow whom are implementing and how to verdaccio. I'll describe couples or examples (e2e mostly) that I've found and might be interesting or as a valid answer.
create-react-app
By far, the most popular integration. Let me give you some context, they are using lerna and have multiple packages that need to test before to publish on main registry aka (npmjs). I'll quote here Dan Abramov explaining their reasons to use a custon registry.
The script is self-explanatory but let me highlight some parts.
+nohup npx verdaccio#2.7.2 &>$tmp_registry_log &
+# Wait for `verdaccio` to boot
+grep -q 'http address' <(tail -f $tmp_registry_log)
+
+# Set registry to local registry
+npm set registry http://localhost:4873
+yarn config set registry http://localhost:4873
+
+# Login so we can publish packages
+npx npm-cli-login#0.0.10 -u user -p password -e user#example.com -r http://localhost:4873 --quotes
# Test local start command
yarn start --smoke-test
+./tasks/release.sh --yes --force-publish=* --skip-git --cd-version=prerelease --exact --npm-tag=latest
As you see, they are running verdaccio and instead a custom config file they have decided to use npm-cli-login and then they run the tests against verdaccio. When all is ready, they publish on verdaccio. As last step, later in the same file, they fetch packages with their own app.
pnpm
They have created a project called pnpm-registry-mock which is an abstraction that allows them to run verdaccio before running the tests.
"pretest:e2e": "rimraf ../.tmp/ && rimraf node_modules/.bin/pnpm && pnpm-registry-mock prepare",
"test:e2e": "preview --skip-prepublishOnly && npm-run-all -p -r pnpm-registry-mock test:tap",
"test": "npm run lint && npm run tsc && npm run test:e2e",
Basically, using npm scripts they prepare verdaccio and run the test as last step. I cannot go too much into details, since I've only saw it shallowly. But I know what it does.
Mozilla Neutrino
This is work in progress, but, it's also interesting to mention here.
+if [ "$PROJECT" == "all" ]; then
+ yarn link:all;
+ yarn validate:eslintrc;
+ yarn lint;
+ yarn build;
+ yarn test;
+else
+ yarn verdaccio --config verdaccio.yml & sleep 10;
+ yarn config set registry "http://localhost:4873";
+ npm config set registry "http://localhost:4873";
+ .scripts/npm-adduser.js;
+ yarn lerna publish \
+ --force-publish=* \
+ --skip-git \
+ --skip-npm \
+ --registry http://localhost:4873/ \
+ --yes \
+ --repo-version $(node_modules/.bin/semver -i patch $(npm view neutrino version));
+ yarn lerna exec npm publish --registry http://localhost:4873/;
+ PROJECT="$PROJECT" TEST_RUNNER="$TEST_RUNNER" LINTER="$LINTER" yarn test:create-project;
+fi
Again, the same approach, project is being built and then verdaccio is being executed and they publish all packages.
Babel.js
I know Babel.js has been experimenting with a smoke-testing for Babel 6 and have plans to integrate a registry with Babel 7. I quote Henry Zhu early this year talking about babel-smoke-tests in the same thread of create-react-app.
The experiment is called babel-smoke-tests and babel-smoke-tests/scripts/test.sh is the key file for you.
Here I see the same pattern than other projects. They are launching verdaccio and then they do their stuff.
START=$(cd scripts; pwd)/section-start.sh
END=$(cd scripts; pwd)/section-end.sh
$START 'Setting up local npm registry' setup.npm.registry
node_modules/.bin/verdaccio -l localhost:4873 -c verdaccio.yml &
export NPM_CONFIG_REGISTRY=http://localhost:4873/
NPM_LOGIN=$(pwd)/scripts/npm-login.sh
$NPM_LOGIN
$END 'Done setting up local npm registry' setup.npm.registry
scripts/bootstrap.sh
export THEM=$(cd them; pwd)
if [[ $SPECIFIC_TEST ]]; then
scripts/tests/$SPECIFIC_TEST.sh
else
scripts/tests/jquery.sh
scripts/tests/react.sh
fi
Wrap up
First of all, I hope my small research give you new ideas how to address your issue. I think npm pack solve some issues, but mocking a registry using verdaccio which is quite light and straightforward to use might be a real option for you. Some big projects are being (or getting started) using it and they follow more or less the same approach. So, Why don't try? :)
https://www.verdaccio.org/
I had the exact same problem, so I created a package called package-preview. What package-preview does is:
packs your package (it is what npm does before publish)
installs your package in a temp location
links the package to your project's node_modules
This allows you to basically require the package as a dependency in your tests. So in tests of "awesome-pkg", intead of require('../lib') you write require('awesome-pkg')
I use this package in all the pnpm repos for several months and it works really well. I also posted an article about this package that explains all the different errors that it can catch: Never ever forget to install a dependency
Referring to npm docs:
[--dry-run] As of npm#6, does everything publish would do except
actually publishing to the registry. Reports the details of what would
have been published.
Similar to --dry-run see npm pack, which figures out the files to be included and packs them into a tarball to be uploaded to the registry.
https://docs.npmjs.com/cli/v6/commands/npm-publish#description
I see too many complicated answers, but according to documentation, you just need to install your local package globally (because it will be installed on different directory)
Go to your module root directory and do
npm install . -g

NPM : how to source ./node_modules/.bin folder?

I have a problem on npm installation
I have created a project say project A
cd ~/projectA
npm install sails
but sails command is not found after installation.
I know that it is successfully install in ~/projectA/node_modules directory.
but the executable cannot be sourced. And i know it is installed ~/projectA/node_modules/.bin
How can I source the .bin automatically whenever I enter into this projectA folder?
Did I did something wrong?
I can give you an inelegant solution, which is working for me. I just exported my path in my .bashrc file.
export PATH="$PATH:./node_modules/.bin"
Edit: It's been a while, but I have changed my strategy to use npm scripts instead. In the above case, setup package.json as follows:
"scripts": {
"sails": "sails"
}
Which you can then run with
npm run sails
or with arguments
npm run sails -- <args>
You should use the npm bin command to get an absolute path to your current node bin directory.
For example:
➤ lessc
bash: lessc: command not found
➤ npm bin
/home/brice/[...]/node_modules/.bin
➤ export PATH=$(npm bin):$PATH
➤ lessc --version
lessc 1.7.3 (Less Compiler) [JavaScript]
This avoids the problem of relative paths, especially if you're going to be using this in a build system that will invoke the command in subdirectories.
A bit more robust is:
export PATH=$(npm bin):$PATH
You can either run it, add it to your shell profile, or create an alias like:
alias snpm='export PATH=$(npm bin):$PATH'
If you do go the alias route, be sure to use single quotes so it delays the execution of the variables!
To use on the command line like sails generate foo you will need to install the npm module globally.
npm install -g sails
You could also use the path to the bin in the command if you don't want to install globally:
./node_modules/sails/bin/sails.js generate foo
The official instructions for sails (https://github.com/balderdashy/sails) advises
To install the latest stable release with the command-line tool:
sudo npm -g install sails
This installs globally and adds to a directory like /usr/local/bin that should be in your $PATH.
But to answer the general question regarding the location of the binaries if you install locally, they should be placed in ./node_modules/.bin directory (so run ./node_modules/.bin/sails ...)
If you don't like to mess up with your PATH for running a npm script that isn't global -- e.g. you are the only one to use it --, I would personally recommend the use of an sh "alias".
npm install (locally) your beloved package (json-diff here, for instance)
cd ~ && npm install json-diff
alias it (save it in your ~/.xxxxrc file):
alias diffj "\`npm bin\`/json-diff !*"
Then, for diffing 2 json's:
diffj old.json new.json
In my ~/.bashrc, I have the following:
function nbin {
local dir;
dir=$(npm bin)
if [ -d "$dir" ]; then
( # subshell to not change this shell's $PATH
PATH=$dir:$PATH
eval "$#"
)
else
echo "\"$dir\" is not an npm binary directory." >&1
return 1
fi
}
I can then run executable foo in the .bin directory as:
nbin foo
Possible workaround with NPM 5.2+ using the npx command.
npx sails new test-project
See this question for a similar use case and elegant solutions.

Global Node modules not installing correctly. Command not found

I am having a problem installing global node modules and everything I find online says the solve is just adding -g. Which is not the problem. I believe it's a linking issue or wrong directory issue.
Here is what I do:
$ npm install -g express
npm http GET https://registry.npmjs.org/express
npm http 304 https://registry.npmjs.org/express
npm http GET https://registry.npmjs.org/range-parser/0.0.4
npm http GET https://registry.npmjs.org/mkdirp/0.3.3
...downloads correctly
$ express myapp
bash: express: command not found
However when I run the direct link location to express it works:
$ /usr/local/share/npm/bin/express myapp
create : myapp
create : myapp/package.json
create : myapp/app.js
... Builds app correctly
Where the module is:
$ which node
/usr/local/bin/node
$ node -pe process.execPath
/usr/local/Cellar/node/0.8.20/bin/node
$ npm link express
/Users/bentonrr/Development/Personal/node_modules/express -> /usr/local/share/npm/lib/node_modules/express
In my .bash_profile I have:
export PATH=/usr/local/bin:$PATH
export NODE_PATH=/usr/local/lib/node_modules:/usr/local/lib/node
Do I need to change my Node environment to download to correct folder? Is something not linking correctly? I am lost..
Thanks!
Other Specs:
$ node --version
v0.8.20
$ npm --version
1.2.11
$ brew --version
0.9.4
OSX Version 10.8.2
This may mean your node install prefix isn't what you expect.
You can set it like so:
npm config set prefix /usr/local
then try running npm install -g again, and it should work out. Worked for me on a mac, and the solution comes from this site:
http://webbb.be/blog/command-not-found-node-npm/
EDIT: Note that I just came across this again on a new Mac I'm setting up, and had to do the process detailed here on stackoverflow as well.
Add $(npm get prefix)/bin to your PATH (e.g., in .bashrc), like so:
echo "export PATH=$PATH:$(npm get prefix)/bin" >> ~/.bashrc
For more info, see npm help npm:
global mode:
npm installs packages into the install prefix at prefix/lib/node_modules and bins are installed in prefix/bin.
You can find the install prefix with npm get prefix or npm config list | grep prefix.
My npm couldn't find global packages as well. I did what Brad Parks suggested:
npm config set prefix /usr/local
Then I got a EACCES permissions error (DON'T USE sudo npm install -g <package>) and fixed it through the official npm docs: https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally
On the command line, in your home directory, create a directory for global installations:
mkdir ~/.npm-global
Configure npm to use the new directory path:
npm config set prefix '~/.npm-global'
In your preferred text editor, open or create a ~/.profile file and add this line:
export PATH=~/.npm-global/bin:$PATH
On the command line, update your system variables:
source ~/.profile
Then install a package globally and test it! For example:
npm install -g awsmobile-cli
awsmobile configure
In my case, The NODE_PATH environment variable was empty. Check whether it is empty-
echo $NODE_PATH
if the NODE_PATH is empty. Then change ~/.bash_profile and add NODE_PATH
nano ~/.bash_profile
export NODE_PATH=`npm root -g`
source ~/.bash_profile
Now install npm modules again and check whether that is being installed on the path npm root -g
For Windows users
Add this to your path: "%AppData%\npm"
I do not ever install any npm stuff, via sudo! I have my own reasons, but I just try to keep things simple, and user based, since this is a user development world, and not everyone has root access, and root/sudo installing things like this just seems to clutter up things to begin with. After all, all developers should be able to follow these instructions, not just privileged sudo users.
This particular system is a RHEL7 accessed via SSH:
Frequently one needs various versions of node, so I use NVM https://github.com/creationix/nvm
So with that said, I can show you a working example for -g global installs, using NVM, NPM, and node paths not using root.
set your prefix for .npm-packages if it isn't already. (note, thats a hyphen, not an underscore)
nvm config ls
prefix = "/home/<yourusername>/.npm-packages"
Then adjust your ~/.bash_profile or .bashrc if you prefer readup on why and which here, with the following information.
#PATH EXPORTS
NODE_MODULES=$HOME/.npm
NPM_PACKAGES=$HOME/.npm-packages/bin
export PATH=$PATH:$HOME/bin:$NODE_MODULES:$NPM_PACKAGES
#NVM ENABLE
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
That pretty much covers all paths. For e.g., if you install gulp like this npm install -g gulp it symlinks in ~/.npm-packages/bin (note thats a hyphen, not an underscore). (no need for gulp-cli, or gulp-cl)
You can pretty much replace/comment-out all other node path exports. You can put this path info below any other path info you already have, safely, without it overwriting that stuff.
Check your global Node module's binary folder, and add it to your $PATH.
npm list -g | head -1
If you use nodenv, the path will change whenever you install a new global node version. Adding a node path like this solves my problem.
"$HOME/.nodenv/versions/$(nodenv global)/bin"
Shortcut for adding the path to zsh
$ echo 'export PATH="$HOME/.nodenv/versions/$(nodenv global)/bin"' >> ~/.zshrc
Add the following line to your ~/.bash_profile
export PATH="$HOME/.npm/bin:$PATH"
Load bash profile
bash -l
The problem I had was missing the binaries because the user specific .npmrc file in my home directory had bin-links set to false, though the default is true.
Just in case this is your problem check that none of your .npmrc files have it set to false.
Then re-installing all modules will create the binaries at the prefix so your PATH can see them.
It may seem like a hack, but setting up yarn when possible saves you a lot of node environment headaches for various unix distros.

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