npm
Version:
a package manager for JavaScript
150 lines (135 loc) • 4.08 kB
JavaScript
const debug = require('./debug.js')
const checkTree = (tree, checkUnreachable = true) => {
const log = [['START TREE CHECK', tree.path]]
// this can only happen in tests where we have a "tree" object
// that isn't actually a tree.
if (!tree.root || !tree.root.inventory)
return tree
const { inventory } = tree.root
const seen = new Set()
const check = (node, via = tree, viaType = 'self') => {
log.push([
'CHECK',
node && node.location,
via && via.location,
viaType,
'seen=' + seen.has(node),
'promise=' + !!(node && node.then),
'root=' + !!(node && node.isRoot),
])
if (!node || seen.has(node) || node.then)
return
seen.add(node)
if (node.isRoot && node !== tree.root) {
throw Object.assign(new Error('double root'), {
node: node.path,
realpath: node.realpath,
tree: tree.path,
root: tree.root.path,
via: via.path,
viaType,
log,
})
}
if (node.root !== tree.root) {
throw Object.assign(new Error('node from other root in tree'), {
node: node.path,
realpath: node.realpath,
tree: tree.path,
root: tree.root.path,
via: via.path,
viaType,
otherRoot: node.root && node.root.path,
log,
})
}
if (!node.isRoot && node.inventory.size !== 0) {
throw Object.assign(new Error('non-root has non-zero inventory'), {
node: node.path,
tree: tree.path,
root: tree.root.path,
via: via.path,
viaType,
inventory: [...node.inventory.values()].map(node =>
[node.path, node.location]),
log,
})
}
if (!node.isRoot && !inventory.has(node) && !node.dummy) {
throw Object.assign(new Error('not in inventory'), {
node: node.path,
tree: tree.path,
root: tree.root.path,
via: via.path,
viaType,
log,
})
}
const devEdges = [...node.edgesOut.values()].filter(e => e.dev)
if (!node.isTop && devEdges.length) {
throw Object.assign(new Error('dev edges on non-top node'), {
node: node.path,
tree: tree.path,
root: tree.root.path,
via: via.path,
viaType,
devEdges: devEdges.map(e => [e.type, e.name, e.spec, e.error]),
log,
})
}
if (node.path === tree.root.path && node !== tree.root) {
throw Object.assign(new Error('node with same path as root'), {
node: node.path,
tree: tree.path,
root: tree.root.path,
via: via.path,
viaType,
log,
})
}
if (!node.isLink && node.path !== node.realpath) {
throw Object.assign(new Error('non-link with mismatched path/realpath'), {
node: node.path,
tree: tree.path,
realpath: node.realpath,
root: tree.root.path,
via: via.path,
viaType,
log,
})
}
const { parent, fsParent, target } = node
check(parent, node, 'parent')
check(fsParent, node, 'fsParent')
check(target, node, 'target')
log.push(['CHILDREN', node.location, ...node.children.keys()])
for (const kid of node.children.values())
check(kid, node, 'children')
for (const kid of node.fsChildren)
check(kid, node, 'fsChildren')
for (const link of node.linksIn)
check(link, node, 'linksIn')
for (const top of node.tops)
check(top, node, 'tops')
log.push(['DONE', node.location])
}
check(tree)
if (checkUnreachable) {
for (const node of inventory.values()) {
if (!seen.has(node) && node !== tree.root) {
throw Object.assign(new Error('unreachable in inventory'), {
node: node.path,
realpath: node.realpath,
location: node.location,
root: tree.root.path,
tree: tree.path,
log,
})
}
}
}
return tree
}
// should only ever run this check in debug mode
module.exports = tree => tree
debug(() => module.exports = checkTree)