diff options
Diffstat (limited to 'node_modules/read-package-tree/rpt.js')
-rw-r--r-- | node_modules/read-package-tree/rpt.js | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/node_modules/read-package-tree/rpt.js b/node_modules/read-package-tree/rpt.js new file mode 100644 index 000000000..e6d283883 --- /dev/null +++ b/node_modules/read-package-tree/rpt.js @@ -0,0 +1,183 @@ +var fs = require('fs') +var rpj = require('read-package-json') +var path = require('path') +var dz = require('dezalgo') +var once = require('once') +var readdir = require('readdir-scoped-modules') +var debug = require('debuglog')('rpt') + +function dpath (p) { + if (!p) return '' + if (p.indexOf(process.cwd()) === 0) { + p = p.substr(process.cwd().length + 1) + } + return p +} + +module.exports = rpt + +rpt.Node = Node +rpt.Link = Link + +var ID = 0 +function Node (pkg, logical, physical, cache) { + if (cache[physical]) return cache[physical] + + if (!(this instanceof Node)) { + return new Node(pkg, logical, physical, cache) + } + + cache[physical] = this + + debug(this.constructor.name, dpath(physical), pkg && pkg._id) + + this.id = ID++ + this.package = pkg + this.path = logical + this.realpath = physical + this.parent = null + this.isLink = false + this.children = [] +} + +Node.prototype.package = null +Node.prototype.path = '' +Node.prototype.realpath = '' +Node.prototype.children = null + +function Link (pkg, logical, physical, realpath, cache) { + if (cache[physical]) return cache[physical] + + if (!(this instanceof Link)) { + return new Link(pkg, logical, physical, realpath, cache) + } + + cache[physical] = this + + debug(this.constructor.name, dpath(physical), pkg && pkg._id) + + this.id = ID++ + this.path = logical + this.realpath = realpath + this.package = pkg + this.parent = null + this.target = new Node(pkg, logical, realpath, cache) + this.isLink = true + this.children = this.target.children +} + +Link.prototype = Object.create(Node.prototype, { + constructor: { value: Link } +}) +Link.prototype.target = null +Link.prototype.realpath = '' + +function loadNode (logical, physical, cache, cb) { + debug('loadNode', dpath(logical)) + fs.realpath(physical, function (er, real) { + if (er) return cb(er) + debug('realpath l=%j p=%j real=%j', dpath(logical), dpath(physical), dpath(real)) + var pj = path.resolve(real, 'package.json') + rpj(pj, function (er, pkg) { + pkg = pkg || null + var node + if (physical === real) { + node = new Node(pkg, logical, physical, cache) + } else { + node = new Link(pkg, logical, physical, real, cache) + } + + cb(er, node) + }) + }) +} + +function loadChildren (node, cache, cb) { + debug('loadChildren', dpath(node.path)) + // don't let it be called more than once + cb = once(cb) + var nm = path.resolve(node.path, 'node_modules') + readdir(nm, function (er, kids) { + // If there are no children, that's fine, just return + if (er) return cb(null, node) + + kids = kids.filter(function (kid) { + return kid[0] !== '.' + }) + + var l = kids . length + if (l === 0) return cb(null, node) + + kids.forEach(function (kid) { + var kidPath = path.resolve(nm, kid) + var kidRealPath = path.resolve(node.realpath,'node_modules',kid) + loadNode(kidPath, kidRealPath, cache, then) + }) + + function then (er, kid) { + if (er) return cb(er) + + node.children.push(kid) + kid.parent = node + if (--l === 0) { + sortChildren(node) + return cb(null, node) + } + } + }) +} + +function sortChildren (node) { + node.children = node.children.sort(function (a, b) { + a = a.package.name.toLowerCase() + b = b.package.name.toLowerCase() + return a > b ? 1 : -1 + }) +} + +function loadTree (node, did, cache, cb) { + debug('loadTree', dpath(node.path), !!cache[node.path]) + + if (did[node.realpath]) { + return dz(cb)(null, node) + } + + did[node.realpath] = true + + cb = once(cb) + loadChildren(node, cache, function (er, node) { + if (er) return cb(er) + + var kids = node.children.filter(function (kid) { + return !did[kid.realpath] + }) + + var l = kids.length + if (l === 0) return cb(null, node) + + kids.forEach(function (kid, index) { + loadTree(kid, did, cache, then) + }) + + function then (er, kid) { + if (er) return cb(er) + + if (--l === 0) cb(null, node) + } + }) +} + +function rpt (root, cb) { + fs.realpath(root, function (er, realRoot) { + if (er) return cb(er) + debug('rpt', dpath(realRoot)) + var cache = Object.create(null) + loadNode(root, realRoot, cache, function (er, node) { + // if there's an error, it's fine, as long as we got a node + if (!node) return cb(er) + loadTree(node, {}, cache, function (lter, tree) { + cb(er && er.code !== 'ENOENT' ? er : lter, tree) + }) + }) + }) +} |