Skip to content

Commit 0cf3b33

Browse files
committed
feat: make unwrapSymlink pluggable
1 parent edfe3f7 commit 0cf3b33

File tree

5 files changed

+72
-22
lines changed

5 files changed

+72
-22
lines changed

lib/async.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ var defaultIsDir = function isDirectory(dir, cb) {
2525
});
2626
};
2727

28-
var maybeUnwrapSymlink = function maybeUnwrapSymlink(x, opts, cb) {
28+
var defaultUnwrapSymlink = function unwrapSymlink(x, cb) {
29+
fs.realpath(x, function (realPathErr, realPath) {
30+
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
31+
else cb(null, realPathErr ? x : realPath);
32+
});
33+
};
34+
35+
var maybeUnwrapSymlink = function maybeUnwrapSymlink(unwrap, x, opts, cb) {
2936
if (!opts || !opts.preserveSymlinks) {
30-
fs.realpath(x, function (realPathErr, realPath) {
31-
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
32-
else cb(null, realPathErr ? x : realPath);
33-
});
37+
unwrap(x, cb);
3438
} else {
3539
cb(null, x);
3640
}
@@ -63,6 +67,7 @@ module.exports = function resolve(x, options, callback) {
6367
var isFile = opts.isFile || defaultIsFile;
6468
var isDirectory = opts.isDirectory || defaultIsDir;
6569
var readFile = opts.readFile || fs.readFile;
70+
var unwrapSymlink = opts.unwrapSymlink || defaultUnwrapSymlink;
6671
var packageIterator = opts.packageIterator;
6772

6873
var extensions = opts.extensions || ['.js'];
@@ -75,6 +80,7 @@ module.exports = function resolve(x, options, callback) {
7580
var absoluteStart = path.resolve(basedir);
7681

7782
maybeUnwrapSymlink(
83+
unwrapSymlink,
7884
absoluteStart,
7985
opts,
8086
function (err, realStart) {
@@ -110,7 +116,7 @@ module.exports = function resolve(x, options, callback) {
110116
} else loadNodeModules(x, basedir, function (err, n, pkg) {
111117
if (err) cb(err);
112118
else if (n) {
113-
return maybeUnwrapSymlink(n, opts, function (err, realN) {
119+
return maybeUnwrapSymlink(unwrapSymlink, n, opts, function (err, realN) {
114120
if (err) {
115121
cb(err);
116122
} else {
@@ -131,7 +137,7 @@ module.exports = function resolve(x, options, callback) {
131137
else loadAsDirectory(res, function (err, d, pkg) {
132138
if (err) cb(err);
133139
else if (d) {
134-
maybeUnwrapSymlink(d, opts, function (err, realD) {
140+
maybeUnwrapSymlink(unwrapSymlink, d, opts, function (err, realD) {
135141
if (err) {
136142
cb(err);
137143
} else {
@@ -195,7 +201,7 @@ module.exports = function resolve(x, options, callback) {
195201
}
196202
if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null);
197203

198-
maybeUnwrapSymlink(dir, opts, function (unwrapErr, pkgdir) {
204+
maybeUnwrapSymlink(unwrapSymlink, dir, opts, function (unwrapErr, pkgdir) {
199205
if (unwrapErr) return loadpkg(path.dirname(dir), cb);
200206
var pkgfile = path.join(pkgdir, 'package.json');
201207
isFile(pkgfile, function (err, ex) {
@@ -223,7 +229,7 @@ module.exports = function resolve(x, options, callback) {
223229
fpkg = opts.package;
224230
}
225231

226-
maybeUnwrapSymlink(x, opts, function (unwrapErr, pkgdir) {
232+
maybeUnwrapSymlink(unwrapSymlink, x, opts, function (unwrapErr, pkgdir) {
227233
if (unwrapErr) return loadAsDirectory(path.dirname(x), fpkg, cb);
228234
var pkgfile = path.join(pkgdir, 'package.json');
229235
isFile(pkgfile, function (err, ex) {

lib/sync.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,24 @@ var defaultIsDir = function isDirectory(dir) {
2525
return stat.isDirectory();
2626
};
2727

28-
var maybeUnwrapSymlink = function maybeUnwrapSymlink(x, opts) {
29-
if (!opts || !opts.preserveSymlinks) {
30-
try {
31-
return fs.realpathSync(x);
32-
} catch (realPathErr) {
33-
if (realPathErr.code !== 'ENOENT') {
34-
throw realPathErr;
35-
}
28+
var defaultUnwrapSymlink = function unwrapSymlink(x) {
29+
try {
30+
return fs.realpathSync(x);
31+
} catch (realPathErr) {
32+
if (realPathErr.code !== 'ENOENT') {
33+
throw realPathErr;
3634
}
3735
}
3836
return x;
3937
};
4038

39+
var maybeUnwrapSymlink = function maybeUnwrapSymlink(unwrap, x, opts) {
40+
if (!opts || !opts.preserveSymlinks) {
41+
return unwrap(x);
42+
}
43+
return x;
44+
};
45+
4146
var getPackageCandidates = function getPackageCandidates(x, start, opts) {
4247
var dirs = nodeModulesPaths(start, opts, x);
4348
for (var i = 0; i < dirs.length; i++) {
@@ -55,6 +60,7 @@ module.exports = function resolveSync(x, options) {
5560
var isFile = opts.isFile || defaultIsFile;
5661
var isDirectory = opts.isDirectory || defaultIsDir;
5762
var readFileSync = opts.readFileSync || fs.readFileSync;
63+
var unwrapSymlink = opts.unwrapSymlink || defaultUnwrapSymlink;
5864
var packageIterator = opts.packageIterator;
5965

6066
var extensions = opts.extensions || ['.js'];
@@ -64,7 +70,7 @@ module.exports = function resolveSync(x, options) {
6470
opts.paths = opts.paths || [];
6571

6672
// ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
67-
var absoluteStart = maybeUnwrapSymlink(path.resolve(basedir), opts);
73+
var absoluteStart = maybeUnwrapSymlink(unwrapSymlink, path.resolve(basedir), opts);
6874

6975
if (opts.basedir && !isDirectory(absoluteStart)) {
7076
var dirError = new TypeError('Provided basedir "' + opts.basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory'));
@@ -76,12 +82,12 @@ module.exports = function resolveSync(x, options) {
7682
var res = path.resolve(absoluteStart, x);
7783
if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
7884
var m = loadAsFileSync(res) || loadAsDirectorySync(res);
79-
if (m) return maybeUnwrapSymlink(m, opts);
85+
if (m) return maybeUnwrapSymlink(unwrapSymlink, m, opts);
8086
} else if (isCore(x)) {
8187
return x;
8288
} else {
8389
var n = loadNodeModulesSync(x, absoluteStart);
84-
if (n) return maybeUnwrapSymlink(n, opts);
90+
if (n) return maybeUnwrapSymlink(unwrapSymlink, n, opts);
8591
}
8692

8793
var err = new Error("Cannot find module '" + x + "' from '" + parent + "'");
@@ -118,7 +124,7 @@ module.exports = function resolveSync(x, options) {
118124
}
119125
if ((/[/\\]node_modules[/\\]*$/).test(dir)) return;
120126

121-
var pkgfile = path.join(isDirectory(dir) ? maybeUnwrapSymlink(dir, opts) : dir, 'package.json');
127+
var pkgfile = path.join(isDirectory(dir) ? maybeUnwrapSymlink(unwrapSymlink, dir, opts) : dir, 'package.json');
122128

123129
if (!isFile(pkgfile)) {
124130
return loadpkg(path.dirname(dir));
@@ -138,7 +144,7 @@ module.exports = function resolveSync(x, options) {
138144
}
139145

140146
function loadAsDirectorySync(x) {
141-
var pkgfile = path.join(isDirectory(x) ? maybeUnwrapSymlink(x, opts) : x, '/package.json');
147+
var pkgfile = path.join(isDirectory(x) ? maybeUnwrapSymlink(unwrapSymlink, x, opts) : x, '/package.json');
142148
if (isFile(pkgfile)) {
143149
try {
144150
var body = readFileSync(pkgfile, 'UTF8');

readme.markdown

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ options are:
6161

6262
* opts.isDirectory - function to asynchronously test whether a file exists and is a directory
6363

64+
* opts.unwrapSymlink - function to asynchronously unwrap a potential symlink
65+
6466
* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field
6567
* pkg - package data
6668
* pkgfile - path to package.json
@@ -117,6 +119,12 @@ default `opts` values:
117119
return cb(err);
118120
});
119121
},
122+
unwrapSymlink: function unwrapSymlink(file, cb) {
123+
fs.realpath(file, function (realPathErr, realPath) {
124+
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
125+
else cb(null, realPathErr ? file : realPath);
126+
});
127+
},
120128
moduleDirectory: 'node_modules',
121129
preserveSymlinks: false
122130
}
@@ -139,6 +147,8 @@ options are:
139147

140148
* opts.isDirectory - function to synchronously test whether a file exists and is a directory
141149

150+
* opts.unwrapSymlink - function to synchronously unwrap a potential symlink
151+
142152
* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field
143153
* pkg - package data
144154
* pkgfile - path to package.json
@@ -195,6 +205,16 @@ default `opts` values:
195205
}
196206
return stat.isDirectory();
197207
},
208+
defaultUnwrapSymlink: function unwrapSymlink(file) {
209+
try {
210+
return fs.realpathSync(file);
211+
} catch (realPathErr) {
212+
if (realPathErr.code !== 'ENOENT') {
213+
throw realPathErr;
214+
}
215+
}
216+
return file;
217+
},
198218
moduleDirectory: 'node_modules',
199219
preserveSymlinks: false
200220
}

test/mock.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ test('mock', function (t) {
2222
},
2323
readFile: function (file, cb) {
2424
cb(null, files[path.resolve(file)]);
25+
},
26+
unwrapSymlink: function (file, cb) {
27+
cb(null, file);
2528
}
2629
};
2730
}
@@ -70,6 +73,9 @@ test('mock from package', function (t) {
7073
'package': { main: 'bar' },
7174
readFile: function (file, cb) {
7275
cb(null, files[file]);
76+
},
77+
unwrapSymlink: function (file, cb) {
78+
cb(null, file);
7379
}
7480
};
7581
}
@@ -121,6 +127,9 @@ test('mock package', function (t) {
121127
},
122128
readFile: function (file, cb) {
123129
cb(null, files[path.resolve(file)]);
130+
},
131+
unwrapSymlink: function (file, cb) {
132+
cb(null, file);
124133
}
125134
};
126135
}
@@ -157,6 +166,9 @@ test('mock package from package', function (t) {
157166
'package': { main: 'bar' },
158167
readFile: function (file, cb) {
159168
cb(null, files[path.resolve(file)]);
169+
},
170+
unwrapSymlink: function (file, cb) {
171+
cb(null, file);
160172
}
161173
};
162174
}

test/mock_sync.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ test('mock', function (t) {
2323
},
2424
readFileSync: function (file) {
2525
return files[path.resolve(file)];
26+
},
27+
unwrapSymlink: function (file) {
28+
return file;
2629
}
2730
};
2831
}
@@ -70,6 +73,9 @@ test('mock package', function (t) {
7073
},
7174
readFileSync: function (file) {
7275
return files[path.resolve(file)];
76+
},
77+
unwrapSymlink: function (file) {
78+
return file;
7379
}
7480
};
7581
}

0 commit comments

Comments
 (0)