codekingpro/portable-devtools
114k
1const debug = require('./debug.js')
2
3const checkTree = (tree, checkUnreachable = true) => {
4 const log = [['START TREE CHECK', tree.path]]
5
6 // this can only happen in tests where we have a "tree" object
7 // that isn't actually a tree.
8 if (!tree.root || !tree.root.inventory) {
9 return tree
10 }
11
12 const { inventory } = tree.root
13 const seen = new Set()
14 const check = (node, via = tree, viaType = 'self') => {
15 log.push([
16 'CHECK',
17 node && node.location,
18 via && via.location,
19 viaType,
20 'seen=' + seen.has(node),
21 'promise=' + !!(node && node.then),
22 'root=' + !!(node && node.isRoot),
23 ])
24
25 if (!node || seen.has(node) || node.then) {
26 return
27 }
28
29 seen.add(node)
30
31 if (node.isRoot && node !== tree.root) {
32 throw Object.assign(new Error('double root'), {
33 node: node.path,
34 realpath: node.realpath,
35 tree: tree.path,
36 root: tree.root.path,
37 via: via.path,
38 viaType,
39 log,
40 })
41 }
42
43 if (node.root !== tree.root) {
44 throw Object.assign(new Error('node from other root in tree'), {
45 node: node.path,
46 realpath: node.realpath,
47 tree: tree.path,
48 root: tree.root.path,
49 via: via.path,
50 viaType,
51 otherRoot: node.root && node.root.path,
52 log,
53 })
54 }
55
56 if (!node.isRoot && node.inventory.size !== 0) {
57 throw Object.assign(new Error('non-root has non-zero inventory'), {
58 node: node.path,
59 tree: tree.path,
60 root: tree.root.path,
61 via: via.path,
62 viaType,
63 inventory: [...node.inventory.values()].map(node =>
64 [node.path, node.location]),
65 log,
66 })
67 }
68
69 if (!node.isRoot && !inventory.has(node) && !node.dummy) {
70 throw Object.assign(new Error('not in inventory'), {
71 node: node.path,
72 tree: tree.path,
73 root: tree.root.path,
74 via: via.path,
75 viaType,
76 log,
77 })
78 }
79
80 const devEdges = [...node.edgesOut.values()].filter(e => e.dev)
81 if (!node.isTop && devEdges.length) {
82 throw Object.assign(new Error('dev edges on non-top node'), {
83 node: node.path,
84 tree: tree.path,
85 root: tree.root.path,
86 via: via.path,
87 viaType,
88 devEdges: devEdges.map(e => [e.type, e.name, e.spec, e.error]),
89 log,
90 })
91 }
92
93 if (node.path === tree.root.path && node !== tree.root && !tree.root.isLink) {
94 throw Object.assign(new Error('node with same path as root'), {
95 node: node.path,
96 tree: tree.path,
97 root: tree.root.path,
98 via: via.path,
99 viaType,
100 log,
101 })
102 }
103
104 if (!node.isLink && node.path !== node.realpath) {
105 throw Object.assign(new Error('non-link with mismatched path/realpath'), {
106 node: node.path,
107 tree: tree.path,
108 realpath: node.realpath,
109 root: tree.root.path,
110 via: via.path,
111 viaType,
112 log,
113 })
114 }
115
116 const { parent, fsParent, target } = node
117 check(parent, node, 'parent')
118 check(fsParent, node, 'fsParent')
119 check(target, node, 'target')
120 log.push(['CHILDREN', node.location, ...node.children.keys()])
121 for (const kid of node.children.values()) {
122 check(kid, node, 'children')
123 }
124 for (const kid of node.fsChildren) {
125 check(kid, node, 'fsChildren')
126 }
127 for (const link of node.linksIn) {
128 check(link, node, 'linksIn')
129 }
130 for (const top of node.tops) {
131 check(top, node, 'tops')
132 }
133 log.push(['DONE', node.location])
134 }
135 check(tree)
136 if (checkUnreachable) {
137 for (const node of inventory.values()) {
138 if (!seen.has(node) && node !== tree.root) {
139 throw Object.assign(new Error('unreachable in inventory'), {
140 node: node.path,
141 realpath: node.realpath,
142 location: node.location,
143 root: tree.root.path,
144 tree: tree.path,
145 log,
146 })
147 }
148 }
149 }
150 return tree
151}
152
153// should only ever run this check in debug mode
154module.exports = tree => tree
155debug(() => module.exports = checkTree)
156 