Loading and using modules
The Node designers believe that most modules should be developed in userland—by developers, for developers. Such an effort is made to limit the growth of the standard library. Node's standard library contains the following short list of modules:
Network and I/O |
Strings and Buffers |
Utilities |
---|---|---|
TTY UDP/Datagram HTTP HTTPS Net DNS TLS/SSL Readline FileSystem |
Path Buffer Url StringDecoder QueryString |
Utilities VM Readline Domain Console Assert |
Encryption and Compression |
Environment |
Events and Streams |
ZLIB Crypto PunyCode |
Process OS Modules |
Child Processes Cluster Events Stream |
Modules are loaded via the global require
statement, which accepts the module name or path as a single argument. You are encouraged to augment the module ecosystem by creating new modules or new combinations of modules.
The module system itself is implemented in the require
(module
) module.
Understanding the module object
A Node module is simply a JavaScript file expected to assign a useful value to the module.exports
property:
module.exports = new function() { this.it = function(it) { console.log(it); } }
We now have a module that can be required by another file. If this module was defined in the file ./say.js
, the following is one way to use it:
var say = require("./say"); say.it("Hello"); // Hello
Note how it was not necessary to use the .js
suffix. We'll discuss how Node resolves paths shortly.
Note
Node's core modules are also defined using the standard module.exports
pattern, as can be seen by browsing the source code defining console
: https://github.com/joyent/node/blob/master/lib/console.js.
Once loaded, modules are cached based on their resolved filename, resolved relative to the calling module. Subsequent calls to require(./myModule)
will return identical (cached) objects. Note however that accessing the same module via a different relative path (such as ../../myModule
) will return a different object—think of the cache being keyed by relative module paths.
A snapshot of the current cache can be fetched via require('module')._cache)
.
The module object itself contains several useful readable properties, such as:
module.filename: The name of the file defining this module.
module.loaded: Whether the module is in process of loading. Boolean true if loaded.
module.parent: The module that required this module, if any.
module.children: The modules required by this module, if any.
Note
You can determine if a module is being executed directly via node module.js
or via require('./module.js')
by checking if require.main === module
, which will return true
in the former case.
Resolving module paths
The require
statement is regularly seen when browsing (or building) a Node program. You will have noticed that the argument passed to require can take many forms, such as the name of a core module or a file path.
The following pseudo code, taken from the Node documentation, is an ordered description of the steps taken when resolving module paths:
require(X) from module at path Y 1. If X is a core module, a. return the core module b. STOP 2. If X begins with './' or '/' or '../' a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X) 3. LOAD_NODE_MODULES(X, dirname(Y)) 4. THROW "not found" LOAD_AS_FILE(X) 1. If X is a file, load X as JavaScript text. STOP 2. If X.js is a file, load X.js as JavaScript text. STOP 3. If X.node is a file, load X.node as binary addon. STOP LOAD_AS_DIRECTORY(X) 1. If X/package.json is a file, a. Parse X/package.json, and look for "main" field. b. let M = X + (json main field) c. LOAD_AS_FILE(M) 2. If X/index.js is a file, load X/index.js as JavaScript text. STOP 3. If X/index.node is a file, load X/index.node as binary addon. STOP LOAD_NODE_MODULES(X, START) 1. let DIRS=NODE_MODULES_PATHS(START) 2. for each DIR in DIRS: a. LOAD_AS_FILE(DIR/X) b. LOAD_AS_DIRECTORY(DIR/X) NODE_MODULES_PATHS(START) 1. let PARTS = path split(START) 2. let ROOT = index of first instance of "node_modules" in PARTS, or 0 3. let I = count of PARTS - 1 4. let DIRS = [] 5. while I > ROOT, a. if PARTS[I] = "node_modules" CONTINUE c. DIR = path join(PARTS[0 .. I] + "node_modules") b. DIRS = DIRS + DIR c. let I = I - 1 6. return DIRS
File paths may be absolute or relative. Note that local relative paths will not be implicitly resolved and must be stated. For example, if you would like to require the file myModule.js
from the current directory it is necessary to at least prepend ./
to the file name in order to reference the current working directory—require('myModule.js')
will not work. Node will assume you are referring to either a core module or a module found in the./node_modules
folder. If neither exists, a MODULE_NOT_FOUND
error will be thrown.
As seen in the pseudo code above, this node_modules
lookup ascends a directory tree beginning from the resolved path of the calling module or file. For example, if the file at '/user/home/sandro/project.js'
called require('library.js')
, then Node would seek in the following order:
/user/home/sandro/node_modules/library.js /user/home/node_modules/library.js /user/node_modules/library.js /node_modules/library.js
Organizing your files and/or modules into directories is always a good idea. Usefully, Node allows modules to be referenced through their containing folder, in two ways. Given a directory, Node will first try to find a package.json
file in that directory, alternatively seeking for an index.js
file. We will discuss the use of the package.json
files in the next section. Here we need to simply point out that if require
is passed the directory ./myModule
it will look for is ./myModule/index.js
.
Note
If you've set the NODE_PATH
environment variable then Node will use that path information to do further searches if a requested module is not found via normal channels. For historical reasons $HOME/.node_modules
, $HOME/.node_libraries
, and $PREFIX/lib/node
will also be searched. $HOME
represents a user's home directory, and $PREFIX
will normally be the location Node was installed to.