Skip to content

Commit 6564aae

Browse files
module: resolve _-prefixed dependencies to real path
Since #5950, `require('some-symlink')` no longer resolves the module to its real path, but instead uses the path of the symlink itself as a cache key. This change allows users to resolve dependencies to their real path when required using the _-prefix. Example using old mechanism (pre v6.0.0): --- node_modules/_real-module -> ../real-module real-modules/index.js require('./real_module') === require('real_module') Example using new mechanism (post v6.0.0): --- node_modules/real-module -> ../real-module real-modules/index.js require('./real_module') !== require('real_module') As discussed in #3402
1 parent 3cdb506 commit 6564aae

File tree

5 files changed

+83
-39
lines changed

5 files changed

+83
-39
lines changed

lib/module.js

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,30 @@ function tryPackage(requestPath, exts, isMain) {
103103
if (!pkg) return false;
104104

105105
var filename = path.resolve(requestPath, pkg);
106+
return readPackagePath(filename, exts, isMain);
107+
}
108+
109+
function readPackagePath(filename, exts, isMain) {
106110
return tryFile(filename, isMain) ||
107111
tryExtensions(filename, exts, isMain) ||
108112
tryExtensions(path.resolve(filename, 'index'), exts, isMain);
109113
}
110114

115+
function readFilePath(requestPath, isMain) {
116+
if (isMain) {
117+
return fs.realpathSync(requestPath);
118+
}
119+
return path.resolve(requestPath);
120+
}
121+
111122
// check if the file exists and is not a directory
112123
// resolve to the absolute realpath if running main module,
113124
// otherwise resolve to absolute while keeping symlinks intact.
114125
function tryFile(requestPath, isMain) {
115126
const rc = stat(requestPath);
116-
if (isMain) {
117-
return rc === 0 && fs.realpathSync(requestPath);
127+
if (rc === 0) {
128+
return readFilePath(requestPath, isMain);
118129
}
119-
return rc === 0 && path.resolve(requestPath);
120130
}
121131

122132
// given a path check a the file exists with any of the set extensions
@@ -131,6 +141,34 @@ function tryExtensions(p, exts, isMain) {
131141
return false;
132142
}
133143

144+
function tryFindPath(basePath, exts, isMain, isAbsolute) {
145+
var filename;
146+
147+
if (isAbsolute) {
148+
const rc = stat(basePath);
149+
if (rc === 0) { // File.
150+
filename = readFilePath(basePath, isMain);
151+
} else if (rc === 1) { // Directory.
152+
filename = tryPackage(basePath, exts, isMain);
153+
}
154+
155+
if (!filename) {
156+
filename = tryExtensions(basePath, exts, isMain);
157+
}
158+
}
159+
160+
if (!filename) {
161+
filename = tryPackage(basePath, exts, isMain);
162+
}
163+
164+
if (!filename) {
165+
// try it with each of the extensions at "index"
166+
filename = tryExtensions(path.resolve(basePath, 'index'), exts, isMain);
167+
}
168+
169+
return filename;
170+
}
171+
134172
var warned = false;
135173
Module._findPath = function(request, paths, isMain) {
136174
if (path.isAbsolute(request)) {
@@ -144,52 +182,24 @@ Module._findPath = function(request, paths, isMain) {
144182
return Module._pathCache[cacheKey];
145183
}
146184

147-
var exts;
185+
const exts = Object.keys(Module._extensions);
148186
const trailingSlash = request.length > 0 &&
149187
request.charCodeAt(request.length - 1) === 47/*/*/;
188+
const isAbsolute = !trailingSlash;
150189

151190
// For each path
152191
for (var i = 0; i < paths.length; i++) {
153192
// Don't search further if path doesn't exist
154193
const curPath = paths[i];
155194
if (curPath && stat(curPath) < 1) continue;
156-
var basePath = path.resolve(curPath, request);
157-
var filename;
158-
159-
if (!trailingSlash) {
160-
const rc = stat(basePath);
161-
if (rc === 0) { // File.
162-
if (!isMain) {
163-
filename = path.resolve(basePath);
164-
} else {
165-
filename = fs.realpathSync(basePath);
166-
}
167-
} else if (rc === 1) { // Directory.
168-
if (exts === undefined)
169-
exts = Object.keys(Module._extensions);
170-
filename = tryPackage(basePath, exts, isMain);
171-
}
172195

173-
if (!filename) {
174-
// try it with each of the extensions
175-
if (exts === undefined)
176-
exts = Object.keys(Module._extensions);
177-
filename = tryExtensions(basePath, exts, isMain);
178-
}
179-
}
196+
// If _basePath is a symlink, use the real path rather than the path of the
197+
// symlink as cache key.
198+
const _basePath = path.resolve(curPath, '_' + request);
199+
const basePath = path.resolve(curPath, request);
180200

181-
if (!filename) {
182-
if (exts === undefined)
183-
exts = Object.keys(Module._extensions);
184-
filename = tryPackage(basePath, exts, isMain);
185-
}
186-
187-
if (!filename) {
188-
// try it with each of the extensions at "index"
189-
if (exts === undefined)
190-
exts = Object.keys(Module._extensions);
191-
filename = tryExtensions(path.resolve(basePath, 'index'), exts, isMain);
192-
}
201+
const filename = tryFindPath(_basePath, exts, true, isAbsolute) ||
202+
tryFindPath(basePath, exts, isMain, isAbsolute);
193203

194204
if (filename) {
195205
// Warn once if '.' resolved outside the module dir
@@ -427,6 +437,7 @@ Module._resolveFilename = function(request, parent, isMain) {
427437
}
428438

429439
var resolvedModule = Module._resolveLookupPaths(request, parent);
440+
430441
var id = resolvedModule[0];
431442
var paths = resolvedModule[1];
432443

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exports.internalRequire = require;

test/fixtures/module-require-real-path/node_modules/real-module/index.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exports.dirname = __dirname;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const path = require('path');
5+
const fs = require('fs');
6+
7+
const realModuleSymlinkPath = path.join(common.fixturesDir,
8+
'/module-require-real-path/node_modules/_real-module');
9+
10+
const localRealModulePath = path.join(common.fixturesDir,
11+
'/module-require-real-path/real-module');
12+
13+
// module-require-real-path exports its require function. That way we can test
14+
// how modules get resolved **from** its.
15+
const _require = require(
16+
path.join(common.fixturesDir, '/module-require-real-path')
17+
).internalRequire;
18+
19+
process.on('exit', function() {
20+
fs.unlinkSync(realModuleSymlinkPath);
21+
});
22+
23+
fs.symlinkSync('../real-module', realModuleSymlinkPath);
24+
25+
assert.equal(_require('./real-module'), _require('real-module'));
26+
assert.equal(_require('real-module').dirname, localRealModulePath);
27+
28+
// When required directly with the _-prefix, resolve to path of symlink.
29+
assert.notEqual(_require('./real-module'), _require('_real-module'));
30+
assert.equal(_require('_real-module').dirname, realModuleSymlinkPath);

0 commit comments

Comments
 (0)