UNPKG

required

Version:

traverse your script and identify dependencies from requires

149 lines (114 loc) 3.72 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'); var cache = {}; // inspect the source for dependencies function from_source(source, parent, cb) { var requires = 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; // for now we just insert the native module into the tree // and mark it as 'native' // allow for whomever uses us to deal with natives as they wish var native = natives[id]; if (native) { // natives are cached by id if (cache[id]) { result.push(cache[id]); return next(); } // cache before calling compile to handle circular references var res = cache[id] = { id: id, native: true }; result.push(res); from_source(native, parent, function(err, details) { if (err) { return cb(err); } res.deps = details; next(); }); return; }; var full_path = lookup_path(req, parent); if (!full_path) { return cb(new Error('unable to find module: ' + req)); } var new_parent = { id: id, filename: full_path, paths: parent.paths } from_filename(full_path, new_parent, function(err, deps) { if (err) { return cb(err); } result.push({ id: id, filename: full_path, deps: deps }); next(); }); })(); } function from_filename(filename, parent, cb) { var cached = cache[filename]; if (cached) { return cb(null, cached); } 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] = []; from_source(content, parent, function(err, deps) { if (err) { return cb(err); } // push onto the result set so circular references are populated result.push.apply(result, deps); return cb(err, result); }); }); } /// 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); } /// process filename and callback with tree of dependencies /// the tree does have circular references when a child requires a parent module.exports = function(filename, cb) { var paths = priv_module.Module._nodeModulePaths(path.dirname(filename)); // entry parent specifies the base node modules path var entry_parent = { id: filename, filename: filename, paths: paths }; from_filename(filename, entry_parent, function(err, details) { // clear the global cache cache = {}; cb(err, details); }); }