buddy

node MVC discord bot
Log | Files | Refs | README

index.js (9504B)


      1 // builtin
      2 var fs = require('fs');
      3 var path = require('path');
      4 
      5 // vendor
      6 var resv = require('resolve');
      7 
      8 // given a path, create an array of node_module paths for it
      9 // borrowed from substack/resolve
     10 function nodeModulesPaths (start, cb) {
     11     var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\/+/;
     12     var parts = start.split(splitRe);
     13 
     14     var dirs = [];
     15     for (var i = parts.length - 1; i >= 0; i--) {
     16         if (parts[i] === 'node_modules') continue;
     17         var dir = path.join.apply(
     18             path, parts.slice(0, i + 1).concat(['node_modules'])
     19         );
     20         if (!parts[0].match(/([A-Za-z]:)/)) {
     21             dir = '/' + dir;
     22         }
     23         dirs.push(dir);
     24     }
     25     return dirs;
     26 }
     27 
     28 function find_shims_in_package(pkgJson, cur_path, shims, browser) {
     29     try {
     30         var info = JSON.parse(pkgJson);
     31     }
     32     catch (err) {
     33         err.message = pkgJson + ' : ' + err.message
     34         throw err;
     35     }
     36 
     37     var replacements = getReplacements(info, browser);
     38 
     39     // no replacements, skip shims
     40     if (!replacements) {
     41         return;
     42     }
     43 
     44     // if browser mapping is a string
     45     // then it just replaces the main entry point
     46     if (typeof replacements === 'string') {
     47         var key = path.resolve(cur_path, info.main || 'index.js');
     48         shims[key] = path.resolve(cur_path, replacements);
     49         return;
     50     }
     51 
     52     // http://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders
     53     Object.keys(replacements).forEach(function(key) {
     54         var val;
     55         if (replacements[key] === false) {
     56             val = path.normalize(__dirname + '/empty.js');
     57         }
     58         else {
     59             val = replacements[key];
     60             // if target is a relative path, then resolve
     61             // otherwise we assume target is a module
     62             if (val[0] === '.') {
     63                 val = path.resolve(cur_path, val);
     64             }
     65         }
     66 
     67         if (key[0] === '/' || key[0] === '.') {
     68             // if begins with / ../ or ./ then we must resolve to a full path
     69             key = path.resolve(cur_path, key);
     70         }
     71         shims[key] = val;
     72     });
     73 
     74     [ '.js', '.json' ].forEach(function (ext) {
     75         Object.keys(shims).forEach(function (key) {
     76             if (!shims[key + ext]) {
     77                 shims[key + ext] = shims[key];
     78             }
     79         });
     80     });
     81 }
     82 
     83 // paths is mutated
     84 // load shims from first package.json file found
     85 function load_shims(paths, browser, cb) {
     86     // identify if our file should be replaced per the browser field
     87     // original filename|id -> replacement
     88     var shims = Object.create(null);
     89 
     90     (function next() {
     91         var cur_path = paths.shift();
     92         if (!cur_path) {
     93             return cb(null, shims);
     94         }
     95 
     96         var pkg_path = path.join(cur_path, 'package.json');
     97 
     98         fs.readFile(pkg_path, 'utf8', function(err, data) {
     99             if (err) {
    100                 // ignore paths we can't open
    101                 // avoids an exists check
    102                 if (err.code === 'ENOENT') {
    103                     return next();
    104                 }
    105 
    106                 return cb(err);
    107             }
    108             try {
    109                 find_shims_in_package(data, cur_path, shims, browser);
    110                 return cb(null, shims);
    111             }
    112             catch (err) {
    113                 return cb(err);
    114             }
    115         });
    116     })();
    117 };
    118 
    119 // paths is mutated
    120 // synchronously load shims from first package.json file found
    121 function load_shims_sync(paths, browser) {
    122     // identify if our file should be replaced per the browser field
    123     // original filename|id -> replacement
    124     var shims = Object.create(null);
    125     var cur_path;
    126 
    127     while (cur_path = paths.shift()) {
    128         var pkg_path = path.join(cur_path, 'package.json');
    129 
    130         try {
    131             var data = fs.readFileSync(pkg_path, 'utf8');
    132             find_shims_in_package(data, cur_path, shims, browser);
    133             return shims;
    134         }
    135         catch (err) {
    136             // ignore paths we can't open
    137             // avoids an exists check
    138             if (err.code === 'ENOENT') {
    139                 continue;
    140             }
    141 
    142             throw err;
    143         }
    144     }
    145     return shims;
    146 }
    147 
    148 function build_resolve_opts(opts, base) {
    149     var packageFilter = opts.packageFilter;
    150     var browser = normalizeBrowserFieldName(opts.browser)
    151 
    152     opts.basedir = base;
    153     opts.packageFilter = function (info, pkgdir) {
    154         if (packageFilter) info = packageFilter(info, pkgdir);
    155 
    156         var replacements = getReplacements(info, browser);
    157 
    158         // no browser field, keep info unchanged
    159         if (!replacements) {
    160             return info;
    161         }
    162 
    163         info[browser] = replacements;
    164 
    165         // replace main
    166         if (typeof replacements === 'string') {
    167             info.main = replacements;
    168             return info;
    169         }
    170 
    171         var replace_main = replacements[info.main || './index.js'] ||
    172             replacements['./' + info.main || './index.js'];
    173 
    174         info.main = replace_main || info.main;
    175         return info;
    176     };
    177 
    178     var pathFilter = opts.pathFilter;
    179     opts.pathFilter = function(info, resvPath, relativePath) {
    180         if (relativePath[0] != '.') {
    181             relativePath = './' + relativePath;
    182         }
    183         var mappedPath;
    184         if (pathFilter) {
    185             mappedPath = pathFilter.apply(this, arguments);
    186         }
    187         if (mappedPath) {
    188             return mappedPath;
    189         }
    190 
    191         var replacements = info[browser];
    192         if (!replacements) {
    193             return;
    194         }
    195 
    196         mappedPath = replacements[relativePath];
    197         if (!mappedPath && path.extname(relativePath) === '') {
    198             mappedPath = replacements[relativePath + '.js'];
    199             if (!mappedPath) {
    200                 mappedPath = replacements[relativePath + '.json'];
    201             }
    202         }
    203         return mappedPath;
    204     };
    205 
    206     return opts;
    207 }
    208 
    209 function resolve(id, opts, cb) {
    210 
    211     // opts.filename
    212     // opts.paths
    213     // opts.modules
    214     // opts.packageFilter
    215 
    216     opts = opts || {};
    217     opts.filename = opts.filename || '';
    218 
    219     var base = path.dirname(opts.filename);
    220 
    221     if (opts.basedir) {
    222         base = opts.basedir;
    223     }
    224 
    225     var paths = nodeModulesPaths(base);
    226 
    227     if (opts.paths) {
    228         paths.push.apply(paths, opts.paths);
    229     }
    230 
    231     paths = paths.map(function(p) {
    232         return path.dirname(p);
    233     });
    234 
    235     // we must always load shims because the browser field could shim out a module
    236     load_shims(paths, opts.browser, function(err, shims) {
    237         if (err) {
    238             return cb(err);
    239         }
    240 
    241         var resid = path.resolve(opts.basedir || path.dirname(opts.filename), id);
    242         if (shims[id] || shims[resid]) {
    243             var xid = shims[id] ? id : resid;
    244             // if the shim was is an absolute path, it was fully resolved
    245             if (shims[xid][0] === '/') {
    246                 return resv(shims[xid], build_resolve_opts(opts, base), function(err, full, pkg) {
    247                     cb(null, full, pkg);
    248                 });
    249             }
    250 
    251             // module -> alt-module shims
    252             id = shims[xid];
    253         }
    254 
    255         var modules = opts.modules || Object.create(null);
    256         var shim_path = modules[id];
    257         if (shim_path) {
    258             return cb(null, shim_path);
    259         }
    260 
    261         // our browser field resolver
    262         // if browser field is an object tho?
    263         var full = resv(id, build_resolve_opts(opts, base), function(err, full, pkg) {
    264             if (err) {
    265                 return cb(err);
    266             }
    267 
    268             var resolved = (shims) ? shims[full] || full : full;
    269             cb(null, resolved, pkg);
    270         });
    271     });
    272 };
    273 
    274 resolve.sync = function (id, opts) {
    275 
    276     // opts.filename
    277     // opts.paths
    278     // opts.modules
    279     // opts.packageFilter
    280 
    281     opts = opts || {};
    282     opts.filename = opts.filename || '';
    283 
    284     var base = path.dirname(opts.filename);
    285 
    286     if (opts.basedir) {
    287         base = opts.basedir;
    288     }
    289 
    290     var paths = nodeModulesPaths(base);
    291 
    292     if (opts.paths) {
    293         paths.push.apply(paths, opts.paths);
    294     }
    295 
    296     paths = paths.map(function(p) {
    297         return path.dirname(p);
    298     });
    299 
    300     // we must always load shims because the browser field could shim out a module
    301     var shims = load_shims_sync(paths, opts.browser);
    302     var resid = path.resolve(opts.basedir || path.dirname(opts.filename), id);
    303 
    304     if (shims[id] || shims[resid]) {
    305         var xid = shims[id] ? id : resid;
    306         // if the shim was is an absolute path, it was fully resolved
    307         if (shims[xid][0] === '/') {
    308             return resv.sync(shims[xid], build_resolve_opts(opts, base));
    309         }
    310 
    311         // module -> alt-module shims
    312         id = shims[xid];
    313     }
    314 
    315     var modules = opts.modules || Object.create(null);
    316     var shim_path = modules[id];
    317     if (shim_path) {
    318         return shim_path;
    319     }
    320 
    321     // our browser field resolver
    322     // if browser field is an object tho?
    323     var full = resv.sync(id, build_resolve_opts(opts, base));
    324 
    325     return (shims) ? shims[full] || full : full;
    326 };
    327 
    328 function normalizeBrowserFieldName(browser) {
    329     return browser || 'browser';
    330 }
    331 
    332 function getReplacements(info, browser) {
    333     browser = normalizeBrowserFieldName(browser);
    334     var replacements = info[browser] || info.browser;
    335 
    336     // support legacy browserify field for easier migration from legacy
    337     // many packages used this field historically
    338     if (typeof info.browserify === 'string' && !replacements) {
    339         replacements = info.browserify;
    340     }
    341 
    342     return replacements;
    343 }
    344 
    345 module.exports = resolve;