Tech talks: NPM dependencies

Tech talks: NPM dependencies

Posted by Stanislav Miklik | November 10, 2015 | tech-talks

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

Basics

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

Colaboration

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.

Versions

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.

Dependencies

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.

Global dependencies

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.

Add a comment

*Please complete all fields correctly

Related Blogs

Posted by bielik | 31 August 2016
When it comes to dynamic and static typing there are big discussions and many opinions all around. I've collected following thoughts about this problem. Each one of it is questionable…
Posted by bielik | 23 December 2015
Nodeschool workshops have finally started in Bratislava (hosted by Progressbar). At the first meet-up we set up node environment and went through several workshoppers from the nodeschool site. There were people from…
Posted by miklik | 07 December 2015
This tech talks was quick presentation of our first steps in server monitoring. And at the same time it was simple introduction to Kibana and Elasticsearch. What to collect? We…