Skip to content

Commit fed3f0f

Browse files
zkochanljharb
authored andcommitted
[New]: add preserveSymlinks option
Add the possibility to make `resolve` use real paths during resolution. Fixes #130.
1 parent 703517b commit fed3f0f

File tree

7 files changed

+81
-2
lines changed

7 files changed

+81
-2
lines changed

lib/node-modules-paths.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var path = require('path');
2+
var fs = require('fs');
23
var parse = path.parse || require('path-parse');
34

45
module.exports = function nodeModulesPaths(start, opts) {
@@ -10,6 +11,16 @@ module.exports = function nodeModulesPaths(start, opts) {
1011
// resolving against the process' current working directory
1112
var absoluteStart = path.resolve(start);
1213

14+
if (opts && opts.preserveSymlinks === false) {
15+
try {
16+
absoluteStart = fs.realpathSync(absoluteStart);
17+
} catch (err) {
18+
if (err.code !== 'ENOENT') {
19+
throw err;
20+
}
21+
}
22+
}
23+
1324
var prefix = '/';
1425
if (/^([A-Za-z]:)/.test(absoluteStart)) {
1526
prefix = '';

readme.markdown

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ node_modules recursive walk (probably don't use this)
7373

7474
* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"`
7575

76+
* opts.preserveSymlinks - if true, doesn't resolve `basedir` to real path before resolving.
77+
This is the way Node resolves dependencies when executed with the [--preserve-symlinks](https://nodejs.org/api/all.html#cli_preserve_symlinks) flag.
78+
**Note:** this property is currently `true` by default but it will be changed to
79+
`false` in the next major version because *Node's resolution algorithm does not preserve symlinks by default*.
80+
7681
default `opts` values:
7782

7883
``` javascript
@@ -88,7 +93,8 @@ default `opts` values:
8893
else cb(null, stat.isFile())
8994
});
9095
},
91-
moduleDirectory: 'node_modules'
96+
moduleDirectory: 'node_modules',
97+
preserveSymlinks: true
9298
}
9399
```
94100

@@ -115,6 +121,11 @@ node_modules recursive walk (probably don't use this)
115121

116122
* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"`
117123

124+
* opts.preserveSymlinks - if true, doesn't resolve `basedir` to real path before resolving.
125+
This is the way Node resolves dependencies when executed with the [--preserve-symlinks](https://nodejs.org/api/all.html#cli_preserve_symlinks) flag.
126+
**Note:** this property is currently `true` by default but it will be changed to
127+
`false` in the next major version because *Node's resolution algorithm does not preserve symlinks by default*.
128+
118129
default `opts` values:
119130

120131
``` javascript
@@ -127,7 +138,8 @@ default `opts` values:
127138
try { return fs.statSync(file).isFile() }
128139
catch (e) { return false }
129140
},
130-
moduleDirectory: 'node_modules'
141+
moduleDirectory: 'node_modules',
142+
preserveSymlinks: true
131143
}
132144
````
133145

test/resolver/symlinked/.gitignore

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

test/resolver/symlinked/_/.gitignore

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

test/resolver/symlinked/_/node_modules/foo.js

Whitespace-only changes.

test/resolver/symlinked/_/symlink_target/.gitkeep

Whitespace-only changes.

test/symlinks.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
var path = require('path');
2+
var fs = require('fs');
3+
var test = require('tape');
4+
var resolve = require('../');
5+
6+
var symlinkDir = path.join(__dirname, 'resolver', 'symlinked', 'symlink');
7+
try {
8+
fs.unlinkSync(symlinkDir);
9+
} catch (err) {}
10+
try {
11+
fs.symlinkSync('./_/symlink_target', symlinkDir, 'dir');
12+
} catch (err) {
13+
// if fails then it is probably on Windows and lets try to create a junction
14+
fs.symlinkSync(path.join(__dirname, 'resolver', 'symlinked', '_', 'symlink_target') + '\\', symlinkDir, 'junction');
15+
}
16+
17+
test('symlink', function (t) {
18+
t.plan(1);
19+
20+
resolve('foo', { basedir: symlinkDir, preserveSymlinks: false }, function (err, res, pkg) {
21+
if (err) t.fail(err);
22+
t.equal(res, path.join(__dirname, 'resolver', 'symlinked', '_', 'node_modules', 'foo.js'));
23+
});
24+
});
25+
26+
test('sync symlink when preserveSymlinks = true', function (t) {
27+
t.plan(4);
28+
29+
resolve('foo', { basedir: symlinkDir }, function (err, res, pkg) {
30+
t.ok(err, 'there is an error');
31+
t.notOk(res, 'no result');
32+
33+
t.equal(err && err.code, 'MODULE_NOT_FOUND', 'error code matches require.resolve');
34+
t.equal(
35+
err && err.message,
36+
'Cannot find module \'foo\' from \'' + symlinkDir + '\'',
37+
'can not find nonexistent module'
38+
);
39+
});
40+
});
41+
42+
test('sync symlink', function (t) {
43+
var start = new Date();
44+
t.equal(resolve.sync('foo', { basedir: symlinkDir, preserveSymlinks: false }), path.join(__dirname, 'resolver', 'symlinked', '_', 'node_modules', 'foo.js'));
45+
t.ok(new Date() - start < 50, 'resolve.sync timedout');
46+
t.end();
47+
});
48+
49+
test('sync symlink when preserveSymlinks = true', function (t) {
50+
t.throws(function () {
51+
resolve.sync('foo', { basedir: symlinkDir });
52+
}, /Cannot find module 'foo'/);
53+
t.end();
54+
});

0 commit comments

Comments
 (0)