UNPKG

denodify

Version:

Use nodejs modules in the browser. Used with bb-server and html-builder

197 lines (156 loc) 5.63 kB
// builtin var priv_module = require('module'); var natives = process.binding('natives'); var path = require('path'); var fs = require('fs'); // 3rd party var detective = require('detective'); // inspect the source for dependencies function from_source(source, parent, opt, cb) { var cache = opt.cache; var ignore_missing = false || opt.ignoreMissing; var requires = (opt.detective || detective)(source); var result = []; // deduplicate requires with the same name // this avoids trying to process the require twice requires = requires.filter(function(elem, idx) { return requires.indexOf(elem) === idx; }); (function next() { var req = requires.shift(); if (!req) { return cb(null, result); } // short require name var id = req; var native = natives[id]; var resolve = opt.resolve(req, parent, function(err, full_path, ignore) { if (err) { return cb(err); } if (!full_path) { // if resolver ignored the native // we just push it manually if (native) { result.push({ id: id, core: true, // native is deprecated in favor of `core` // kept here for backwards compat native: true }); return next(); } // skip the dependency if we can't find it if (ignore_missing) { if (typeof ignore_missing === 'function') { ignore_missing(req, parent.filename) } return next(); } return cb(new Error('Cannot find module: \'' + req + '\' ' + 'required from ' + parent.filename)); } // ignore indicates we should not process dependencies for this file // this is useful if we don't care about certain files being handled further // we still want the dependency added to the deps of the file we processed // but do not process this file or it's deps if (ignore) { result.push({ id: id, filename: full_path }); return next(); } var paths = parent.paths.concat(node_module_paths(full_path)); var new_parent = { id: id, filename: full_path, paths: paths } from_filename(full_path, new_parent, opt, function(err, deps, src) { if (err) { return cb(err); } var res = { id: id, filename: full_path, deps: deps }; if (opt.includeSource) { res.source = src; } result.push(res); next(); }); }); })(); } function from_filename(filename, parent, opt, cb) { var cache = opt.cache; // wtf is this cache? // appears to be the list of dependencies for this filename // what it really should be is the info var cached = cache[filename]; if (cached) { return cb(null, cached.deps, cached.src); } fs.readFile(filename, 'utf8', function(err, content) { if (err) { return cb(err); } // must be set before the compile call to handle circular references var result = cache[filename] = { deps: [] }; try { from_source(content, parent, opt, function(err, deps) { if (err) { return cb(err); } result.deps = deps; // only cache source if caller will want the source if (opt.includeSource) { result.src = content; } return cb(err, deps, content); }); } catch (err) { err.message = filename + ': ' + err.message; cb(err); // throw err; }; }); } /// lookup the full path to our module with local name 'name' function lookup_path(name, parent) { var resolved_module = priv_module.Module._resolveLookupPaths(name, parent); var paths = resolved_module[1]; return priv_module.Module._findPath(name, paths); } /// return an array of node_module paths given a filename function node_module_paths(filename) { return priv_module.Module._nodeModulePaths(path.dirname(filename)); } /// process filename and callback with tree of dependencies /// the tree does have circular references when a child requires a parent module.exports = function(filename, opt, cb) { opt = opt || {}; if (typeof opt === 'function') { cb = opt; opt = {}; } // add the cache storage opt.cache = opt.cache || {}; // default resolver if none specified just resolves as node would opt.resolve = opt.resolve || function(id, parent, cb) { // TODO(shtylman) async cb(null, lookup_path(id, parent)); }; var paths = node_module_paths(filename); // entry parent specifies the base node modules path var entry_parent = { id: filename, filename: filename, paths: paths }; from_filename(filename, entry_parent, opt, cb); };