mirror of https://github.com/nodejs/node.git
node_modules module lookup, +docs and test.
parent
dafd6d9137
commit
46513483cd
|
@ -82,11 +82,32 @@ then `require('./foo/bar')` would load the file at
|
|||
entry point to their module, while structuring their package how it
|
||||
suits them.
|
||||
|
||||
Any folders named `"node_modules"` that exist in the current module path
|
||||
will also be appended to the effective require path. This allows for
|
||||
bundling libraries and other dependencies in a 'node_modules' folder at
|
||||
the root of a program.
|
||||
|
||||
To avoid overly long lookup paths in the case of nested packages,
|
||||
the following 2 optimizations are made:
|
||||
|
||||
1. If the module calling `require()` is already within a `node_modules`
|
||||
folder, then the lookup will not go above the top-most `node_modules`
|
||||
directory.
|
||||
2. Node will not append `node_modules` to a path already ending in
|
||||
`node_modules`.
|
||||
|
||||
So, for example, if the file at
|
||||
`/usr/lib/node_modules/foo/node_modules/bar.js` were to do
|
||||
`require('baz')`, then the following places would be searched for a
|
||||
`baz` module, in this order:
|
||||
|
||||
* 1: `/usr/lib/node_modules/foo/node_modules`
|
||||
* 2: `/usr/lib/node_modules`
|
||||
|
||||
`require.paths` can be modified at runtime by simply unshifting new
|
||||
paths onto it, or at startup with the `NODE_PATH` environmental
|
||||
variable (which should be a list of paths, colon separated).
|
||||
|
||||
|
||||
The second time `require('foo')` is called, it is not loaded again from
|
||||
disk. It looks in the `require.cache` object to see if it has been loaded
|
||||
before.
|
||||
|
|
|
@ -163,6 +163,35 @@ Module._findPath = function(request, paths) {
|
|||
return false;
|
||||
};
|
||||
|
||||
// 'from' is the __dirname of the module.
|
||||
Module._nodeModulePaths = function(from) {
|
||||
// guarantee that 'from' is absolute.
|
||||
from = path.resolve(from);
|
||||
|
||||
// note: this approach *only* works when the path is guaranteed
|
||||
// to be absolute. Doing a fully-edge-case-correct path.split
|
||||
// that works on both Windows and Posix is non-trivial.
|
||||
var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//;
|
||||
// yes, '/' works on both, but let's be a little canonical.
|
||||
var joiner = process.platform === 'win32' ? '\\' : '/';
|
||||
var paths = [];
|
||||
var parts = from.split(splitRe);
|
||||
|
||||
var root = parts.indexOf('node_modules') - 1;
|
||||
if (root < 0) root = 0;
|
||||
|
||||
var tip = parts.length - 1;
|
||||
|
||||
for (var tip = parts.length - 1; tip >= root; tip --) {
|
||||
// don't search in .../node_modules/node_modules
|
||||
if (parts[tip] === 'node_modules') continue;
|
||||
var dir = parts.slice(0, tip + 1).concat('node_modules').join(joiner);
|
||||
paths.push(dir);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
Module._resolveLookupPaths = function(request, parent) {
|
||||
if (NativeModule.exists(request)) {
|
||||
|
@ -171,14 +200,18 @@ Module._resolveLookupPaths = function(request, parent) {
|
|||
|
||||
var start = request.substring(0, 2);
|
||||
if (start !== './' && start !== '..') {
|
||||
return [request, Module._paths];
|
||||
var paths = Module._paths;
|
||||
if (parent) paths = paths.concat(parent.paths);
|
||||
return [request, paths];
|
||||
}
|
||||
|
||||
// with --eval, parent.id is not set and parent.filename is null
|
||||
if (!parent || !parent.id || !parent.filename) {
|
||||
// make require('./path/to/foo') work - normally the path is taken
|
||||
// from realpath(__filename) but with eval there is no filename
|
||||
return [request, ['.'].concat(Module._paths)];
|
||||
var mainPaths = ['.'].concat(Module._paths);
|
||||
mainPaths = mainPaths.concat(Module._nodeModulePaths('.'));
|
||||
return [request, mainPaths];
|
||||
}
|
||||
|
||||
// Is the parent an index module?
|
||||
|
@ -268,6 +301,7 @@ Module.prototype.load = function(filename) {
|
|||
|
||||
assert(!this.loaded);
|
||||
this.filename = filename;
|
||||
this.paths = Module._nodeModulePaths(path.dirname(filename));
|
||||
|
||||
var extension = path.extname(filename) || '.js';
|
||||
if (!Module._extensions[extension]) extension = '.js';
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
console.error(__filename);
|
||||
console.error(module.paths.join('\n')+'\n');
|
||||
throw new Error('Should not ever get here.');
|
|
@ -0,0 +1,2 @@
|
|||
console.error(__filename);
|
||||
console.error(module.paths.join('\n')+'\n');
|
|
@ -0,0 +1,13 @@
|
|||
console.error(__filename);
|
||||
console.error(module.paths.join('\n')+'\n');
|
||||
// this should work, and get the one that doesn't throw
|
||||
require('bar');
|
||||
|
||||
// since this is inside a node_modules folder,
|
||||
// it should be impossible to ever see /node_modules in the
|
||||
// lookup paths, since it's rooted on the uppermost node_modules
|
||||
// directory.
|
||||
require('assert').equal(-1, module.paths.indexOf('/node_modules'));
|
||||
|
||||
// this should work, and get the one in ./node_modules/asdf.js
|
||||
require('asdf');
|
|
@ -0,0 +1,2 @@
|
|||
console.error(__filename);
|
||||
console.error(module.paths.join('\n')+'\n');
|
|
@ -0,0 +1,3 @@
|
|||
console.error(__filename);
|
||||
console.error(module.paths.join('\n')+'\n');
|
||||
require('baz');
|
|
@ -0,0 +1,3 @@
|
|||
console.error(__filename);
|
||||
console.error(module.paths.join('\n')+'\n');
|
||||
throw new Error('Should not ever get here.');
|
|
@ -77,6 +77,11 @@ var root = require('../fixtures/cycles/root'),
|
|||
assert.equal(root.foo, foo);
|
||||
assert.equal(root.sayHello(), root.hello);
|
||||
|
||||
common.debug('test node_modules folders');
|
||||
// asserts are in the fixtures files themselves,
|
||||
// since they depend on the folder structure.
|
||||
require('../fixtures/node_modules/foo');
|
||||
|
||||
common.debug('test name clashes');
|
||||
// this one exists and should import the local module
|
||||
var my_path = require('./path');
|
||||
|
|
Loading…
Reference in New Issue