Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New] Add isDirectory; use to speed up node_modules lookups #192

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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