reqursive
var detective = require('detective')
, async = require('async')
, nub = require('nub')
, fs = require('fs')
, path = require('path')
, mod = require('module').Module
var findPath = mod._findPath
, lookupPaths = mod._resolveLookupPaths
, nodeModules = mod._nodeModulePaths
function findRelative(request, directory, parent) {
var target = path.resolve(directory, request)
var exists = false
;[
target
, target + '.js'
, target + '.json'
, target + '.coffee'
, target + '/index.js'
].forEach(function(attempt) {
var ok = (path.existsSync || fs.existsSync)(attempt)
exists = ok || exists
if (ok) {
target = attempt
}
})
return {
'id': path.basename(target)
, 'filename': target
, 'parents': [parent.filename]
, 'module': false
, 'native': false
, 'mgroup': parent.mgroup || false
}
};
function findModule(request, parent) {
var directory
, filename
, paths
, id
directory = path.resolve(parent.filename)
directory = path.dirname(directory)
paths = lookupPaths(request, {
id: parent.id || request
, paths: nodeModules(directory)
})
if (paths && paths[0] && !paths[1].length) {
return {
'id': request
, 'module': true
, 'native': true
, 'parents': [parent.filename]
, 'mgroup': request
};
} else
if (!paths || !paths.length) {
return false
}
id = paths[0]
paths = [directory].concat(paths[1])
filename = findPath(id, paths)
return {
'id': id
, 'filename': filename
, 'parents': [parent.filename]
, 'module': true
, 'native': false
, 'mgroup': id
};
};
function entryObject(filename) {
return {
id: path.basename(filename)
, parents: []
, module: false
, mgroup: false
, filename: filename
};
};
function getChildren(parent, callback) {
if (typeof parent === 'string') {
parent = { filename: parent }
}
var filename = parent.filename
fs.readFile(filename, 'utf8', function(err, body) {
if (err) return callback(null, [])
body = body.split('\n')
if (/^\#!\//.test(body[0])) {
body = body.slice(1)
}
body = body.join('\n')
try {
var modules = detective(body);
} catch(e) {
return callback(e)
}
modules = modules.map(function(id) {
if (id.match(/^[\.\/]/)) {
return findRelative(id
, path.resolve(path.dirname(filename))
, parent
);
} else {
return findModule(id, parent);
}
}).filter(function(script) {
return script && script.id
})
modules = nub.by(modules, function(one, two) {
if (one.module && two.module) {
return one.id === two.id
}
return one.filename === two.filename
})
callback(null, modules)
})
};
function getChildrenRecursive(entry, options, callback) {
var results = {}
, entry = Array.isArray(entry) ? entry : [entry]
, first = true
, queue
if (typeof options === 'function') {
callback = options
options = {}
}
callback = callback || function(){}
options = options || {}
options.traverseModules = !!options.traverseModules
entry = entry.map(function(filename) {
filename = path.resolve(filename)
results[filename] = entryObject(filename)
return filename
})
queue = [].concat(entry)
async.whilst(function() {
return queue.length > 0
}, iteration
, finished)
function iteration(next) {
var absolute = path.resolve(queue.shift())
getChildren(results[absolute] || absolute, function(err, children) {
if (err && first) {
return next(err)
} else
if (err) {
results[absolute].error = err
return next()
}
first = false
children.forEach(function(child) {
if (results[child.filename]) {
results[child.filename].parents.push(absolute)
return
}
results[
child.native ? 'native::' + child.id :
child.filename
] = child
if (child.native) return;
if (!child.module || options.traverseModules) {
queue.push(child.filename)
}
})
next()
});
};
function finished(err) {
if (err) return callback(err);
var response = []
Object.keys(results).forEach(function(key) {
var dirname = path.dirname(entry[0])
, relative = path.relative(dirname, key)
results[key].parents = nub(
results[key].parents || []
).map(function(script) {
return options.absolute ? script : path.relative(dirname, script)
})
if (results[key].filename && !options.absolute) {
results[key].filename = path.relative(dirname, results[key].filename)
}
response.push(results[key])
});
callback(null, response)
};
};
module.exports = getChildrenRecursive
module.exports.children = function children(filename, callback) {
var absolute = path.resolve(filename)
getChildren(filename, function(err, children) {
if (err) return callback(err)
children.unshift(entryObject(absolute))
callback(null, children)
})
};