123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- // builtin
- var fs = require('fs');
- var path = require('path');
- // vendor
- var resv = require('resolve');
- // given a path, create an array of node_module paths for it
- // borrowed from substack/resolve
- function nodeModulesPaths (start, cb) {
- var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\/+/;
- var parts = start.split(splitRe);
- var dirs = [];
- for (var i = parts.length - 1; i >= 0; i--) {
- if (parts[i] === 'node_modules') continue;
- var dir = path.join.apply(
- path, parts.slice(0, i + 1).concat(['node_modules'])
- );
- if (!parts[0].match(/([A-Za-z]:)/)) {
- dir = '/' + dir;
- }
- dirs.push(dir);
- }
- return dirs;
- }
- function find_shims_in_package(pkgJson, cur_path, shims, browser) {
- try {
- var info = JSON.parse(pkgJson);
- }
- catch (err) {
- err.message = pkgJson + ' : ' + err.message
- throw err;
- }
- var replacements = getReplacements(info, browser);
- // no replacements, skip shims
- if (!replacements) {
- return;
- }
- // if browser mapping is a string
- // then it just replaces the main entry point
- if (typeof replacements === 'string') {
- var key = path.resolve(cur_path, info.main || 'index.js');
- shims[key] = path.resolve(cur_path, replacements);
- return;
- }
- // http://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders
- Object.keys(replacements).forEach(function(key) {
- var val;
- if (replacements[key] === false) {
- val = path.normalize(__dirname + '/empty.js');
- }
- else {
- val = replacements[key];
- // if target is a relative path, then resolve
- // otherwise we assume target is a module
- if (val[0] === '.') {
- val = path.resolve(cur_path, val);
- }
- }
- if (key[0] === '/' || key[0] === '.') {
- // if begins with / ../ or ./ then we must resolve to a full path
- key = path.resolve(cur_path, key);
- }
- shims[key] = val;
- });
- [ '.js', '.json' ].forEach(function (ext) {
- Object.keys(shims).forEach(function (key) {
- if (!shims[key + ext]) {
- shims[key + ext] = shims[key];
- }
- });
- });
- }
- // paths is mutated
- // load shims from first package.json file found
- function load_shims(paths, browser, cb) {
- // identify if our file should be replaced per the browser field
- // original filename|id -> replacement
- var shims = Object.create(null);
- (function next() {
- var cur_path = paths.shift();
- if (!cur_path) {
- return cb(null, shims);
- }
- var pkg_path = path.join(cur_path, 'package.json');
- fs.readFile(pkg_path, 'utf8', function(err, data) {
- if (err) {
- // ignore paths we can't open
- // avoids an exists check
- if (err.code === 'ENOENT') {
- return next();
- }
- return cb(err);
- }
- try {
- find_shims_in_package(data, cur_path, shims, browser);
- return cb(null, shims);
- }
- catch (err) {
- return cb(err);
- }
- });
- })();
- };
- // paths is mutated
- // synchronously load shims from first package.json file found
- function load_shims_sync(paths, browser) {
- // identify if our file should be replaced per the browser field
- // original filename|id -> replacement
- var shims = Object.create(null);
- var cur_path;
- while (cur_path = paths.shift()) {
- var pkg_path = path.join(cur_path, 'package.json');
- try {
- var data = fs.readFileSync(pkg_path, 'utf8');
- find_shims_in_package(data, cur_path, shims, browser);
- return shims;
- }
- catch (err) {
- // ignore paths we can't open
- // avoids an exists check
- if (err.code === 'ENOENT') {
- continue;
- }
- throw err;
- }
- }
- return shims;
- }
- function build_resolve_opts(opts, base) {
- var packageFilter = opts.packageFilter;
- var browser = normalizeBrowserFieldName(opts.browser)
- opts.basedir = base;
- opts.packageFilter = function (info, pkgdir) {
- if (packageFilter) info = packageFilter(info, pkgdir);
- var replacements = getReplacements(info, browser);
- // no browser field, keep info unchanged
- if (!replacements) {
- return info;
- }
- info[browser] = replacements;
- // replace main
- if (typeof replacements === 'string') {
- info.main = replacements;
- return info;
- }
- var replace_main = replacements[info.main || './index.js'] ||
- replacements['./' + info.main || './index.js'];
- info.main = replace_main || info.main;
- return info;
- };
- var pathFilter = opts.pathFilter;
- opts.pathFilter = function(info, resvPath, relativePath) {
- if (relativePath[0] != '.') {
- relativePath = './' + relativePath;
- }
- var mappedPath;
- if (pathFilter) {
- mappedPath = pathFilter.apply(this, arguments);
- }
- if (mappedPath) {
- return mappedPath;
- }
- var replacements = info[browser];
- if (!replacements) {
- return;
- }
- mappedPath = replacements[relativePath];
- if (!mappedPath && path.extname(relativePath) === '') {
- mappedPath = replacements[relativePath + '.js'];
- if (!mappedPath) {
- mappedPath = replacements[relativePath + '.json'];
- }
- }
- return mappedPath;
- };
- return opts;
- }
- function resolve(id, opts, cb) {
- // opts.filename
- // opts.paths
- // opts.modules
- // opts.packageFilter
- opts = opts || {};
- opts.filename = opts.filename || '';
- var base = path.dirname(opts.filename);
- if (opts.basedir) {
- base = opts.basedir;
- }
- var paths = nodeModulesPaths(base);
- if (opts.paths) {
- paths.push.apply(paths, opts.paths);
- }
- paths = paths.map(function(p) {
- return path.dirname(p);
- });
- // we must always load shims because the browser field could shim out a module
- load_shims(paths, opts.browser, function(err, shims) {
- if (err) {
- return cb(err);
- }
- var resid = path.resolve(opts.basedir || path.dirname(opts.filename), id);
- if (shims[id] || shims[resid]) {
- var xid = shims[id] ? id : resid;
- // if the shim was is an absolute path, it was fully resolved
- if (shims[xid][0] === '/') {
- return resv(shims[xid], build_resolve_opts(opts, base), function(err, full, pkg) {
- cb(null, full, pkg);
- });
- }
- // module -> alt-module shims
- id = shims[xid];
- }
- var modules = opts.modules || Object.create(null);
- var shim_path = modules[id];
- if (shim_path) {
- return cb(null, shim_path);
- }
- // our browser field resolver
- // if browser field is an object tho?
- var full = resv(id, build_resolve_opts(opts, base), function(err, full, pkg) {
- if (err) {
- return cb(err);
- }
- var resolved = (shims) ? shims[full] || full : full;
- cb(null, resolved, pkg);
- });
- });
- };
- resolve.sync = function (id, opts) {
- // opts.filename
- // opts.paths
- // opts.modules
- // opts.packageFilter
- opts = opts || {};
- opts.filename = opts.filename || '';
- var base = path.dirname(opts.filename);
- if (opts.basedir) {
- base = opts.basedir;
- }
- var paths = nodeModulesPaths(base);
- if (opts.paths) {
- paths.push.apply(paths, opts.paths);
- }
- paths = paths.map(function(p) {
- return path.dirname(p);
- });
- // we must always load shims because the browser field could shim out a module
- var shims = load_shims_sync(paths, opts.browser);
- var resid = path.resolve(opts.basedir || path.dirname(opts.filename), id);
- if (shims[id] || shims[resid]) {
- var xid = shims[id] ? id : resid;
- // if the shim was is an absolute path, it was fully resolved
- if (shims[xid][0] === '/') {
- return resv.sync(shims[xid], build_resolve_opts(opts, base));
- }
- // module -> alt-module shims
- id = shims[xid];
- }
- var modules = opts.modules || Object.create(null);
- var shim_path = modules[id];
- if (shim_path) {
- return shim_path;
- }
- // our browser field resolver
- // if browser field is an object tho?
- var full = resv.sync(id, build_resolve_opts(opts, base));
- return (shims) ? shims[full] || full : full;
- };
- function normalizeBrowserFieldName(browser) {
- return browser || 'browser';
- }
- function getReplacements(info, browser) {
- browser = normalizeBrowserFieldName(browser);
- var replacements = info[browser] || info.browser;
- // support legacy browserify field for easier migration from legacy
- // many packages used this field historically
- if (typeof info.browserify === 'string' && !replacements) {
- replacements = info.browserify;
- }
- return replacements;
- }
- module.exports = resolve;
|