npm 1.0 is in release candidate mode. Go get it!
More than anything else, the driving force behind the npm 1.0 rearchitecture was the desire to simplify what a package installation directory structure looks like.
In npm 0.x, there was a command called bundle that a lot of people liked. bundle let you install your dependencies locally in your project, but even still, it was basically a hack that never really worked very reliably.
Also, there was that activation/deactivation thing. That’s confusing.
Two paths
In npm 1.0, there are two ways to install things:
- globally —- This drops modules in
{prefix}/lib/node_modules, and puts executable files in{prefix}/bin, where{prefix}is usually something like/usr/local. It also installs man pages in{prefix}/share/man, if they’re supplied. - locally —- This installs your package in the current working directory. Node modules go in
./node_modules, executables go in./node_modules/.bin/, and man pages aren’t installed at all.
Which to choose
Whether to install a package globally or locally depends on the global config, which is aliased to the -g command line switch.
Just like how global variables are kind of gross, but also necessary in some cases, global packages are important, but best avoided if not needed.
In general, the rule of thumb is:
- If you’re installing something that you want to use in your program, using
require('whatever'), then install it locally, at the root of your project. - If you’re installing something that you want to use in your shell, on the command line or something, install it globally, so that its binaries end up in your
PATHenvironment variable.
When you can’t choose
Of course, there are some cases where you want to do both. Coffee-script and Express both are good examples of apps that have a command line interface, as well as a library. In those cases, you can do one of the following:
- Install it in both places. Seriously, are you that short on disk space? It’s fine, really. They’re tiny JavaScript programs.
- Install it globally, and then
npm link coffee-scriptornpm link express(if you’re on a platform that supports symbolic links.) Then you only need to update the global copy to update all the symlinks as well.
The first option is the best in my opinion. Simple, clear, explicit. The second is really handy if you are going to re-use the same library in a bunch of different projects. (More on npm link in a future installment.)
You can probably think of other ways to do it by messing with environment variables. But I don’t recommend those ways. Go with the grain.
Slight exception: It’s not always the cwd.
Let’s say you do something like this:
cd ~/projects/foo # go into my project
npm install express # ./node_modules/express
cd lib/utils # move around in there
vim some-thing.js # edit some stuff, work work work
npm install redis # ./lib/utils/node_modules/redis!? ew.
In this case, npm will install redis into ~/projects/foo/node_modules/redis. Sort of like how git will work anywhere within a git repository, npm will work anywhere within a package, defined by having a node_modules folder.
Test runners and stuff
If your package’s scripts.test command uses a command-line program installed by one of your dependencies, not to worry. npm makes ./node_modules/.bin the first entry in the PATH environment variable when running any lifecycle scripts, so this will work fine, even if your program is not globally installed:
{ "name" : "my-program"
, "version" : "1.2.3"
, "dependencies": { "express": "*", "coffee-script": "*" }
, "devDependencies": { "vows": "*" }
, "scripts":
{ "test": "vows test/*.js"
, "preinstall": "cake build" } }
Is there an easy upgrade path from a system-wide npm 0.3 installation to npm 1.0? Or do global packages need to be uninstalled and reinstalled? (Especially when they have stuff in /usr/local/bin or /usr/local/share/man.)
The two *can* live side-by-side fairly painlessly, but npm 1.0 is careful about not trashing anything that doesn’t belong to it. You may occasionally have to throw
-f(or--forceif you prefer long opts) onto global installation commands.The cleanest way, of course, is to uninstall everything, and then re-install what you need with 1.0.
Re: the cwd exception, does npm work exactly like git? Can you nest packages like “fake” git submodules, or is there some kind of contextual awareness about what “package” you last messed with?
Not “exactly”, but yes, sort of. You can do
npm install foo; npm install bar --prefix node_modules/footo install it inside of foo’s tree. If you donpm explore foo, then you’ll be in a subshell innode_modules/foo, and anything you install there will be inside of foo’s root.I inadvertently upgraded to 1.0 today and was caught off guard by the local installation — I guess I should’ve read this post first
BTW: Is there any way to do something like an “npm update” that installs only packages that are missing from an installation?
Well,
npm updatedoes that. But you could also donpm update footo only update the versions of “foo” packages.I’m a bit curious about how you “inadvertently” updated. Either the curl request or
npm install npmshould refuse to install 1.0 unless you explicitly tell it to.Here’s my particular example. I have a package.json file in my project that lists “underscore”, but I haven’t installed it yet. When I do “npm update”, it still isn’t installed. When I do “npm install”, “underscore” is installed, but everything else is also reinstalled. What I’m looking for is something like “npm install-missing”.
Regarding my inadvertent update, I used the one-liner from https://github.com/isaacs/npm. I didn’t look closely enough to see the “=rc” flag.
Ah, that’s a bug. Fixed on https://github.com/isaacs/npm/commit/791bf193df0a05301951ff18905bb4b0fa3be6b0
EDIT: Published on 1.0.0rc8
Super — thanks so much! I’m really liking npm.
Hello. I installed npm 1.0 today and was surprised by how it handles global installs.
You say in this post that when installed globally, packages live in
{prefix}/lib/node_module. I’m wondering why it doesn’t install in{prefix}/lib/nodesince it is there that node looks for global modules.Is this intentional ? should I set my
$NODE_PATHaccordingly?Yes, that is intentional.
No, you don’t need to change your
$NODE_PATH.Install locally for
require(). Install globally to put things in thePATHto run from the command line.Also check out
npm help linkandnpm help foldersKnowing how you do things, I suspected it was intentional =)
It just bothered me when testing new modules in the REPL, But I don’t do it very often so it’s not a big deal.
thanks for the pointers
If you install locally, it actually will work in the repl now.
I understand that the recommendation is to install locally for require(). But, theoretically, if I wanted to use require() for a module that I use all the time (e.g. express), couldn’t I just install express globally, and update my $NODE_PATH to “{prefix}/lib/node_modules” ?
I guess I don’t understand the recommendation here NOT to update $NODE_PATH. What does having $NODE_PATH point to {prefix}/lib/node get me if nothing gets installed there?
Any clarification would be great!
You can of course put modules in
{prefix}/lib/nodeif you want, and they will be visible. Or you can point the environment var at the global folder, and it’ll find your globally installed things. You can also make use of thenpm linkcommand, and that’s helpful sometimes.A lot of us have found through (sometimes painful) experience that dependencies are just a lot easier to manage when they’re local. You don’t have to worry about conflicts, and it’s a lot more obvious where node is finding things. When it’s time to deploy, everything you need is in one folder, which is nice.
Pingback: Git per il controllo di qualità - ReFactor.it
The ‘easy deployment’ case certainly sounds compelling. As a ‘first weekend with node’ guy, I’m looking to start wrapping my head around best practices, general conventions, etc.
I appreciate the clarification, Isaac. Thanks!
“Install it in both places. Seriously, are you that short on disk space? It’s fine, really. They’re tiny JavaScript programs.”
Are you familiar with disk caching? If you read 5 separate copies of the same library, that’s going to mean 5 cold reads and 5 times more disk caching all for a library that may well be compatible with the 5 places it’s being called from.
You can do better than this. This method is just a lazy cop out and your rationalising of it completely misses the point.
If you show me a node program where that matters, I’ll show you the help documentation for the
npm linkcommand.