| // 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; |