This talk is dedicated to npm
version 2 with focus on dependency management. With npm you can handle both your server and client side javascript dependencies (e.g. for React and Webpack).
When you start your javascript project it is useful to initialize your project with npm init
. It creates package.json
file which describes your projects. Let’s say that you want to use async library for the project. Just type npm install async --save
in the directory where your package.json file is.
sodik@sentinel:/tmp/npm$ npm install async --save npm WARN package.json test-project@1.0.0 No repository field. npm WARN package.json test-project@1.0.0 No README data async@1.5.0 node_modules/async
Npm downloaded the dependency to node_modules
so that you can import (assuming ES6) it from your javascript code. Additionally package.json was automatically updated to mark your dependencies:
{ "name": "test-project", "version": "1.0.0", "description": "Test project", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "js" ], "author": "sodik", "license": "ISC", "dependencies": { "async": "^1.5.0" } }
Npm will install any transitive dependencies if needed for you. Why do I need --save
parameter? Without it npm will just download dependency but won’t update package.json for you. Maybe you just want to try new library but you don’t know if it will work? However in general I would not recommend to do that since you can easily forget to update your package.json (you can “rollback” any your changes in GIT if the library won’t work).
Your colleague wants to join the project, you push it to source control (without node_modules, of course). Now he can simply install all dependencies from package.json with single command npm install
. This needs to be used each time someone adds new dependencies.
Occasionally you (or your colleague) will remove some dependency. In that situation npm install
won’t remove it for others. At least npm will warn you when you list all your dependencies:
sodik@sentinel:/tmp/npm$ npm ls test-project@1.0.0 /tmp/npm ├── async@1.5.0 └── moment@2.10.6 extraneous npm ERR! extraneous: moment@2.10.6 /tmp/npm/node_modules/moment
If you are sure that you want remove all “not tracked” dependencies, just type npm prune
and you are done.
As time progresses, your 3rd party dependencies can get updated. Maybe some bug fix or just new functionality was added. You can easily check that.
sodik@sentinel:/tmp/npm$ npm outdate Package Current Wanted Latest Location moment 2.0.0 2.10.6 2.10.6 moment
To update dependencies use npm update --save
. You can also only specify specific packages to be updated. Once the package.json is updated, others can simply call npm install
to update their dependencies (actually to fix as such dependencies do not satisfy package.json definition – npm calls them invalid
).
But how does npm handle versions? Npm assumes semantic versioning (although npm can’t force package authors to comply to it). When you install new dependency, you will find in package.json something like "moment": "^2.10.6"
. It means that you want version 2.10.6 or newer but only if there are no breaking changes (i.e. less then version 3.0.0 – as semantic versioning defines). Note that breaking changes works differently for version less then 1.0.0! See example output of npm outdated:
Package Current Wanted Latest Location babel-core 5.8.33 5.8.33 6.1.2 babel-core
Even though there is version 6.1.2 of babel-core, wanted version is still 5.8.33. If you want to update anyway, you have to do it explicitly and face the consequences 🙂
Caret “^2.10.6” syntax is not the only one, you can use use “1.x” for any “1.*.*” version. There are plenty other syntaxes like “~1.2.3” or even expressions like “1.2.7 || >=1.2.9 <2.0.0”. For more details check the documentation.
So far we have spoken only about dependencies. However in package.json you can have also devDependencies, peerDependencies, bundledDependencies and optionalDependencies. For example you don’t need your test dependencies to run the package, thus they should be installed as development dependencies via npm install sax --save-dev
or just npm install sax -D
. Be default dev dependencies will be installed with normal dependencies. Same holds true for optional dependencies with only exception that failure to install optional dependency won’t result in failure of installation process.
Npm and node.js can be useful not only for client-server applications but also for various utilities. You might want to generate documentation using apidoc or just run your tests with mocha. If you use such utilities across multiple projects (or just use it also outside of your projects), you can install globally. Such dependency won’t be installed in your local directory but will be installed somewhere in your system.
In some cases it is fine but the only drawback is that you can’t manage them (at least not easily) via your package.json. In general I would recommend to put all project dependencies into package.json and install them locally (e.g. you won’t affect different projects with possibly different version of your build tooling). Just use your build tooling (e.g. gulp) to do all work automatically or you can use package.json scripts section.
"scripts": { ...., "lint": "eslint **/*.jsx" },
Then you can execute it via npm run lint
. It is even possible to pass additional arguments to the end via ‘–‘:
npm run lint -- --format compact
. Or check this trick from stack overflow.