The Node Module System & npm Internals
The problem
When code says require('lodash') or import someModule from './utils', something has to turn that string into an actual loaded file — and with potentially hundreds of installed packages nested inside each other's dependencies, resolving exactly which file that string refers to is a real, nontrivial algorithm.
Why now
Modules and bundling already established what a module is at the language level; this topic is Node's specific, concrete resolution algorithm for finding a module's actual file on disk, and package management already covered how those files got installed and organized into node_modules in the first place.
Mental model
Node's module resolution is a defined search algorithm: for a relative path, look at the exact file/directory specified; for a bare package name, walk up the directory tree checking each ancestor's node_modules folder until one contains a matching package — which is exactly why nested dependencies can each have their own, different version of the same package, resolved independently at each level of that walk.
Requires
- Modules & BundlingJavaScript Runtime
This topic is Node's concrete implementation of exactly the module concept (a file with its own scope, explicit imports/exports) modules-and-bundling already established in the abstract.
- Package ManagementLinux
Module resolution walks the node_modules directory structure package management already covered creating; you need to know how and why that structure exists before an algorithm for searching it is meaningful.
Used in
- Every require() and import statement in a Node.js application
- Debugging 'cannot find module' errors by understanding exactly where Node looked
- Understanding how two packages can depend on different versions of the same third package without conflict
- package.json's main/exports fields, controlling what a package's entry point actually resolves to
Projects
- Create a nested node_modules structure by hand where two packages depend on different versions of the same dependency, and confirm each resolves to its own version
- Trace, step by step, exactly which directories Node checks when resolving require('some-package') from a file several directories deep in a project
Examples
- require('./math') looks for exactly ./math.js (or ./math/index.js), while require('lodash') walks up through node_modules folders until one contains lodash
- Package A depending on lodash@2 and Package B depending on lodash@4 can coexist because each gets its own nested node_modules/lodash resolved independently
Resources
Mastery checklist
- I can explain Node's module resolution algorithm for relative paths versus bare package names
- I can explain why nested dependencies can each resolve to different versions of the same package
- I can diagnose a 'cannot find module' error by tracing the resolution algorithm
- I can explain what package.json's main or exports field controls