summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/read-package-tree/rpt.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/read-package-tree/rpt.js')
-rw-r--r--deps/npm/node_modules/read-package-tree/rpt.js380
1 files changed, 150 insertions, 230 deletions
diff --git a/deps/npm/node_modules/read-package-tree/rpt.js b/deps/npm/node_modules/read-package-tree/rpt.js
index fd43be9c22..b12a09dfb3 100644
--- a/deps/npm/node_modules/read-package-tree/rpt.js
+++ b/deps/npm/node_modules/read-package-tree/rpt.js
@@ -1,249 +1,169 @@
-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()
+const fs = require('fs')
+/* istanbul ignore next */
+const promisify = require('util').promisify || require('util-promisify')
+const { resolve, basename, dirname, join } = require('path')
+const rpj = promisify(require('read-package-json'))
+const readdir = promisify(require('readdir-scoped-modules'))
+const realpath = require('./realpath.js')
+
+let ID = 0
+class Node {
+ constructor (pkg, logical, physical, er, cache) {
+ // should be impossible.
+ const cached = cache.get(physical)
+ /* istanbul ignore next */
+ if (cached && !cached.then)
+ throw new Error('re-creating already instantiated node')
+
+ cache.set(physical, this)
+
+ const parent = basename(dirname(logical))
+ if (parent.charAt(0) === '@')
+ this.name = `${parent}/${basename(logical)}`
+ else
+ this.name = basename(logical)
+ this.path = logical
+ this.realpath = physical
+ this.error = er
+ this.id = ID++
+ this.package = pkg || {}
+ this.parent = null
+ this.isLink = false
+ this.children = []
}
}
-function dpath (p) {
- if (!p) return ''
- if (p.indexOf(process.cwd()) === 0) {
- p = p.substr(process.cwd().length + 1)
+class Link extends Node {
+ constructor (pkg, logical, physical, realpath, er, cache) {
+ super(pkg, logical, physical, er, cache)
+
+ // if the target has started, but not completed, then
+ // a Promise will be in the cache to indicate this.
+ const cachedTarget = cache.get(realpath)
+ if (cachedTarget && cachedTarget.then)
+ cachedTarget.then(node => {
+ this.target = node
+ this.children = node.children
+ })
+
+ this.target = cachedTarget || new Node(pkg, logical, realpath, er, cache)
+ this.realpath = realpath
+ this.isLink = true
+ this.error = er
+ this.children = this.target.children
}
- 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.basename(path.dirname(logical))
- if (parent[0] === '@') {
- node.name = 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 parent = path.basename(path.dirname(logical))
- if (parent[0] === '@') {
- this.name = parent + '/' + path.basename(logical)
- } else {
- this.name = path.basename(logical)
- }
- 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)
- }
+// this is the way it is to expose a timing issue which is difficult to
+// test otherwise. The creation of a Node may take slightly longer than
+// the creation of a Link that targets it. If the Node has _begun_ its
+// creation phase (and put a Promise in the cache) then the Link will
+// get a Promise as its cachedTarget instead of an actual Node object.
+// This is not a problem, because it gets resolved prior to returning
+// the tree or attempting to load children. However, it IS remarkably
+// difficult to get to happen in a test environment to verify reliably.
+// Hence this kludge.
+const newNode = (pkg, logical, physical, er, cache) =>
+ process.env._TEST_RPT_SLOW_LINK_TARGET_ === '1'
+ ? new Promise(res => setTimeout(() =>
+ res(new Node(pkg, logical, physical, er, cache)), 10))
+ : new Node(pkg, logical, physical, er, cache)
+
+const loadNode = (logical, physical, cache, rpcache, stcache) => {
+ // cache temporarily holds a promise placeholder so we
+ // don't try to create the same node multiple times.
+ // this is very rare to encounter, given the aggressive
+ // caching on fs.realpath and fs.lstat calls, but
+ // it can happen in theory.
+ const cached = cache.get(physical)
+ /* istanbul ignore next */
+ if (cached)
+ return Promise.resolve(cached)
+
+ const p = realpath(physical, rpcache, stcache, 0).then(real =>
+ rpj(join(real, 'package.json'))
+ .then(pkg => [pkg, null], er => [null, er])
+ .then(([pkg, er]) =>
+ physical === real ? newNode(pkg, logical, physical, er, cache)
+ : new Link(pkg, logical, physical, real, er, cache)
+ ),
+ // if the realpath fails, don't bother with the rest
+ er => new Node(null, logical, physical, er, cache))
+
+ cache.set(physical, p)
+ return p
}
-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))
+const loadChildren = (node, cache, filterWith, rpcache, stcache) => {
+ // if a Link target has started, but not completed, then
+ // a Promise will be in the cache to indicate this.
+ //
+ // XXX When we can one day loadChildren on the link *target* instead of
+ // the link itself, to match real dep resolution, then we may end up with
+ // a node target in the cache that isn't yet done resolving when we get
+ // here. For now, though, this line will never be reached, so it's hidden
+ //
+ // if (node.then)
+ // return node.then(node => loadChildren(node, cache, filterWith, rpcache, stcache))
+
+ const nm = join(node.path, 'node_modules')
+ return realpath(nm, rpcache, stcache, 0)
+ .then(rm => readdir(rm).then(kids => [rm, kids]))
+ .then(([rm, kids]) => Promise.all(
+ kids.filter(kid =>
+ kid.charAt(0) !== '.' && (!filterWith || filterWith(node, kid)))
+ .map(kid => loadNode(join(nm, kid), join(rm, kid), cache, rpcache, stcache)))
+ ).then(kidNodes => {
+ kidNodes.forEach(k => k.parent = node)
+ node.children.push.apply(node.children, kidNodes.sort((a, b) =>
+ (a.package.name ? a.package.name.toLowerCase() : a.path)
+ .localeCompare(
+ (b.package.name ? b.package.name.toLowerCase() : b.path)
+ )))
+ return node
})
-
- 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)
- }
+ .catch(() => 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
- })
+const loadTree = (node, did, cache, filterWith, rpcache, stcache) => {
+ // impossible except in pathological ELOOP cases
+ /* istanbul ignore next */
+ if (did.has(node.realpath))
+ return Promise.resolve(node)
+
+ did.add(node.realpath)
+
+ // load children on the target, not the link
+ return loadChildren(node, cache, filterWith, rpcache, stcache)
+ .then(node => Promise.all(
+ node.children
+ .filter(kid => !did.has(kid.realpath))
+ .map(kid => loadTree(kid, did, cache, filterWith, rpcache, stcache))
+ )).then(() => node)
}
-function loadTree (node, did, cache, filterWith, cb) {
- debug('loadTree', dpath(node.path), !!cache[node.path])
-
- if (did[node.realpath]) {
- return dz(cb)(null, node)
+// XXX Drop filterWith and/or cb in next semver major bump
+const rpt = (root, filterWith, cb) => {
+ if (!cb && typeof filterWith === 'function') {
+ cb = filterWith
+ filterWith = null
}
- did[node.realpath] = true
+ const cache = new Map()
+ // we can assume that the cwd is real enough
+ const cwd = process.cwd()
+ const rpcache = new Map([[ cwd, cwd ]])
+ const stcache = new Map()
+ const p = realpath(root, rpcache, stcache, 0)
+ .then(realRoot => loadNode(root, realRoot, cache, rpcache, stcache))
+ .then(node => loadTree(node, new Set(), cache, filterWith, rpcache, stcache))
- // 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]
- })
+ if (typeof cb === 'function')
+ p.then(tree => cb(null, tree), cb)
- return asyncForEach(kids, loadTreeForKid, cb)
- }
- function loadTreeForKid (kid, done) {
- loadTree(kid, did, cache, filterWith, done)
- }
+ return p
}
-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)
- }
-}
+rpt.Node = Node
+rpt.Link = Link
+module.exports = rpt