| var core = require('./core'); | |
| var fs = require('fs'); | |
| var path = require('path'); | |
| var caller = require('./caller.js'); | |
| var nodeModulesPaths = require('./node-modules-paths.js'); | |
| var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//; | |
| module.exports = function resolve (x, opts, cb) { | |
| if (typeof opts === 'function') { | |
| cb = opts; | |
| opts = {}; | |
| } | |
| if (!opts) opts = {}; | |
| if (typeof x !== 'string') { | |
| return process.nextTick(function () { | |
| cb(new Error('path must be a string')); | |
| }); | |
| } | |
| var isFile = opts.isFile || function (file, cb) { | |
| fs.stat(file, function (err, stat) { | |
| if (err && err.code === 'ENOENT') cb(null, false) | |
| else if (err) cb(err) | |
| else cb(null, stat.isFile() || stat.isFIFO()) | |
| }); | |
| }; | |
| var readFile = opts.readFile || fs.readFile; | |
| var extensions = opts.extensions || [ '.js' ]; | |
| var y = opts.basedir || path.dirname(caller()); | |
| opts.paths = opts.paths || []; | |
| if (/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[\\\/])/.test(x)) { | |
| var res = path.resolve(y, x); | |
| if (x === '..') res += '/'; | |
| if (/\/$/.test(x) && res === y) { | |
| loadAsDirectory(res, opts.package, onfile); | |
| } | |
| else loadAsFile(res, opts.package, onfile); | |
| } | |
| else loadNodeModules(x, y, function (err, n, pkg) { | |
| if (err) cb(err) | |
| else if (n) cb(null, n, pkg) | |
| else if (core[x]) return cb(null, x); | |
| else cb(new Error("Cannot find module '" + x + "' from '" + y + "'")) | |
| }); | |
| function onfile (err, m, pkg) { | |
| if (err) cb(err) | |
| else if (m) cb(null, m, pkg) | |
| else loadAsDirectory(res, function (err, d, pkg) { | |
| if (err) cb(err) | |
| else if (d) cb(null, d, pkg) | |
| else cb(new Error("Cannot find module '" + x + "' from '" + y + "'")) | |
| }) | |
| } | |
| function loadAsFile (x, pkg, cb) { | |
| if (typeof pkg === 'function') { | |
| cb = pkg; | |
| pkg = undefined; | |
| } | |
| var exts = [''].concat(extensions); | |
| load(exts, x, pkg) | |
| function load (exts, x, pkg) { | |
| if (exts.length === 0) return cb(null, undefined, pkg); | |
| var file = x + exts[0]; | |
| if (pkg) onpkg(null, pkg) | |
| else loadpkg(path.dirname(file), onpkg); | |
| function onpkg (err, pkg_, dir) { | |
| pkg = pkg_; | |
| if (err) return cb(err) | |
| if (dir && pkg && opts.pathFilter) { | |
| var rfile = path.relative(dir, file); | |
| var rel = rfile.slice(0, rfile.length - exts[0].length); | |
| var r = opts.pathFilter(pkg, x, rel); | |
| if (r) return load( | |
| [''].concat(extensions.slice()), | |
| path.resolve(dir, r), | |
| pkg | |
| ); | |
| } | |
| isFile(file, onex); | |
| } | |
| function onex (err, ex) { | |
| if (err) cb(err) | |
| else if (!ex) load(exts.slice(1), x, pkg) | |
| else cb(null, file, pkg) | |
| } | |
| } | |
| } | |
| function loadpkg (dir, cb) { | |
| if (dir === '' || dir === '/') return cb(null); | |
| if (process.platform === 'win32' && /^\w:[\\\/]*$/.test(dir)) { | |
| return cb(null); | |
| } | |
| if (/[\\\/]node_modules[\\\/]*$/.test(dir)) return cb(null); | |
| var pkgfile = path.join(dir, 'package.json'); | |
| isFile(pkgfile, function (err, ex) { | |
| // on err, ex is false | |
| if (!ex) return loadpkg( | |
| path.dirname(dir), cb | |
| ); | |
| readFile(pkgfile, function (err, body) { | |
| if (err) cb(err); | |
| try { var pkg = JSON.parse(body) } | |
| catch (err) {} | |
| if (pkg && opts.packageFilter) { | |
| pkg = opts.packageFilter(pkg, pkgfile); | |
| } | |
| cb(null, pkg, dir); | |
| }); | |
| }); | |
| } | |
| function loadAsDirectory (x, fpkg, cb) { | |
| if (typeof fpkg === 'function') { | |
| cb = fpkg; | |
| fpkg = opts.package; | |
| } | |
| var pkgfile = path.join(x, '/package.json'); | |
| isFile(pkgfile, function (err, ex) { | |
| if (err) return cb(err); | |
| if (!ex) return loadAsFile(path.join(x, '/index'), fpkg, cb); | |
| readFile(pkgfile, function (err, body) { | |
| if (err) return cb(err); | |
| try { | |
| var pkg = JSON.parse(body); | |
| } | |
| catch (err) {} | |
| if (opts.packageFilter) { | |
| pkg = opts.packageFilter(pkg, pkgfile); | |
| } | |
| if (pkg.main) { | |
| if (pkg.main === '.' || pkg.main === './'){ | |
| pkg.main = 'index' | |
| } | |
| loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) { | |
| if (err) return cb(err); | |
| if (m) return cb(null, m, pkg); | |
| if (!pkg) return loadAsFile(path.join(x, '/index'), pkg, cb); | |
| var dir = path.resolve(x, pkg.main); | |
| loadAsDirectory(dir, pkg, function (err, n, pkg) { | |
| if (err) return cb(err); | |
| if (n) return cb(null, n, pkg); | |
| loadAsFile(path.join(x, '/index'), pkg, cb); | |
| }); | |
| }); | |
| return; | |
| } | |
| loadAsFile(path.join(x, '/index'), pkg, cb); | |
| }); | |
| }); | |
| } | |
| function loadNodeModules (x, start, cb) { | |
| (function process (dirs) { | |
| if (dirs.length === 0) return cb(null, undefined); | |
| var dir = dirs[0]; | |
| var file = path.join(dir, '/', x); | |
| loadAsFile(file, undefined, onfile); | |
| function onfile (err, m, pkg) { | |
| if (err) return cb(err); | |
| if (m) return cb(null, m, pkg); | |
| loadAsDirectory(path.join(dir, '/', x), undefined, ondir); | |
| } | |
| function ondir (err, n, pkg) { | |
| if (err) return cb(err); | |
| if (n) return cb(null, n, pkg); | |
| process(dirs.slice(1)); | |
| } | |
| })(nodeModulesPaths(start, opts)); | |
| } | |
| }; |