Generally speaking, the module name is a pathname, but with the file extension removed. Earlier, when we wrote require('./simple'), Node.js knew to add .js to the filename and load in simple.js. Similarly, Node.js would recognize simple.json or simple.node as the filename legitimately satisfying require('./simple').
There are three types of module identifiers: relative, absolute, and top-level:
- Relative module identifiers: These begin with ./ or ../ and absolute identifiers begin with /. The module name is identical with POSIX filesystem semantics. The resultant pathname is interpreted relative to the location of the file being executed. That is, a module identifier beginning with ./ is looked for in the current directory, whereas one starting with ../ is looked for in the parent directory.
- Absolute module identifiers: These begin with/and are, of course, looked for in the root of the filesystem, but this is not a recommended practice.
- Top-level module identifiers: These begin with none of those strings and are just the module name, or else module-name/path/to/module. These must be stored in a node_modules directory, and the Node.js runtime has a nicely flexible algorithm for locating the correct node_modules directory:
- In the case of module-name/path/to/module specifiers, what will be loaded is a module path/to/module within the top-level module named module-name
- The baked-in modules are specified using top-level module names
The search begins in the directory containing the file calling require(). If that directory contains a node_modules directory, which then contains either a matching directory module or a matching file module, then the search is satisfied. If the local node_modules directory does not contain a suitable module, it tries again in the parent directory, and it will continue upward in the filesystem until it either finds a suitable module or it reaches the root directory.
That is, with a require call in /home/david/projects/notes/foo.js, the following directories will be consulted:
- /home/david/projects/notes/node_modules
- /home/david/projects/node_modules
- /home/david/node_modules
- /home/node_modules
- /node_modules
If the module is not found through this search, there are global folders in which modules can be located. The first is specified in the NODE_PATH environment variable. This is interpreted as a colon-delimited list of absolute paths similar to the PATH environment variable. On Windows, the elements of NODE_PATH are of course separated by semicolons. Node.js will search those directories for a matching module.
This variable was implemented before the module resolution algorithm just described was finalized. Because of that algorithm, NODE_PATH is largely unnecessary.
There are three additional locations that can hold modules:
- $HOME/.node_modules
- $HOME/.node_libraries
- $PREFIX/lib/node
In this case, $HOME is what you expect, the user's home directory, and $PREFIX is the directory where Node.js is installed.
Some are beginning to recommend against using global modules. The rationale is the desire for repeatability and deployability. If you've tested an app, and all its code is conveniently located within a directory tree, you can copy that tree for deployment to other machines. But, what if the app depended on some other file that was magically installed elsewhere on the system? Will you remember to deploy such files?