Skip to content

Commit 2321cd4

Browse files
committed
[Fix] sync/async: fix preserveSymlinks option
Fixes #177.
1 parent 86a8293 commit 2321cd4

File tree

16 files changed

+148
-34
lines changed

16 files changed

+148
-34
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# gitignore
22
node_modules
3+
**/node_modules
34

45
# Only apps should have lockfiles
56
npm-shrinkwrap.json

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ matrix:
3535
include:
3636
- node_js: "lts/*"
3737
env: PRETEST=true
38+
- node_js: "lts/*"
39+
env: POSTTEST=true
3840
- node_js: "11.3"
3941
env: TEST=true ALLOW_FAILURE=true
4042
- node_js: "11.2"

lib/async.js

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,34 @@ module.exports = function resolve(x, options, callback) {
5151

5252
opts.paths = opts.paths || [];
5353

54-
if (opts.basedir) {
55-
var basedirError = new TypeError('Provided basedir "' + opts.basedir + '" is not a directory');
56-
isDirectory(opts.basedir, function (err, result) {
57-
if (err) return cb(err);
58-
if (!result) { return cb(basedirError); }
59-
validBasedir();
54+
// ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
55+
var absoluteStart = path.resolve(basedir);
56+
57+
if (!opts || !opts.preserveSymlinks) {
58+
fs.realpath(absoluteStart, function (realPathErr, realStart) {
59+
if (realPathErr && realPathErr.code !== 'ENOENT') cb(err);
60+
else validateBasedir(realPathErr ? absoluteStart : realStart);
6061
});
6162
} else {
62-
validBasedir();
63+
validateBasedir(absoluteStart);
64+
}
65+
66+
function validateBasedir(basedir) {
67+
if (opts.basedir) {
68+
var dirError = new TypeError('Provided basedir "' + basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory'));
69+
dirError.code = 'INVALID_BASEDIR';
70+
isDirectory(basedir, function (err, result) {
71+
if (err) return cb(err);
72+
if (!result) { return cb(dirError); }
73+
validBasedir(basedir);
74+
});
75+
} else {
76+
validBasedir(basedir);
77+
}
6378
}
79+
6480
var res;
65-
function validBasedir() {
81+
function validBasedir(basedir) {
6682
if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) {
6783
res = path.resolve(basedir, x);
6884
if (x === '..' || x.slice(-1) === '/') res += '/';

lib/node-modules-paths.js

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

54
var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) {
@@ -29,28 +28,15 @@ module.exports = function nodeModulesPaths(start, opts, request) {
2928
? [].concat(opts.moduleDirectory)
3029
: ['node_modules'];
3130

32-
// ensure that `start` is an absolute path at this point, resolving against the process' current working directory
33-
var absoluteStart = path.resolve(start);
34-
35-
if (!opts || !opts.preserveSymlinks) {
36-
try {
37-
absoluteStart = fs.realpathSync(absoluteStart);
38-
} catch (err) {
39-
if (err.code !== 'ENOENT') {
40-
throw err;
41-
}
42-
}
43-
}
44-
4531
if (opts && typeof opts.paths === 'function') {
4632
return opts.paths(
4733
request,
48-
absoluteStart,
49-
function () { return getNodeModulesDirs(absoluteStart, modules); },
34+
start,
35+
function () { return getNodeModulesDirs(start, modules); },
5036
opts
5137
);
5238
}
5339

54-
var dirs = getNodeModulesDirs(absoluteStart, modules);
40+
var dirs = getNodeModulesDirs(start, modules);
5541
return opts && opts.paths ? dirs.concat(opts.paths) : dirs;
5642
};

lib/sync.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,34 @@ module.exports = function (x, options) {
4141

4242
opts.paths = opts.paths || [];
4343

44-
if (opts.basedir && !isDirectory(opts.basedir)) {
45-
throw new TypeError('Provided basedir "' + opts.basedir + '" is not a directory');
44+
// ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
45+
var absoluteStart = path.resolve(basedir);
46+
47+
if (!opts.preserveSymlinks) {
48+
try {
49+
absoluteStart = fs.realpathSync(absoluteStart);
50+
} catch (realPathErr) {
51+
if (realPathErr.code !== 'ENOENT') {
52+
throw realPathErr;
53+
}
54+
}
55+
}
56+
57+
if (opts.basedir && !isDirectory(absoluteStart)) {
58+
var dirError = new TypeError('Provided basedir "' + opts.basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory'));
59+
dirError.code = 'INVALID_BASEDIR';
60+
throw dirError;
4661
}
4762

4863
if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) {
49-
var res = path.resolve(basedir, x);
64+
var res = path.resolve(absoluteStart, x);
5065
if (x === '..' || x.slice(-1) === '/') res += '/';
5166
var m = loadAsFileSync(res) || loadAsDirectorySync(res);
5267
if (m) return m;
5368
} else if (core[x]) {
5469
return x;
5570
} else {
56-
var n = loadNodeModulesSync(x, basedir);
71+
var n = loadNodeModulesSync(x, absoluteStart);
5772
if (n) return n;
5873
}
5974

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
"lint": "eslint .",
1919
"tests-only": "tape test/*.js",
2020
"pretest": "npm run lint",
21-
"test": "npm run --silent tests-only"
21+
"test": "npm run --silent tests-only",
22+
"posttest": "npm run test:multirepo",
23+
"test:multirepo": "cd ./test/resolver/multirepo && npm install && npm test"
2224
},
2325
"devDependencies": {
2426
"@ljharb/eslint-config": "^13.0.0",

test/node_path.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ test('$NODE_PATH', function (t) {
6060
paths: [
6161
'node_path'
6262
],
63-
basedir: 'node_path/x',
63+
basedir: path.join(__dirname, 'node_path/x'),
6464
isDirectory: isDir
6565
}, function (err, res) {
6666
var root = require('tap/package.json').main;

test/resolver.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,15 +353,16 @@ test('async dot slash main', function (t) {
353353
});
354354

355355
test('not a directory', function (t) {
356-
t.plan(5);
356+
t.plan(6);
357357
var path = './foo';
358358
resolve(path, { basedir: __filename }, function (err, res, pkg) {
359359
t.ok(err, 'a non-directory errors');
360360
t.equal(arguments.length, 1);
361361
t.equal(res, undefined);
362362
t.equal(pkg, undefined);
363363

364-
t.equal(err && err.message, 'Provided basedir "' + __filename + '" is not a directory');
364+
t.equal(err && err.message, 'Provided basedir "' + __filename + '" is not a directory, or a symlink to a directory');
365+
t.equal(err && err.code, 'INVALID_BASEDIR');
365366
});
366367
});
367368

test/resolver/multirepo/.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

test/resolver/multirepo/lerna.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"packages": [
3+
"packages/*"
4+
],
5+
"version": "0.0.0"
6+
}

0 commit comments

Comments
 (0)