Skip to content

Commit

Permalink
Merge pull request #192 from keithamus/new-add-isdirectory-use-to-spe…
Browse files Browse the repository at this point in the history
…ed-up-node-modules-lookups

[New] Add `isDirectory`; use to speed up `node_modules` lookups
  • Loading branch information
ljharb committed May 15, 2019
2 parents c36ba3d + d2816d8 commit 38559e0
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 10 deletions.
21 changes: 19 additions & 2 deletions lib/async.js
Expand Up @@ -15,6 +15,16 @@ var defaultIsFile = function isFile(file, cb) {
});
};

var defaultIsDir = function isDirectory(dir, cb) {
fs.stat(dir, function (err, stat) {
if (!err) {
return cb(null, stat.isDirectory());
}
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
return cb(err);
});
};

module.exports = function resolve(x, options, callback) {
var cb = callback;
var opts = options;
Expand All @@ -32,6 +42,7 @@ module.exports = function resolve(x, options, callback) {
opts = normalizeOptions(x, opts);

var isFile = opts.isFile || defaultIsFile;
var isDirectory = opts.isDirectory || defaultIsDir;
var readFile = opts.readFile || fs.readFile;

var extensions = opts.extensions || ['.js'];
Expand Down Expand Up @@ -208,8 +219,14 @@ module.exports = function resolve(x, options, callback) {
if (dirs.length === 0) return cb(null, undefined);
var dir = dirs[0];

var file = path.join(dir, x);
loadAsFile(file, opts.package, onfile);
isDirectory(dir, isdir);

function isdir(err, isdir) {
if (err) return cb(err);
if (!isdir) return processDirs(cb, dirs.slice(1));
var file = path.join(dir, x);
loadAsFile(file, opts.package, onfile);
}

function onfile(err, m, pkg) {
if (err) return cb(err);
Expand Down
21 changes: 17 additions & 4 deletions lib/sync.js
Expand Up @@ -15,6 +15,16 @@ var defaultIsFile = function isFile(file) {
return stat.isFile() || stat.isFIFO();
};

var defaultIsDir = function isDirectory(dir) {
try {
var stat = fs.statSync(dir);
} catch (e) {
if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
throw e;
}
return stat.isDirectory();
};

module.exports = function (x, options) {
if (typeof x !== 'string') {
throw new TypeError('Path must be a string.');
Expand All @@ -23,6 +33,7 @@ module.exports = function (x, options) {

var isFile = opts.isFile || defaultIsFile;
var readFileSync = opts.readFileSync || fs.readFileSync;
var isDirectory = opts.isDirectory || defaultIsDir;

var extensions = opts.extensions || ['.js'];
var basedir = opts.basedir || path.dirname(caller());
Expand Down Expand Up @@ -145,10 +156,12 @@ module.exports = function (x, options) {
var dirs = nodeModulesPaths(start, opts, x);
for (var i = 0; i < dirs.length; i++) {
var dir = dirs[i];
var m = loadAsFileSync(path.join(dir, '/', x));
if (m) return m;
var n = loadAsDirectorySync(path.join(dir, '/', x));
if (n) return n;
if (isDirectory(dir)) {
var m = loadAsFileSync(path.join(dir, '/', x));
if (m) return m;
var n = loadAsDirectorySync(path.join(dir, '/', x));
if (n) return n;
}
}
}
};
22 changes: 22 additions & 0 deletions readme.markdown
Expand Up @@ -59,6 +59,8 @@ options are:

* opts.isFile - function to asynchronously test whether a file exists

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

* `opts.packageFilter(pkg, pkgfile)` - transform the parsed package.json contents before looking at the "main" field
* pkg - package data
* pkgfile - path to package.json
Expand Down Expand Up @@ -101,6 +103,15 @@ default `opts` values:
return cb(err);
});
},
isDirectory: function isDirectory(dir, cb) {
fs.stat(dir, function (err, stat) {
if (!err) {
return cb(null, stat.isDirectory());
}
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
return cb(err);
});
},
moduleDirectory: 'node_modules',
preserveSymlinks: true
}
Expand All @@ -121,6 +132,8 @@ options are:

* opts.isFile - function to synchronously test whether a file exists

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

* `opts.packageFilter(pkg, dir)` - transform the parsed package.json contents before looking at the "main" field
* pkg - package data
* dir - directory for package.json (Note: the second argument will change to "pkgfile" in v2)
Expand Down Expand Up @@ -157,6 +170,15 @@ default `opts` values:
}
return stat.isFile() || stat.isFIFO();
},
isDirectory: function isDirectory(dir) {
try {
var stat = fs.statSync(dir);
} catch (e) {
if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
throw e;
}
return stat.isDirectory();
},
moduleDirectory: 'node_modules',
preserveSymlinks: true
}
Expand Down
26 changes: 26 additions & 0 deletions test/mock.js
Expand Up @@ -8,12 +8,18 @@ test('mock', function (t) {
var files = {};
files[path.resolve('/foo/bar/baz.js')] = 'beep';

var dirs = {};
dirs[path.resolve('/foo/bar')] = true;

function opts(basedir) {
return {
basedir: path.resolve(basedir),
isFile: function (file, cb) {
cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file)));
},
isDirectory: function (dir, cb) {
cb(null, !!dirs[path.resolve(dir)]);
},
readFile: function (file, cb) {
cb(null, files[path.resolve(file)]);
}
Expand Down Expand Up @@ -49,12 +55,18 @@ test('mock from package', function (t) {
var files = {};
files[path.resolve('/foo/bar/baz.js')] = 'beep';

var dirs = {};
dirs[path.resolve('/foo/bar')] = true;

function opts(basedir) {
return {
basedir: path.resolve(basedir),
isFile: function (file, cb) {
cb(null, Object.prototype.hasOwnProperty.call(files, file));
},
isDirectory: function (dir, cb) {
cb(null, !!dirs[path.resolve(dir)]);
},
'package': { main: 'bar' },
readFile: function (file, cb) {
cb(null, files[file]);
Expand Down Expand Up @@ -94,12 +106,19 @@ test('mock package', function (t) {
main: './baz.js'
});

var dirs = {};
dirs[path.resolve('/foo')] = true;
dirs[path.resolve('/foo/node_modules')] = true;

function opts(basedir) {
return {
basedir: path.resolve(basedir),
isFile: function (file, cb) {
cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file)));
},
isDirectory: function (dir, cb) {
cb(null, !!dirs[path.resolve(dir)]);
},
readFile: function (file, cb) {
cb(null, files[path.resolve(file)]);
}
Expand All @@ -122,12 +141,19 @@ test('mock package from package', function (t) {
main: './baz.js'
});

var dirs = {};
dirs[path.resolve('/foo')] = true;
dirs[path.resolve('/foo/node_modules')] = true;

function opts(basedir) {
return {
basedir: path.resolve(basedir),
isFile: function (file, cb) {
cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file)));
},
isDirectory: function (dir, cb) {
cb(null, !!dirs[path.resolve(dir)]);
},
'package': { main: 'bar' },
readFile: function (file, cb) {
cb(null, files[path.resolve(file)]);
Expand Down
21 changes: 17 additions & 4 deletions test/mock_sync.js
Expand Up @@ -8,14 +8,20 @@ test('mock', function (t) {
var files = {};
files[path.resolve('/foo/bar/baz.js')] = 'beep';

var dirs = {};
dirs[path.resolve('/foo/bar')] = true;

function opts(basedir) {
return {
basedir: path.resolve(basedir),
isFile: function (file) {
return Object.prototype.hasOwnProperty.call(files, file);
return Object.prototype.hasOwnProperty.call(files, path.resolve(file));
},
isDirectory: function (dir) {
return !!dirs[path.resolve(dir)];
},
readFileSync: function (file) {
return files[file];
return files[path.resolve(file)];
}
};
}
Expand Down Expand Up @@ -48,14 +54,21 @@ test('mock package', function (t) {
main: './baz.js'
});

var dirs = {};
dirs[path.resolve('/foo')] = true;
dirs[path.resolve('/foo/node_modules')] = true;

function opts(basedir) {
return {
basedir: path.resolve(basedir),
isFile: function (file) {
return Object.prototype.hasOwnProperty.call(files, file);
return Object.prototype.hasOwnProperty.call(files, path.resolve(file));
},
isDirectory: function (dir) {
return !!dirs[path.resolve(dir)];
},
readFileSync: function (file) {
return files[file];
return files[path.resolve(file)];
}
};
}
Expand Down

0 comments on commit 38559e0

Please sign in to comment.