0
# Tree Traversal
1
2
Tree traversal system in Istanbul Lib Report implements the visitor pattern for walking coverage trees and generating reports with different organizational strategies. The system provides flexible navigation through coverage data with support for custom processing at each node.
3
4
## Capabilities
5
6
### Visitor Pattern
7
8
The core visitor implementation allows custom processing during tree traversal with specific callbacks for different node types and traversal phases.
9
10
```javascript { .api }
11
/**
12
* Visitor for traversing coverage trees with custom callbacks
13
*/
14
class Visitor {
15
/**
16
* Create a visitor with delegate methods
17
* @param {Object} delegate - Partial visitor implementing methods of interest
18
*/
19
constructor(delegate);
20
21
/**
22
* Called before traversal begins
23
* @param {Object} root - Root node of the tree
24
* @param {Object} state - Optional state passed around during traversal
25
*/
26
onStart(root, state);
27
28
/**
29
* Called for every summary node during traversal
30
* @param {Object} node - Current summary node
31
* @param {Object} state - Optional state passed around during traversal
32
*/
33
onSummary(node, state);
34
35
/**
36
* Called for every detail node during traversal
37
* @param {Object} node - Current detail node
38
* @param {Object} state - Optional state passed around during traversal
39
*/
40
onDetail(node, state);
41
42
/**
43
* Called after all children have been visited for a summary node
44
* @param {Object} node - Current summary node
45
* @param {Object} state - Optional state passed around during traversal
46
*/
47
onSummaryEnd(node, state);
48
49
/**
50
* Called after traversal ends
51
* @param {Object} root - Root node of the tree
52
* @param {Object} state - Optional state passed around during traversal
53
*/
54
onEnd(root, state);
55
}
56
```
57
58
**Usage Example:**
59
60
```javascript
61
const visitor = new Visitor({
62
onStart(root, state) {
63
console.log('Starting traversal');
64
state.fileCount = 0;
65
},
66
67
onDetail(node, state) {
68
state.fileCount++;
69
const summary = node.getCoverageSummary();
70
console.log(`File: ${node.getQualifiedName()}`);
71
console.log(` Statements: ${summary.statements.pct}%`);
72
},
73
74
onEnd(root, state) {
75
console.log(`Processed ${state.fileCount} files`);
76
}
77
});
78
79
// Use with tree
80
tree.visit(visitor, {});
81
```
82
83
### Composite Visitor
84
85
Allows combining multiple visitors to process a tree with multiple different operations in a single traversal.
86
87
```javascript { .api }
88
/**
89
* Composite visitor that delegates to multiple visitors
90
*/
91
class CompositeVisitor extends Visitor {
92
/**
93
* Create a composite visitor
94
* @param {Array|Object} visitors - Array of visitors or single visitor
95
*/
96
constructor(visitors);
97
}
98
```
99
100
**Usage Example:**
101
102
```javascript
103
const summaryVisitor = {
104
onSummary(node) {
105
console.log(`Summary: ${node.getQualifiedName()}`);
106
}
107
};
108
109
const detailVisitor = {
110
onDetail(node) {
111
console.log(`Detail: ${node.getQualifiedName()}`);
112
}
113
};
114
115
const composite = new CompositeVisitor([summaryVisitor, detailVisitor]);
116
tree.visit(composite);
117
```
118
119
### Base Node
120
121
Abstract base class for tree nodes providing common navigation and traversal methods.
122
123
```javascript { .api }
124
/**
125
* Base class for tree nodes
126
*/
127
class BaseNode {
128
/**
129
* Check if this node is the root of the tree
130
* @returns {boolean} True if this is the root node
131
*/
132
isRoot();
133
134
/**
135
* Visit all nodes depth-first from this node down
136
* @param {Visitor} visitor - Visitor called during tree traversal
137
* @param {Object} [state] - Optional state passed around during traversal
138
*/
139
visit(visitor, state);
140
141
/**
142
* Check if this is a summary node (abstract)
143
* @returns {boolean} True if this is a summary node
144
*/
145
isSummary();
146
147
/**
148
* Get the parent node (abstract)
149
* @returns {BaseNode|null} Parent node or null if root
150
*/
151
getParent();
152
153
/**
154
* Get child nodes (abstract)
155
* @returns {Array<BaseNode>} Array of child nodes
156
*/
157
getChildren();
158
}
159
```
160
161
### Base Tree
162
163
Abstract base class for coverage trees providing tree-wide traversal operations.
164
165
```javascript { .api }
166
/**
167
* Abstract base class for a coverage tree
168
*/
169
class BaseTree {
170
/**
171
* Create a tree with the specified root node
172
* @param {BaseNode} root - Root node of the tree
173
*/
174
constructor(root);
175
176
/**
177
* Returns the root node of the tree
178
* @returns {BaseNode} Root node
179
*/
180
getRoot();
181
182
/**
183
* Visits the tree depth-first with the supplied partial visitor
184
* @param {Visitor|Object} visitor - Visitor or partial visitor object
185
* @param {Object} [state] - State to be passed around during traversal
186
*/
187
visit(visitor, state);
188
}
189
```
190
191
**Usage Example:**
192
193
```javascript
194
// Get tree from context
195
const tree = context.getTree('nested');
196
197
// Visit with partial visitor
198
tree.visit({
199
onStart(root) {
200
console.log('=== Coverage Report ===');
201
},
202
203
onSummary(node) {
204
const summary = node.getCoverageSummary();
205
const name = node.getQualifiedName() || 'All files';
206
console.log(`\\n${name}:`);
207
console.log(` Statements: ${summary.statements.covered}/${summary.statements.total} (${summary.statements.pct}%)`);
208
console.log(` Branches: ${summary.branches.covered}/${summary.branches.total} (${summary.branches.pct}%)`);
209
console.log(` Functions: ${summary.functions.covered}/${summary.functions.total} (${summary.functions.pct}%)`);
210
console.log(` Lines: ${summary.lines.covered}/${summary.lines.total} (${summary.lines.pct}%)`);
211
},
212
213
onDetail(node) {
214
const summary = node.getCoverageSummary();
215
console.log(` ${node.getRelativeName()}: ${summary.statements.pct}%`);
216
}
217
});
218
```
219
220
### Path Utility
221
222
Path management utility for normalized file path operations across platforms.
223
224
```javascript { .api }
225
/**
226
* Path utility for normalized file path operations
227
*/
228
class Path {
229
/**
230
* Create a path from string or array
231
* @param {string|Array} strOrArray - Path string or array of path elements
232
*/
233
constructor(strOrArray);
234
235
/**
236
* Convert path to string with '/' separators
237
* @returns {string} Path as string
238
*/
239
toString();
240
241
/**
242
* Check if path has parent elements
243
* @returns {boolean} True if path has parent
244
*/
245
hasParent();
246
247
/**
248
* Get parent path
249
* @returns {Path} Parent path instance
250
* @throws {Error} If path has no parent
251
*/
252
parent();
253
254
/**
255
* Get copy of path elements array
256
* @returns {Array} Array of path elements
257
*/
258
elements();
259
260
/**
261
* Get the last element of the path
262
* @returns {string} Last path element
263
*/
264
name();
265
266
/**
267
* Check if this path contains another path
268
* @param {Path} other - Other path to compare
269
* @returns {boolean} True if this path contains the other
270
*/
271
contains(other);
272
273
/**
274
* Check if this path is an ancestor of another path
275
* @param {Path} other - Other path to compare
276
* @returns {boolean} True if this is ancestor of other
277
*/
278
ancestorOf(other);
279
280
/**
281
* Check if this path is a descendant of another path
282
* @param {Path} other - Other path to compare
283
* @returns {boolean} True if this is descendant of other
284
*/
285
descendantOf(other);
286
287
/**
288
* Get common prefix path with another path
289
* @param {Path} other - Other path to compare
290
* @returns {Path} Common prefix path
291
*/
292
commonPrefixPath(other);
293
294
/**
295
* Compare two paths for sorting
296
* @param {Path} a - First path
297
* @param {Path} b - Second path
298
* @returns {number} -1, 0, or 1 for sorting
299
*/
300
static compare(a, b);
301
302
/**
303
* Array-like operations - Path implements array manipulation methods
304
*/
305
306
/**
307
* Add elements to the end of the path
308
* @param {...string} elements - Elements to add
309
* @returns {number} New length of the path
310
*/
311
push(...elements);
312
313
/**
314
* Remove and return the last element of the path
315
* @returns {string} Last element, or undefined if empty
316
*/
317
pop();
318
319
/**
320
* Remove and return the first element of the path
321
* @returns {string} First element, or undefined if empty
322
*/
323
shift();
324
325
/**
326
* Add elements to the beginning of the path
327
* @param {...string} elements - Elements to add
328
* @returns {number} New length of the path
329
*/
330
unshift(...elements);
331
332
/**
333
* Change path contents by removing/inserting elements
334
* @param {number} start - Index to start changes
335
* @param {number} [deleteCount] - Number of elements to remove
336
* @param {...string} items - Elements to insert
337
* @returns {Array} Array of removed elements
338
*/
339
splice(start, deleteCount, ...items);
340
341
/**
342
* Length of the path (number of elements)
343
* @type {number}
344
*/
345
length;
346
}
347
```
348
349
**Usage Examples:**
350
351
```javascript
352
const { Path } = require('istanbul-lib-report/lib/path');
353
354
// Create paths
355
const path1 = new Path('/src/components/Button.js');
356
const path2 = new Path(['src', 'components', 'Button.js']);
357
358
// Path operations
359
console.log(path1.toString()); // "src/components/Button.js"
360
console.log(path1.name()); // "Button.js"
361
console.log(path1.hasParent()); // true
362
363
const parentPath = path1.parent(); // Path for "src/components"
364
console.log(parentPath.toString()); // "src/components"
365
366
// Path relationships
367
const rootPath = new Path(['src']);
368
console.log(rootPath.ancestorOf(path1)); // true
369
console.log(path1.descendantOf(rootPath)); // true
370
371
// Common prefix
372
const path3 = new Path(['src', 'utils', 'helpers.js']);
373
const common = path1.commonPrefixPath(path3); // Path for "src"
374
375
// Array-like operations
376
const workingPath = new Path(['src', 'components']);
377
console.log(workingPath.length); // 2
378
379
// Add elements
380
workingPath.push('Button.js'); // ['src', 'components', 'Button.js']
381
workingPath.unshift('project'); // ['project', 'src', 'components', 'Button.js']
382
383
// Remove elements
384
const filename = workingPath.pop(); // 'Button.js', path is now ['project', 'src', 'components']
385
const project = workingPath.shift(); // 'project', path is now ['src', 'components']
386
387
// Splice operations
388
const removed = workingPath.splice(1, 0, 'ui'); // [], path is now ['src', 'ui', 'components']
389
workingPath.splice(1, 1, 'shared'); // ['ui'], path is now ['src', 'shared', 'components']
390
```
391
392
## Types
393
394
```javascript { .api }
395
interface VisitorDelegate {
396
onStart?(root: Object, state: Object): void;
397
onSummary?(node: Object, state: Object): void;
398
onDetail?(node: Object, state: Object): void;
399
onSummaryEnd?(node: Object, state: Object): void;
400
onEnd?(root: Object, state: Object): void;
401
}
402
403
interface TreeNode {
404
isRoot(): boolean;
405
isSummary(): boolean;
406
getParent(): TreeNode | null;
407
getChildren(): TreeNode[];
408
getQualifiedName(): string;
409
getRelativeName(): string;
410
getCoverageSummary(): Object;
411
visit(visitor: Visitor, state?: Object): void;
412
}
413
414
interface Tree {
415
getRoot(): TreeNode;
416
visit(visitor: Visitor | VisitorDelegate, state?: Object): void;
417
}
418
```