In node.js, a module is a file that exports a value (e.g., object, string, function, etc...) and can be required by other modules at runtime via the require(modulePath)
function.
In node.js, a package, is a special case of module that when installed resides in the node_modules
directory. Packages are different from modules in that they must have a package.json
and can declare dependendencies, which npm will auto-install and resolve according to the versions specified in their package.json
. Packages are required by name (e.g., require('bluebird')
), and can be published for others to install.
When exporting a single value, your module.exports
assignment should be at the top of your file just below the require
s.
// greet.js
module.exports = function greet(name) {
console.log(`Hello, ${name}!`)
}
// index.js
let greet = require('./greet')
greet('World') // Outputs: "Hello, World!"
When exporting an object or set of values, the module.exports
should be the very last statement in the file.
// greet.js
function greet(name) {
console.log(`Hello, ${name}!`)
}
function goodbye(name) {
console.log(`Goodbye, ${name}!`)
}
module.exports = {greet, goodbye}
// index.js
let {greet, goodbye} = require('./greet')
greet('World') // Outputs: "Hello, World!"
goodbye('World') // Outputs: "Goodbye, World!"
Note: While use of the exports
shorthand alias in place of module.exports
is supported, it is discouraged due to issues that arise with circular or cyclic dependencies.
Depending on the the argument, require
follows 1 of 3 resolution patterns:
When the module name matches the name of a node.js core modules (e.g., path
, fs
, http
) the core module will be returned:
let path = require('path')
If the module contains a relative or absolute file path (i.e., by containing ./
or ../
), the file is loaded based on that path. (See the export example above)
let path = require('./path')
In this case, even though path
is a core module, the module at ./path.js
, path.json
, or path.node
(in that order) is loaded instead.
Note: Don't name modules or packages names that conflict with core. For packages, they will be unrequirable, and for modules, they will cause confusion.
Lastly, if it's not a core module and contains no file delimiters, require
will check for corresponding packages installed in the node_modules
directory.
Note: 3rd party packages are one of node.js' greatest strengths. Checkout the npm registry for a searchable list.
To install the latest version of a package from the npm registry (e.g., fs-extra
):
npm install fs-extra
Or, for a specific version:
npm install fs-extra@0.18.2
npm install
will download and save the package in node_modules
, and proceed to recursively install all the dependencies listed in the package's package.json
's dependencies
list.
Packages can be installed "globally" with the -g
flag. This can cause confusion, because global packages are not globally requirable. In node.js, a "global" package is a package that exposes a shell executable API (e.g., babel-node
). If you need to require
a package, you must install it locally.
Run npm init
inside the project directory to create a package.json
file, using the defaults results in the following:
{
"name": "foo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Typically, "name"
and "version"
are the most important properties followed by "main"
, "scripts"
and "dependencies"
(explained below). "name"
is the name given to npm to install a package, and "version"
is the version this install corresponds to.
Note: Typically, a starting version of 0.0.1
is more appropriate than 1.0.0
since your new package is probably not production ready.
"main"
The package entry point when require
d. (e.g., require('fs-extra')
loads node_modules/fs-extra/lib/index
). If "main"
is empty, "index.js"
will be used.
"scripts"
Used for CLI convenience commands, most commonly npm test
and npm start
. Additional keys can be added to "scripts"
and run via npm run <script name>
:
"scripts": {
"start": "node index.js",
"test": "mocha",
"hello": "echo 'hello'"
}
In this case, npm run hello
would run echo 'hello'
.
"dependencies"
Since node_modules
is ignored when publishing a package and is not typically checked-in, dependencies must be specified in the "dependencies"
key. For convenience, use npm install --save <package name>
to save the name and version in package.json
:
npm install --save fs-extra
Now package.json
includes:
"dependencies": {
"fs-extra": "^0.18.2"
}
"devDependencies"
Use "devDependencies"
for specifying dependencies that are only needed when running tests or builds. For convenience, use npm install --save-dev <package name>
to save the name and version in package.json
:
npm install --save-dev mocha
Now package.json
includes:
"devDependencies": {
"mocha": "^2.2.4"
}
Note: Unlike "dependencies"
, "devDependencies"
are only installed when npm install
is run from the package's root directory. In other words, a dependency's "devDependencies"
is never installed.
In package.json
"dependencies"
include a three-number hierarchical version number spec called semver. (npm's semver documentation)
The semver hierarchy is as follows, major.minor.patch
:
The best approach to versioning is to "trust but verify". When we trust publishers, the major version is usually all that matters. npm follows this rule by using the ^
range (see "Ranges" below) by default.
*
"*"
is synonymous with "latest"
. npm will install whatever the latest version is regardless of potential incompatibility. *
can also be used for minor and patch versions: "1.3.*"
or "1.*.*"
.
^
^
is the default. npm will install the latest version with a matching major value. For example, "mocha": "^2.2.4"
will allow mocha to be updated up to 3.0.0
non-inclusive, which denotes breaking changes.
~
~
is similar to ^
, but for the latest minor version. For example, "mocha": "~2.2.4"
will allow mocha to be updated up to 2.3.0
non-inclusive.
For more information the npm
and package.json
documentation.
How I Write Modules by Substack