Skip to content

Commit d7f9908

Browse files
lhoriertsao
andauthored
Resolve symlinked modules correctly (#92)
Some systems (e.g. rush, pnpm) use symlinks to create a recursive dependency graph instead of relying on the hoisting aspect of the node module resolution algorithm (like npm and yarn do) in these cases, we want to resolve to the real path of a module, so that the tree structure below only ever tries to run the `x` module once (rather than once on each module that depends on it) - node_modules - a - node_modules - symlink to x - b - node_modules - symlink to x Co-authored-by: Ryan Tsao <[email protected]>
1 parent 427bb88 commit d7f9908

File tree

8 files changed

+193
-7
lines changed

8 files changed

+193
-7
lines changed

.travis.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
language: node_js
22
node_js:
3-
- "0.8"
4-
- "0.10"
5-
- "4"
6-
- "5"
7-
- "6"
3+
- "8"
4+
- "10"
5+
- "12"

index.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ function build_resolve_opts(opts, base) {
206206
return opts;
207207
}
208208

209-
function resolve(id, opts, cb) {
209+
function resolve(id, opts, callback) {
210210

211211
// opts.filename
212212
// opts.paths
@@ -216,6 +216,24 @@ function resolve(id, opts, cb) {
216216
opts = opts || {};
217217
opts.filename = opts.filename || '';
218218

219+
var cb = function(err, path, pkg) {
220+
fs.stat(path, function(notPath) {
221+
if (notPath) {
222+
callback(err, path, pkg);
223+
}
224+
else {
225+
fs.realpath(path, function(notReal, real) {
226+
if (notReal) {
227+
callback(err, path, pkg);
228+
}
229+
else {
230+
callback(err, real, pkg);
231+
}
232+
});
233+
}
234+
});
235+
}
236+
219237
var base = path.dirname(opts.filename);
220238

221239
if (opts.basedir) {

package-lock.json

Lines changed: 136 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"empty.js"
99
],
1010
"scripts": {
11-
"test": "mocha --reporter list test/*.js"
11+
"test": "node scripts/setup-symlinks.js && mocha --reporter list test/*.js"
1212
},
1313
"repository": {
1414
"type": "git",

scripts/setup-symlinks.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
var fs = require('fs');
2+
3+
try {
4+
fs.mkdirSync(__dirname + '/../test/fixtures/node_modules/linker/node_modules');
5+
} catch (e) {}
6+
process.chdir(__dirname + '/../test/fixtures/node_modules/linker/node_modules');
7+
try {
8+
fs.unlinkSync('linked');
9+
} catch (e) {}
10+
fs.symlinkSync('../../../linked', 'linked', 'dir');

test/fixtures/linked/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// dummy

test/fixtures/node_modules/linker/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.

test/modules.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,25 @@ test('not fail on accessing path name defined in Object.prototype', function (do
319319
done();
320320
});
321321
});
322+
323+
test('respect symlinks', function (done) {
324+
// some systems (e.g. rush, pnpm) use symlinks to create a recursive dependency graph
325+
// instead of relying on the hoisting aspect of the node module resolution algorithm (like npm and yarn do)
326+
// in these cases, we want to resolve to the real path of a module, so that the tree structure below
327+
// only ever tries to run the `x` module once (rather than once on each module that depends on it)
328+
//
329+
// - node_modules
330+
// - a
331+
// - node_modules
332+
// - symlink to x
333+
// - b
334+
// - node_modules
335+
// - symlink to x
336+
//
337+
resolve('linked', { paths: [ fixtures_dir + '/linker/node_modules' ], package: { main: 'fixtures' } }, function(err, path, pkg) {
338+
assert.ifError(err);
339+
assert.equal(path, require.resolve('./fixtures/linked/index'));
340+
assert.strictEqual(pkg, undefined);
341+
done();
342+
});
343+
});

0 commit comments

Comments
 (0)