@speclynx/apidom-traverse 1.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,238 @@
1
+ # @speclynx/apidom-traverse
2
+
3
+ `@speclynx/apidom-traverse` provides traversal utilities for ApiDOM structures.
4
+
5
+ ## Installation
6
+
7
+ You can install this package via [npm CLI](https://docs.npmjs.com/cli) by running the following command:
8
+
9
+ ```sh
10
+ $ npm install @speclynx/apidom-traverse
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### traverse
16
+
17
+ The core traversal function that walks an ApiDOM tree using visitors.
18
+
19
+ ```js
20
+ import { ObjectElement } from '@speclynx/apidom-datamodel';
21
+ import { traverse } from '@speclynx/apidom-traverse';
22
+
23
+ const element = new ObjectElement({ a: 'b' });
24
+
25
+ traverse(element, {
26
+ enter(path) {
27
+ console.log('entering:', path.node.element);
28
+ },
29
+ leave(path) {
30
+ console.log('leaving:', path.node.element);
31
+ },
32
+ });
33
+ ```
34
+
35
+ ### traverseAsync
36
+
37
+ Async version of traverse that supports async visitors.
38
+
39
+ ```js
40
+ import { ObjectElement } from '@speclynx/apidom-datamodel';
41
+ import { traverseAsync } from '@speclynx/apidom-traverse';
42
+
43
+ const element = new ObjectElement({ a: 'b' });
44
+
45
+ await traverseAsync(element, {
46
+ async enter(path) {
47
+ await someAsyncOperation(path.node);
48
+ },
49
+ });
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Operations
55
+
56
+ Higher-level functions built on top of `traverse` for common use cases.
57
+ All operations use **data-first** signatures: the element comes before the predicate/options.
58
+
59
+ ### filter
60
+
61
+ Finds all elements matching the predicate.
62
+
63
+ ```js
64
+ import { ObjectElement, isNumberElement } from '@speclynx/apidom-datamodel';
65
+ import { filter } from '@speclynx/apidom-traverse';
66
+
67
+ const element = new ObjectElement({ a: 'b', c: 2 });
68
+
69
+ filter(element, isNumberElement); // => [NumberElement<2>]
70
+ ```
71
+
72
+ ### find
73
+
74
+ Find first element that satisfies the provided predicate.
75
+
76
+ ```js
77
+ import { ObjectElement, isNumberElement } from '@speclynx/apidom-datamodel';
78
+ import { find } from '@speclynx/apidom-traverse';
79
+
80
+ const element = new ObjectElement({ a: 'b', c: 2 });
81
+
82
+ find(element, isNumberElement); // => NumberElement<2>
83
+ ```
84
+
85
+ ### findAtOffset
86
+
87
+ ApiDOM nodes can be associated with source maps. This function finds the most inner node at the given offset.
88
+ If includeRightBound is set, also finds nodes that end at the given offset.
89
+
90
+ ```js
91
+ import { findAtOffset } from '@speclynx/apidom-traverse';
92
+
93
+ findAtOffset(elementWithSourceMaps, 3); // => returns most inner node at offset 3
94
+ findAtOffset(elementWithSourceMaps, { offset: 3, includeRightBound: true });
95
+ ```
96
+
97
+ ### reject
98
+
99
+ Complement of [filter](#filter). Finds all elements NOT matching the predicate.
100
+
101
+ ```js
102
+ import { ArrayElement, isNumberElement } from '@speclynx/apidom-datamodel';
103
+ import { reject } from '@speclynx/apidom-traverse';
104
+
105
+ const element = new ArrayElement([1, 'a']);
106
+
107
+ reject(element, isNumberElement); // => [ArrayElement, StringElement<'a'>]
108
+ ```
109
+
110
+ ### some
111
+
112
+ Tests whether at least one element passes the predicate.
113
+
114
+ ```js
115
+ import { ArrayElement, isNumberElement } from '@speclynx/apidom-datamodel';
116
+ import { some } from '@speclynx/apidom-traverse';
117
+
118
+ const element = new ArrayElement([1, 'a']);
119
+
120
+ some(element, isNumberElement); // => true
121
+ ```
122
+
123
+ ### forEach
124
+
125
+ Executes the callback on this element and all descendants.
126
+
127
+ ```js
128
+ import { ArrayElement } from '@speclynx/apidom-datamodel';
129
+ import { forEach } from '@speclynx/apidom-traverse';
130
+
131
+ const element = new ArrayElement([1, 'a']);
132
+
133
+ forEach(element, console.dir); // => prints ArrayElement, NumberElement, StringElement in this order
134
+ ```
135
+
136
+ The execution of the callback can be controlled further by providing a predicate.
137
+
138
+ ```js
139
+ import { ArrayElement, isNumberElement } from '@speclynx/apidom-datamodel';
140
+ import { forEach } from '@speclynx/apidom-traverse';
141
+
142
+ const element = new ArrayElement([1, 'a']);
143
+
144
+ forEach(element, { callback: console.dir, predicate: isNumberElement }); // => prints NumberElement<1>
145
+ ```
146
+
147
+ ### parents
148
+
149
+ Computes upwards edges from every child to its parent.
150
+
151
+ #### ObjectElement example
152
+
153
+ ```js
154
+ import { ObjectElement } from '@speclynx/apidom-datamodel';
155
+ import { parents } from '@speclynx/apidom-traverse';
156
+
157
+ const element = new ObjectElement({ key: 'value' });
158
+ const memberElement = element.getMember('key');
159
+ const { key: keyElement, value: valueElement } = memberElement;
160
+
161
+ const parentEdges = parents(element); // => WeakMap<ChildElement, ParentElement>
162
+
163
+ parentEdges.get(memberElement) === element; // => true
164
+ parentEdges.get(keyElement) === memberElement; // => true
165
+ parentEdges.get(valueElement) === memberElement; // => true
166
+ ```
167
+
168
+ #### ArrayElement example
169
+
170
+ ```js
171
+ import { ArrayElement, StringElement } from '@speclynx/apidom-datamodel';
172
+ import { parents } from '@speclynx/apidom-traverse';
173
+
174
+ const itemElement1 = new StringElement('item1');
175
+ const itemElement2 = new StringElement('item2');
176
+ const element = new ArrayElement([itemElement1, itemElement2]);
177
+
178
+ const parentEdges = parents(element); // => WeakMap<ChildElement, ParentElement>
179
+
180
+ parentEdges.get(itemElement1) === element; // => true
181
+ parentEdges.get(itemElement2) === element; // => true
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Path
187
+
188
+ The `Path` object is passed to visitor functions and provides context about the current node.
189
+
190
+ ### Properties
191
+
192
+ - `node` - The current element being visited
193
+ - `parent` - The parent container (array or element)
194
+ - `parentPath` - The Path of the parent element
195
+ - `key` - The key or index in the parent
196
+ - `index` - Numeric index if inside an array (undefined otherwise)
197
+ - `inList` - Whether the node is inside an array
198
+
199
+ ### Methods
200
+
201
+ - `skip()` - Skip visiting children of this node
202
+ - `stop()` - Stop all traversal
203
+ - `replaceWith(node)` - Replace the current node
204
+ - `remove()` - Remove the current node (in mutable mode)
205
+ - `isRoot()` - Check if this is the root node
206
+ - `getAncestry()` - Get all ancestor paths
207
+ - `getAncestorNodes()` - Get all ancestor nodes
208
+ - `getPathKeys()` - Get the path from root to current node
209
+ - `findParent(predicate)` - Find first parent matching predicate
210
+ - `find(predicate)` - Find self or parent matching predicate
211
+
212
+ ---
213
+
214
+ ## Visitor Utilities
215
+
216
+ ### getNodeType
217
+
218
+ Returns the element type name for use in visitors.
219
+
220
+ ```js
221
+ import { StringElement } from '@speclynx/apidom-datamodel';
222
+ import { getNodeType } from '@speclynx/apidom-traverse';
223
+
224
+ getNodeType(new StringElement('hello')); // => 'StringElement'
225
+ ```
226
+
227
+ ### mergeVisitors
228
+
229
+ Merges multiple visitors into a single visitor.
230
+
231
+ ```js
232
+ import { mergeVisitors } from '@speclynx/apidom-traverse';
233
+
234
+ const visitor1 = { StringElement(path) { /* ... */ } };
235
+ const visitor2 = { NumberElement(path) { /* ... */ } };
236
+
237
+ const merged = mergeVisitors([visitor1, visitor2]);
238
+ ```