Skip to content

Commit

Permalink
feat: make unwrapSymlink pluggable
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Apr 7, 2020
1 parent edfe3f7 commit 0cf3b33
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 22 deletions.
24 changes: 15 additions & 9 deletions lib/async.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ var defaultIsDir = function isDirectory(dir, cb) {
});
};

var maybeUnwrapSymlink = function maybeUnwrapSymlink(x, opts, cb) {
var defaultUnwrapSymlink = function unwrapSymlink(x, cb) {
fs.realpath(x, function (realPathErr, realPath) {
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
else cb(null, realPathErr ? x : realPath);
});
};

var maybeUnwrapSymlink = function maybeUnwrapSymlink(unwrap, x, opts, cb) {
if (!opts || !opts.preserveSymlinks) {
fs.realpath(x, function (realPathErr, realPath) {
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
else cb(null, realPathErr ? x : realPath);
});
unwrap(x, cb);
} else {
cb(null, x);
}
Expand Down Expand Up @@ -63,6 +67,7 @@ module.exports = function resolve(x, options, callback) {
var isFile = opts.isFile || defaultIsFile;
var isDirectory = opts.isDirectory || defaultIsDir;
var readFile = opts.readFile || fs.readFile;
var unwrapSymlink = opts.unwrapSymlink || defaultUnwrapSymlink;
var packageIterator = opts.packageIterator;

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

maybeUnwrapSymlink(
unwrapSymlink,
absoluteStart,
opts,
function (err, realStart) {
Expand Down Expand Up @@ -110,7 +116,7 @@ module.exports = function resolve(x, options, callback) {
} else loadNodeModules(x, basedir, function (err, n, pkg) {
if (err) cb(err);
else if (n) {
return maybeUnwrapSymlink(n, opts, function (err, realN) {
return maybeUnwrapSymlink(unwrapSymlink, n, opts, function (err, realN) {
if (err) {
cb(err);
} else {
Expand All @@ -131,7 +137,7 @@ module.exports = function resolve(x, options, callback) {
else loadAsDirectory(res, function (err, d, pkg) {
if (err) cb(err);
else if (d) {
maybeUnwrapSymlink(d, opts, function (err, realD) {
maybeUnwrapSymlink(unwrapSymlink, d, opts, function (err, realD) {
if (err) {
cb(err);
} else {
Expand Down Expand Up @@ -195,7 +201,7 @@ module.exports = function resolve(x, options, callback) {
}
if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null);

maybeUnwrapSymlink(dir, opts, function (unwrapErr, pkgdir) {
maybeUnwrapSymlink(unwrapSymlink, dir, opts, function (unwrapErr, pkgdir) {
if (unwrapErr) return loadpkg(path.dirname(dir), cb);
var pkgfile = path.join(pkgdir, 'package.json');
isFile(pkgfile, function (err, ex) {
Expand Down Expand Up @@ -223,7 +229,7 @@ module.exports = function resolve(x, options, callback) {
fpkg = opts.package;
}

maybeUnwrapSymlink(x, opts, function (unwrapErr, pkgdir) {
maybeUnwrapSymlink(unwrapSymlink, x, opts, function (unwrapErr, pkgdir) {
if (unwrapErr) return loadAsDirectory(path.dirname(x), fpkg, cb);
var pkgfile = path.join(pkgdir, 'package.json');
isFile(pkgfile, function (err, ex) {
Expand Down
32 changes: 19 additions & 13 deletions lib/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,24 @@ var defaultIsDir = function isDirectory(dir) {
return stat.isDirectory();
};

var maybeUnwrapSymlink = function maybeUnwrapSymlink(x, opts) {
if (!opts || !opts.preserveSymlinks) {
try {
return fs.realpathSync(x);
} catch (realPathErr) {
if (realPathErr.code !== 'ENOENT') {
throw realPathErr;
}
var defaultUnwrapSymlink = function unwrapSymlink(x) {
try {
return fs.realpathSync(x);
} catch (realPathErr) {
if (realPathErr.code !== 'ENOENT') {
throw realPathErr;
}
}
return x;
};

var maybeUnwrapSymlink = function maybeUnwrapSymlink(unwrap, x, opts) {
if (!opts || !opts.preserveSymlinks) {
return unwrap(x);
}
return x;
};

var getPackageCandidates = function getPackageCandidates(x, start, opts) {
var dirs = nodeModulesPaths(start, opts, x);
for (var i = 0; i < dirs.length; i++) {
Expand All @@ -55,6 +60,7 @@ module.exports = function resolveSync(x, options) {
var isFile = opts.isFile || defaultIsFile;
var isDirectory = opts.isDirectory || defaultIsDir;
var readFileSync = opts.readFileSync || fs.readFileSync;
var unwrapSymlink = opts.unwrapSymlink || defaultUnwrapSymlink;
var packageIterator = opts.packageIterator;

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

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

if (opts.basedir && !isDirectory(absoluteStart)) {
var dirError = new TypeError('Provided basedir "' + opts.basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory'));
Expand All @@ -76,12 +82,12 @@ module.exports = function resolveSync(x, options) {
var res = path.resolve(absoluteStart, x);
if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
var m = loadAsFileSync(res) || loadAsDirectorySync(res);
if (m) return maybeUnwrapSymlink(m, opts);
if (m) return maybeUnwrapSymlink(unwrapSymlink, m, opts);
} else if (isCore(x)) {
return x;
} else {
var n = loadNodeModulesSync(x, absoluteStart);
if (n) return maybeUnwrapSymlink(n, opts);
if (n) return maybeUnwrapSymlink(unwrapSymlink, n, opts);
}

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

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

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

function loadAsDirectorySync(x) {
var pkgfile = path.join(isDirectory(x) ? maybeUnwrapSymlink(x, opts) : x, '/package.json');
var pkgfile = path.join(isDirectory(x) ? maybeUnwrapSymlink(unwrapSymlink, x, opts) : x, '/package.json');
if (isFile(pkgfile)) {
try {
var body = readFileSync(pkgfile, 'UTF8');
Expand Down
20 changes: 20 additions & 0 deletions readme.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ options are:

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

* opts.unwrapSymlink - function to asynchronously unwrap a potential symlink

* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field
* pkg - package data
* pkgfile - path to package.json
Expand Down Expand Up @@ -117,6 +119,12 @@ default `opts` values:
return cb(err);
});
},
unwrapSymlink: function unwrapSymlink(file, cb) {
fs.realpath(file, function (realPathErr, realPath) {
if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
else cb(null, realPathErr ? file : realPath);
});
},
moduleDirectory: 'node_modules',
preserveSymlinks: false
}
Expand All @@ -139,6 +147,8 @@ options are:

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

* opts.unwrapSymlink - function to synchronously unwrap a potential symlink

* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field
* pkg - package data
* pkgfile - path to package.json
Expand Down Expand Up @@ -195,6 +205,16 @@ default `opts` values:
}
return stat.isDirectory();
},
defaultUnwrapSymlink: function unwrapSymlink(file) {
try {
return fs.realpathSync(file);
} catch (realPathErr) {
if (realPathErr.code !== 'ENOENT') {
throw realPathErr;
}
}
return file;
},
moduleDirectory: 'node_modules',
preserveSymlinks: false
}
Expand Down
12 changes: 12 additions & 0 deletions test/mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ test('mock', function (t) {
},
readFile: function (file, cb) {
cb(null, files[path.resolve(file)]);
},
unwrapSymlink: function (file, cb) {
cb(null, file);
}
};
}
Expand Down Expand Up @@ -70,6 +73,9 @@ test('mock from package', function (t) {
'package': { main: 'bar' },
readFile: function (file, cb) {
cb(null, files[file]);
},
unwrapSymlink: function (file, cb) {
cb(null, file);
}
};
}
Expand Down Expand Up @@ -121,6 +127,9 @@ test('mock package', function (t) {
},
readFile: function (file, cb) {
cb(null, files[path.resolve(file)]);
},
unwrapSymlink: function (file, cb) {
cb(null, file);
}
};
}
Expand Down Expand Up @@ -157,6 +166,9 @@ test('mock package from package', function (t) {
'package': { main: 'bar' },
readFile: function (file, cb) {
cb(null, files[path.resolve(file)]);
},
unwrapSymlink: function (file, cb) {
cb(null, file);
}
};
}
Expand Down
6 changes: 6 additions & 0 deletions test/mock_sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ test('mock', function (t) {
},
readFileSync: function (file) {
return files[path.resolve(file)];
},
unwrapSymlink: function (file) {
return file;
}
};
}
Expand Down Expand Up @@ -70,6 +73,9 @@ test('mock package', function (t) {
},
readFileSync: function (file) {
return files[path.resolve(file)];
},
unwrapSymlink: function (file) {
return file;
}
};
}
Expand Down

0 comments on commit 0cf3b33

Please sign in to comment.