module: fix wrong condition in early return logic for node_module path
Checklist
-
tests and code linting passes -
a test and/or benchmark is included -
the commit message follows commit guidelines
Affected core subsystem(s)
module
Description of change
the p < nmLen
condition will fail when a module's name is end with
node_modules
like foo_node_modules
. The old logic will miss the
foo_node_modules/node_modules
in node_modules paths.
TL;TR, a module named like foo_node_modules
can't require any module
in his node_modules folder.
Here is a demo which compared the new nodeModulePaths with the old one.
const path = require('path');
var nmChars = [115, 101, 108, 117, 100, 111, 109, 95, 101, 100, 111, 110];
var nmLen = nmChars.length;
function nodeModulePaths(from) {
// guarantee that 'from' is absolute.
from = path.resolve(from);
// Return early not only to avoid unnecessary work, but to *avoid* returning
// an array of two items for a root: [ '//node_modules', '/node_modules' ]
if (from === '/')
return ['/node_modules'];
// 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.
const paths = [];
var p = 0;
var last = from.length;
for (var i = from.length - 1; i >= 0; --i) {
const code = from.charCodeAt(i);
if (code === 47 /*/*/ ) {
if (p !== nmLen)
paths.push(from.slice(0, last) + '/node_modules');
last = i;
p = 0;
}
else if (p !== -1 && p < nmLen) {
if (nmChars[p] === code) {
++p;
}
else {
p = -1;
}
}
}
return paths;
};
const splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//;
nodeModulePathsOld = 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 paths = [];
var parts = from.split(splitRe);
for (var tip = parts.length - 1; tip >= 0; 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(path.sep);
paths.push(dir);
}
return paths;
}
console.log('new:', nodeModulePaths('/Users/hefangshi/test/foo_node_modules'));
console.log('old:', nodeModulePathsOld('/Users/hefangshi/test/foo_node_modules'));
Output:
new: [ '/Users/hefangshi/test/node_modules',
'/Users/hefangshi/node_modules',
'/Users/node_modules' ]
old: [ '/Users/hefangshi/test/foo_node_modules/node_modules',
'/Users/hefangshi/test/node_modules',
'/Users/hefangshi/node_modules',
'/Users/node_modules',
'/node_modules' ]
The main reason is the p < nmLen
condition will ignore foo_node_modules
and simply
think it should be /node_modules
, and won't search /Users/hefangshi/test/foo_node_modules/node_modules
for dependency.
I already tested in mac and windows, but the testcases were not very full edge tested.
@mscdex please have a look on it.