Deploying Node applications should be a straightforward task. For some reasons, you can read here and there that additional dependencies need to be installed globally.
Which is generally very rarely needed.
You will learn how to create tidy one line Node.js installs and how to distribute complimentary tooling for advanced users and developers.
Global install of Node modules is designed for system-wide command-line applications.
If you request your users to install additional and global Node modules in order to use your app… well, it works but it is wrong. And please, don't make Soledad angry ;-)
It is wrong because it adds extraneous implicit steps in the install process. And worse, it exposes you to global version conflicts.
If a module asks for
npm install -g email@example.com and another one asks for
npm install -g firstname.lastname@example.org, you are screwed in a way or another.
It is okay to install global modules for unique executables (like npm) or global wrappers (like grunt-cli or gulp-cli) if they are non-project specific.
So what if you want to provide executables to your standalone application? Like building, updating data from a registry or whatever? Well, you have two choices:
- provide an executable Node module (in the
./binfolder of your app for example);
- use npm scripts.
Understanding the npm scripts environment
npm executable exposes a script mechanism through the
run action. It will basically look for a matching script entry in your
package.json and will run it as a shell command.
Let's say we want to expose a command responsible for our code linting:
npm run lint
We could use jshint to do so. And as jshint exposes an executable, it will be symlinked as
node_modules/.bin/jshint during the install process.
package.json fashion, it would result in:
The trick is any npm command prefixes the
$NODE_PATH variable for the duration of the command only (no global leak).
In other terms, Node will first look for local executables before looking for globally available ones.
package.json can be shortened as:
And by keeping npm tasks command simple and explicit, you can silently upgrade the underlying process. We could provide HTML linting to our previous command for free:
This is your Two for One meal deal! Vinegar chips are not included.
I see two major benefits in that technique:
- it is a good way to provide a tooling as a habit —
npm testwill run whatever
karmacommand is called under the hood;
- the tooling is shipped with and scoped to your application.
You can read more on npm-based task automation on substack's blog.
Advanced npm scripts
With the recent release of
email@example.com, we now have the ability to pass extra parameters to our
These arguments will be simply appended to your script command.
In a nutshell, we now have proxy scripts:
npm run lint-js -- --version
Cordova users will be happy to enjoy the long-awaited inception:
npm install -g is impossible
It happens sometimes you cannot even either install a module globally. Or a global wrapper module is incompatible with your local scripts.
In that case, enclose your usual global Node executables as local dependencies:
Again, it is not ideal but everything is installable at once as long as the npm command is globally available on your system.
Meanwhile people argue on which is the best ephemeral tooling system between Grunt, Gulp and Broccoli, we benefit of a suitable and long-term task mechanism since the early days of
Whereas npm tasks are not always perfect and can hardly scale for large projects or for multiple executable targets (enters Task/Build systems), they are efficient for small to medium sized projects.
Moreover, the combination of Node dependencies and the npm task environment is great to fulfil a flexible yet simple install and runnable Node.js system.