npm: Understanding dependencies
1. dependencies
Suppose, you create a javascript package nameyour-package and release a your-package.js file.
And Bob (someone) want to use
your-package.js in his project name bob-project. Then Bob install your-package via npm:
npm install your-package
But
your-package.js required lodash 1.0.0 to work with.
'use strict';
var _ = require('lodash');
// call some lodash functions
so if Bob want to use
your-package.js then he must have lodash 1.0.0 somewhere in bob-project.
And because of Bob only care about
your-package.js, it painfull for Bob to install lodash 1.0.0 manually.
In this case, you should define
lodash 1.0.0 as your-package dependency, inside package.json file:
"dependencies": {
"lodash": "1.0.0"
}
# Note 1
You may needlodash 1.0.0as well to run test. So you should install lodash via npm:npm install lodash@1.0.0npm will automaticlly add dependencylodash 1.0.0topackage.jsonfile.
Now if Bob install
your-package, npm automaticlly install lodash 1.0.0 along with it.
bob-project folder structure is like:
bob-project/ ├──node_modules/ │ ├──your-package/ │ └──lodash/ # version 1.0.0 ├──package-lock.json └──package.json
# Note 2
If already have other version of lodash (ex 2.0.0) installed before Bob installingbob-project, folder struct will become:bob-project/ ├──node_modules/ │ ├──your-package/ │ │ └──node_modules/ │ │ └──lodash/ # version 1.0.0 │ └──lodash/ # version 2.0.0 ├──package-lock.json └──package.jsonwhen executerequire, nodejs will search modules from current folder first, if package can not be found nodejs will find package in parent folder an so on, soyour-package.jswill getlodash 1.0.0, separate withlodash 2.0.0. Everything still work!
2. devDependencies
To createyour-package you may need some task-runner like gulp, for example to concat all js file into one. So you install gulp via npm:
npm install gulp
By this way (# Note 1 above), npm will add
gulp to dependencies, and Bob will get automaticlly install gulp that he does not need to run your-package.js.
In this case, you should define
gulp as "devDependencies" by install gulp with --save-dev:
npm install gulp --save-devyour
package.json will be like:
"dependencies": {
"lodash": "1.0.0"
},
"devDependencies": {
"gulp": "^3.9.1"
},
Now when Bob install
your-package, npm does not care about "devDependencies" and Bob only get lodash be installed.
3. peerDependencies
Ifyour-package actually is a grunt plugin for example, your-package.js file will be like:
'use strict';
var _ = require('lodash');
module.exports = (grunt) => {
// call some lodash functions
// use grunt instance to do some small stuff to support main grunt function
}
Without require('grunt'), it's using host grunt package instance (the package installed in bob-project). That mean your-package still need grunt for running, and like "1. dependencies" above it may need to automaticlly install grunt as dependencies.
But, because of
your-package is a grunt plugin so maybe Bob has already installed other version of grunt in bob-project, therefore a version of grunt will be automaticlly installed inside your-package folder structure (# Note 2) and would never be used, that is not good.
So
your-package should not automaticlly install grunt. By this way you need to tell Bob (when he's installing your-package) that he need grunt for your-package, moreover which version of grunt that your-package can run with. To do this, you need peerDependencies!
You define grunt as peerDependencies like this:
"dependencies": {
"lodash": "1.0.0"
},
"devDependencies": {
"gulp": "^3.9.1"
},
"peerDependencies": {
"grunt": "0.4.2" # suppose that your-package only work with 0.4.2
},
Now when Bob install your-package, he will get a warning that he need to manually install grunt 0.4.2.
Yes, peerDependencies is just for giving warnings, nothing else!
Event ifyour-packageusing direct dependency likelodash 1.0.0(1.0.0 is lasted version of lodash when you createyour-package). But now lodash release 2.0.0 with many great features, usingyour-packagewithlodash 2.0.0would great, and you too lazy to test and updateyour-package. In this case, you would consider to define lodash in peerDependencies like this:"dependencies": { }, "devDependencies": { "gulp": "^3.9.1" }, "peerDependencies": { "grunt": "0.4.2" # suppose thatNow when Bob installyour-packageonly work with 0.4.2 "lodash": ">=1.0.0" # 1.0.0 or newer },your-package, he just manually installlodash 2.0.0to work with.
Why not define direct dependency to automaticlly installlodash 2.0.0like this ?:"dependencies": { "lodash": ">=1.0.0" # 1.0.0 or newer }, "devDependencies": { "gulp": "^3.9.1" }, "peerDependencies": { "grunt": "0.4.2" # suppose thatBecause in case ofyour-packageonly work with 0.4.2 },your-packagecan not work withlodash 2.0.0due to removed APIs (you can not test in time when lodash 2.0.0 release),your-packageinbob-projectwill brocken.
If you using peerDependencies, after manually installed lodash 2.0.0 and realize that issues, Bob can rollback to lodash 1.0.0 to makeyour-packagework. Fine!
# Preferences
# Peer Dependencies# Understanding the npm dependency model
# Why use peer dependencies in npm for plugins?
# Semantic Versioning
# Specifics of npm's package.json handling
# Requiring modules in Node.js: Everything you need to know