| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- 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 asyncForEach (items, todo, done) {
- var remaining = items.length
- if (remaining === 0) return done()
- var seenErr
- items.forEach(function (item) {
- todo(item, handleComplete)
- })
- function handleComplete (err) {
- if (seenErr) return
- if (err) {
- seenErr = true
- return done(err)
- }
- if (--remaining === 0) done()
- }
- }
- 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, er, cache, fromLink) {
- if (!(this instanceof Node)) {
- return new Node(pkg, logical, physical, er, cache)
- }
- var node = cache[physical] || this
- if (fromLink && cache[physical]) return cache[physical]
- debug(node.constructor.name, dpath(physical), pkg && pkg._id)
- const parent = path.dirname(logical)
- if (parent[0] === '@') {
- node.name = path.basename(parent) + '/' + path.basename(logical)
- } else {
- node.name = path.basename(logical)
- }
- node.path = logical
- node.realpath = physical
- node.error = er
- if (!cache[physical]) {
- node.id = ID++
- node.package = pkg || {}
- node.parent = null
- node.isLink = false
- node.children = []
- }
- return cache[physical] = node
- }
- Node.prototype.package = null
- Node.prototype.path = ''
- Node.prototype.realpath = ''
- Node.prototype.children = null
- Node.prototype.error = null
- function Link (pkg, logical, physical, realpath, er, cache) {
- if (cache[physical]) return cache[physical]
- if (!(this instanceof Link)) {
- return new Link(pkg, logical, physical, realpath, er, cache)
- }
- cache[physical] = this
- debug(this.constructor.name, dpath(physical), pkg && pkg._id)
- const dir = path.dirname(logical)
- const parent = path.dirname(dir)
- if (parent[0] === '@') {
- this.name = path.basename(parent) + '/' + path.basename(dir)
- } else {
- this.name = path.basename(dir)
- }
- this.id = ID++
- this.path = logical
- this.realpath = realpath
- this.package = pkg || {}
- this.parent = null
- this.target = new Node(this.package, logical, realpath, er, cache, true)
- this.isLink = true
- this.children = this.target.children
- this.error = er
- }
- 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))
- return fs.realpath(physical, thenReadPackageJson)
- var realpath
- function thenReadPackageJson (er, real) {
- if (er) {
- var node = new Node(null, logical, physical, er, cache)
- return cb(null, node)
- }
- debug('realpath l=%j p=%j real=%j', dpath(logical), dpath(physical), dpath(real))
- var pj = path.join(real, 'package.json')
- realpath = real
- return rpj(pj, thenCreateNode)
- }
- function thenCreateNode (er, pkg) {
- pkg = pkg || null
- var node
- if (physical === realpath) {
- node = new Node(pkg, logical, physical, er, cache)
- } else {
- node = new Link(pkg, logical, physical, realpath, er, cache)
- }
- cb(null, node)
- }
- }
- function loadChildren (node, cache, filterWith, cb) {
- debug('loadChildren', dpath(node.path))
- // needed 'cause we process all kids async-like and errors
- // short circuit, so we have to be sure that after an error
- // the cbs from other kids don't result in calling cb a second
- // (or more) time.
- cb = once(cb)
- var nm = path.join(node.path, 'node_modules')
- var rm
- return fs.realpath(path.join(node.path, 'node_modules'), thenReaddir)
- function thenReaddir (er, real_nm) {
- if (er) return cb(null, node)
- rm = real_nm
- readdir(nm, thenLoadKids)
- }
- function thenLoadKids (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] !== '.' && (!filterWith || filterWith(node, kid))
- })
- asyncForEach(kids, thenLoadNode, thenSortChildren)
- }
- function thenLoadNode (kid, done) {
- var kidPath = path.join(nm, kid)
- var kidRealPath = path.join(rm, kid)
- loadNode(kidPath, kidRealPath, cache, andAddNode(done))
- }
- function andAddNode (done) {
- return function (er, kid) {
- if (er) return done(er)
- node.children.push(kid)
- kid.parent = node
- done()
- }
- }
- function thenSortChildren (er) {
- sortChildren(node)
- cb(er, node)
- }
- }
- function sortChildren (node) {
- node.children = node.children.sort(function (a, b) {
- a = a.package.name ? a.package.name.toLowerCase() : a.path
- b = b.package.name ? b.package.name.toLowerCase() : b.path
- return a > b ? 1 : -1
- })
- }
- function loadTree (node, did, cache, filterWith, cb) {
- debug('loadTree', dpath(node.path), !!cache[node.path])
- if (did[node.realpath]) {
- return dz(cb)(null, node)
- }
- did[node.realpath] = true
- // needed 'cause we process all kids async-like and errors
- // short circuit, so we have to be sure that after an error
- // the cbs from other kids don't result in calling cb a second
- // (or more) time.
- cb = once(cb)
- return loadChildren(node, cache, filterWith, thenProcessChildren)
- function thenProcessChildren (er, node) {
- if (er) return cb(er)
- var kids = node.children.filter(function (kid) {
- return !did[kid.realpath]
- })
- return asyncForEach(kids, loadTreeForKid, cb)
- }
- function loadTreeForKid (kid, done) {
- loadTree(kid, did, cache, filterWith, done)
- }
- }
- function rpt (root, filterWith, cb) {
- if (!cb) {
- cb = filterWith
- filterWith = null
- }
- var cache = Object.create(null)
- var topErr
- var tree
- return fs.realpath(root, thenLoadNode)
- function thenLoadNode (er, realRoot) {
- if (er) return cb(er)
- debug('rpt', dpath(realRoot))
- loadNode(root, realRoot, cache, thenLoadTree)
- }
- function thenLoadTree(er, node) {
- // even if there's an error, it's fine, as long as we got a node
- if (node) {
- topErr = er
- tree = node
- loadTree(node, {}, cache, filterWith, thenHandleErrors)
- } else {
- cb(er)
- }
- }
- function thenHandleErrors (er) {
- cb(topErr && topErr.code !== 'ENOENT' ? topErr : er, tree)
- }
- }
|