Skip to content

Commit 52038e1

Browse files
committed
feat: make unwrapSymlink pluggable
1 parent 535ec22 commit 52038e1

File tree

5 files changed

+74
-22
lines changed

5 files changed

+74
-22
lines changed

lib/async.js

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

30-
var maybeUnwrapSymlink = function maybeUnwrapSymlink(x, opts, cb) {
30+
var defaultUnwrapSymlink = function unwrapSymlink(x, cb) {
31+
realpath(x, function (realPathErr, realPath) {
32+
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
33+
else cb(null, realPathErr ? x : realPath);
34+
});
35+
};
36+
37+
var maybeUnwrapSymlink = function maybeUnwrapSymlink(unwrap, x, opts, cb) {
3138
if (!opts || !opts.preserveSymlinks) {
32-
realpath(x, function (realPathErr, realPath) {
33-
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
34-
else cb(null, realPathErr ? x : realPath);
35-
});
39+
unwrap(x, cb);
3640
} else {
3741
cb(null, x);
3842
}
@@ -65,6 +69,7 @@ module.exports = function resolve(x, options, callback) {
6569
var isFile = opts.isFile || defaultIsFile;
6670
var isDirectory = opts.isDirectory || defaultIsDir;
6771
var readFile = opts.readFile || fs.readFile;
72+
var unwrapSymlink = opts.unwrapSymlink || defaultUnwrapSymlink;
6873
var packageIterator = opts.packageIterator;
6974

7075
var extensions = opts.extensions || ['.js'];
@@ -77,6 +82,7 @@ module.exports = function resolve(x, options, callback) {
7782
var absoluteStart = path.resolve(basedir);
7883

7984
maybeUnwrapSymlink(
85+
unwrapSymlink,
8086
absoluteStart,
8187
opts,
8288
function (err, realStart) {
@@ -112,7 +118,7 @@ module.exports = function resolve(x, options, callback) {
112118
} else loadNodeModules(x, basedir, function (err, n, pkg) {
113119
if (err) cb(err);
114120
else if (n) {
115-
return maybeUnwrapSymlink(n, opts, function (err, realN) {
121+
return maybeUnwrapSymlink(unwrapSymlink, n, opts, function (err, realN) {
116122
if (err) {
117123
cb(err);
118124
} else {
@@ -133,7 +139,7 @@ module.exports = function resolve(x, options, callback) {
133139
else loadAsDirectory(res, function (err, d, pkg) {
134140
if (err) cb(err);
135141
else if (d) {
136-
maybeUnwrapSymlink(d, opts, function (err, realD) {
142+
maybeUnwrapSymlink(unwrapSymlink, d, opts, function (err, realD) {
137143
if (err) {
138144
cb(err);
139145
} else {
@@ -197,7 +203,7 @@ module.exports = function resolve(x, options, callback) {
197203
}
198204
if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null);
199205

200-
maybeUnwrapSymlink(dir, opts, function (unwrapErr, pkgdir) {
206+
maybeUnwrapSymlink(unwrapSymlink, dir, opts, function (unwrapErr, pkgdir) {
201207
if (unwrapErr) return loadpkg(path.dirname(dir), cb);
202208
var pkgfile = path.join(pkgdir, 'package.json');
203209
isFile(pkgfile, function (err, ex) {
@@ -225,7 +231,7 @@ module.exports = function resolve(x, options, callback) {
225231
fpkg = opts.package;
226232
}
227233

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

lib/sync.js

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

30-
var maybeUnwrapSymlink = function maybeUnwrapSymlink(x, opts) {
31-
if (!opts || !opts.preserveSymlinks) {
32-
try {
33-
return realpath(x);
34-
} catch (realPathErr) {
35-
if (realPathErr.code !== 'ENOENT') {
36-
throw realPathErr;
37-
}
30+
var defaultUnwrapSymlink = function unwrapSymlink(x) {
31+
try {
32+
return realpath(x);
33+
} catch (realPathErr) {
34+
if (realPathErr.code !== 'ENOENT') {
35+
throw realPathErr;
3836
}
3937
}
4038
return x;
4139
};
4240

41+
var maybeUnwrapSymlink = function maybeUnwrapSymlink(unwrap, x, opts) {
42+
if (!opts || !opts.preserveSymlinks) {
43+
return unwrap(x);
44+
}
45+
return x;
46+
};
47+
4348
var getPackageCandidates = function getPackageCandidates(x, start, opts) {
4449
var dirs = nodeModulesPaths(start, opts, x);
4550
for (var i = 0; i < dirs.length; i++) {
@@ -57,6 +62,7 @@ module.exports = function resolveSync(x, options) {
5762
var isFile = opts.isFile || defaultIsFile;
5863
var isDirectory = opts.isDirectory || defaultIsDir;
5964
var readFileSync = opts.readFileSync || fs.readFileSync;
65+
var unwrapSymlink = opts.unwrapSymlink || defaultUnwrapSymlink;
6066
var packageIterator = opts.packageIterator;
6167

6268
var extensions = opts.extensions || ['.js'];
@@ -66,7 +72,7 @@ module.exports = function resolveSync(x, options) {
6672
opts.paths = opts.paths || [];
6773

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

7177
if (opts.basedir && !isDirectory(absoluteStart)) {
7278
var dirError = new TypeError('Provided basedir "' + opts.basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory'));
@@ -78,12 +84,12 @@ module.exports = function resolveSync(x, options) {
7884
var res = path.resolve(absoluteStart, x);
7985
if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
8086
var m = loadAsFileSync(res) || loadAsDirectorySync(res);
81-
if (m) return maybeUnwrapSymlink(m, opts);
87+
if (m) return maybeUnwrapSymlink(unwrapSymlink, m, opts);
8288
} else if (isCore(x)) {
8389
return x;
8490
} else {
8591
var n = loadNodeModulesSync(x, absoluteStart);
86-
if (n) return maybeUnwrapSymlink(n, opts);
92+
if (n) return maybeUnwrapSymlink(unwrapSymlink, n, opts);
8793
}
8894

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

123-
var pkgfile = path.join(isDirectory(dir) ? maybeUnwrapSymlink(dir, opts) : dir, 'package.json');
129+
var pkgfile = path.join(isDirectory(dir) ? maybeUnwrapSymlink(unwrapSymlink, dir, opts) : dir, 'package.json');
124130

125131
if (!isFile(pkgfile)) {
126132
return loadpkg(path.dirname(dir));
@@ -140,7 +146,7 @@ module.exports = function resolveSync(x, options) {
140146
}
141147

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

readme.markdown

Lines changed: 22 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,13 @@ default `opts` values:
117119
return cb(err);
118120
});
119121
},
122+
unwrapSymlink: function unwrapSymlink(file, cb) {
123+
var realpath = typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath;
124+
realpath(file, function (realPathErr, realPath) {
125+
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
126+
else cb(null, realPathErr ? file : realPath);
127+
});
128+
},
120129
moduleDirectory: 'node_modules',
121130
preserveSymlinks: false
122131
}
@@ -139,6 +148,8 @@ options are:
139148

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

151+
* opts.unwrapSymlink - function to synchronously unwrap a potential symlink
152+
142153
* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field
143154
* pkg - package data
144155
* pkgfile - path to package.json
@@ -195,6 +206,17 @@ default `opts` values:
195206
}
196207
return stat.isDirectory();
197208
},
209+
defaultUnwrapSymlink: function unwrapSymlink(file) {
210+
try {
211+
var realpath = typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync;
212+
return realpath(file);
213+
} catch (realPathErr) {
214+
if (realPathErr.code !== 'ENOENT') {
215+
throw realPathErr;
216+
}
217+
}
218+
return file;
219+
},
198220
moduleDirectory: 'node_modules',
199221
preserveSymlinks: false
200222
}

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)