@speclynx/apidom-traverse 4.0.3 → 4.0.4
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/CHANGELOG.md +6 -0
- package/package.json +8 -7
- package/src/Path.ts +0 -368
- package/src/index.ts +0 -44
- package/src/operations/filter.ts +0 -27
- package/src/operations/find-at-offset.ts +0 -61
- package/src/operations/find.ts +0 -28
- package/src/operations/for-each.ts +0 -44
- package/src/operations/parents.ts +0 -24
- package/src/operations/reject.ts +0 -17
- package/src/operations/some.ts +0 -17
- package/src/traversal.ts +0 -391
- package/src/visitors.ts +0 -490
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [4.0.4](https://github.com/speclynx/apidom/compare/v4.0.3...v4.0.4) (2026-03-12)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **release:** override minimatch 10.2.3 to fix glob pattern regression in lerna publish ([#157](https://github.com/speclynx/apidom/issues/157)) ([c2d65a0](https://github.com/speclynx/apidom/commit/c2d65a06a2187e8563a9dc9db74ba27255450e0b)), closes [lerna/lerna#4305](https://github.com/lerna/lerna/issues/4305) [isaacs/minimatch#284](https://github.com/isaacs/minimatch/issues/284)
|
|
11
|
+
|
|
6
12
|
## [4.0.3](https://github.com/speclynx/apidom/compare/v4.0.2...v4.0.3) (2026-03-11)
|
|
7
13
|
|
|
8
14
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speclynx/apidom-traverse",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"description": "Traversal and visitor utilities for walking and transforming ApiDOM structures.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"apidom",
|
|
@@ -61,14 +61,15 @@
|
|
|
61
61
|
"license": "Apache-2.0",
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@babel/runtime-corejs3": "^7.28.4",
|
|
64
|
-
"@speclynx/apidom-datamodel": "4.0.
|
|
65
|
-
"@speclynx/apidom-error": "4.0.
|
|
66
|
-
"@speclynx/apidom-json-path": "4.0.
|
|
67
|
-
"@speclynx/apidom-json-pointer": "4.0.
|
|
64
|
+
"@speclynx/apidom-datamodel": "4.0.4",
|
|
65
|
+
"@speclynx/apidom-error": "4.0.4",
|
|
66
|
+
"@speclynx/apidom-json-path": "4.0.4",
|
|
67
|
+
"@speclynx/apidom-json-pointer": "4.0.4",
|
|
68
68
|
"ramda-adjunct": "^6.0.0"
|
|
69
69
|
},
|
|
70
70
|
"files": [
|
|
71
|
-
"src
|
|
71
|
+
"src/**/*.mjs",
|
|
72
|
+
"src/**/*.cjs",
|
|
72
73
|
"dist/",
|
|
73
74
|
"types/apidom-traverse.d.ts",
|
|
74
75
|
"LICENSES",
|
|
@@ -76,5 +77,5 @@
|
|
|
76
77
|
"README.md",
|
|
77
78
|
"CHANGELOG.md"
|
|
78
79
|
],
|
|
79
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "a06f6ef3d37ad5bf860cbbf4b442bfbe3a522cc2"
|
|
80
81
|
}
|
package/src/Path.ts
DELETED
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isMemberElement,
|
|
3
|
-
isArrayElement,
|
|
4
|
-
isStringElement,
|
|
5
|
-
type Element,
|
|
6
|
-
} from '@speclynx/apidom-datamodel';
|
|
7
|
-
import { compile as compileJSONPointer } from '@speclynx/apidom-json-pointer';
|
|
8
|
-
import { NormalizedPath } from '@speclynx/apidom-json-path';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Possible return values from a visitor function.
|
|
12
|
-
* @public
|
|
13
|
-
*/
|
|
14
|
-
export type VisitorResult<TNode> = void | undefined | TNode | Promise<void | undefined | TNode>;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Visitor function signature - receives a Path object.
|
|
18
|
-
* @public
|
|
19
|
-
*/
|
|
20
|
-
export type VisitorFn<TNode, TVisitor = unknown> = (
|
|
21
|
-
this: TVisitor,
|
|
22
|
-
path: Path<TNode>,
|
|
23
|
-
) => VisitorResult<TNode>;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Path represents a node's position in the tree during traversal.
|
|
27
|
-
* Inspired by Babel's NodePath API.
|
|
28
|
-
* @public
|
|
29
|
-
*/
|
|
30
|
-
export class Path<TNode = Element> {
|
|
31
|
-
/**
|
|
32
|
-
* The current AST node.
|
|
33
|
-
*/
|
|
34
|
-
public node: TNode;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The key of this node in its parent.
|
|
38
|
-
* `undefined` for the root node.
|
|
39
|
-
*/
|
|
40
|
-
public readonly key: PropertyKey | undefined;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* The index if this node is in an array.
|
|
44
|
-
* Same as `key` when parent property is an array, `undefined` otherwise.
|
|
45
|
-
*/
|
|
46
|
-
public readonly index: number | undefined;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* The parent node.
|
|
50
|
-
* `undefined` for the root node.
|
|
51
|
-
*/
|
|
52
|
-
public readonly parent: TNode | undefined;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* The parent Path.
|
|
56
|
-
* `null` for the root node.
|
|
57
|
-
*/
|
|
58
|
-
public readonly parentPath: Path<TNode> | null;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Whether this node is inside an array in the parent.
|
|
62
|
-
*/
|
|
63
|
-
public readonly inList: boolean;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Internal state for traversal control.
|
|
67
|
-
*/
|
|
68
|
-
#shouldSkip: boolean = false;
|
|
69
|
-
#shouldStop: boolean = false;
|
|
70
|
-
#removed: boolean = false;
|
|
71
|
-
#replaced: boolean = false;
|
|
72
|
-
#replacementNode: TNode | undefined;
|
|
73
|
-
#stale: boolean = false;
|
|
74
|
-
|
|
75
|
-
constructor(
|
|
76
|
-
node: TNode,
|
|
77
|
-
parent: TNode | undefined,
|
|
78
|
-
parentPath: Path<TNode> | null,
|
|
79
|
-
key: PropertyKey | undefined,
|
|
80
|
-
inList: boolean,
|
|
81
|
-
) {
|
|
82
|
-
this.node = node;
|
|
83
|
-
this.parent = parent;
|
|
84
|
-
this.parentPath = parentPath;
|
|
85
|
-
this.key = key;
|
|
86
|
-
this.index = inList && typeof key === 'number' ? key : undefined;
|
|
87
|
-
this.inList = inList;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// ===========================================================================
|
|
91
|
-
// Traversal state
|
|
92
|
-
// ===========================================================================
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Whether skip() was called on this path.
|
|
96
|
-
*/
|
|
97
|
-
get shouldSkip(): boolean {
|
|
98
|
-
return this.#shouldSkip;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Whether stop() was called on this path.
|
|
103
|
-
*/
|
|
104
|
-
get shouldStop(): boolean {
|
|
105
|
-
return this.#shouldStop;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Whether this node was removed.
|
|
110
|
-
*/
|
|
111
|
-
get removed(): boolean {
|
|
112
|
-
return this.#removed;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ===========================================================================
|
|
116
|
-
// Ancestry
|
|
117
|
-
// ===========================================================================
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Returns true if this is the root path.
|
|
121
|
-
*/
|
|
122
|
-
isRoot(): boolean {
|
|
123
|
-
return this.parentPath === null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Get the depth of this path (0 for root).
|
|
128
|
-
*/
|
|
129
|
-
get depth(): number {
|
|
130
|
-
let depth = 0;
|
|
131
|
-
let current: Path<TNode> | null = this.parentPath;
|
|
132
|
-
while (current !== null) {
|
|
133
|
-
depth += 1;
|
|
134
|
-
current = current.parentPath;
|
|
135
|
-
}
|
|
136
|
-
return depth;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get all ancestor paths from immediate parent to root.
|
|
141
|
-
*/
|
|
142
|
-
getAncestry(): Path<TNode>[] {
|
|
143
|
-
const ancestry: Path<TNode>[] = [];
|
|
144
|
-
let current: Path<TNode> | null = this.parentPath;
|
|
145
|
-
while (current !== null) {
|
|
146
|
-
ancestry.push(current);
|
|
147
|
-
current = current.parentPath;
|
|
148
|
-
}
|
|
149
|
-
return ancestry;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Get all ancestor nodes from immediate parent to root.
|
|
154
|
-
*/
|
|
155
|
-
getAncestorNodes(): TNode[] {
|
|
156
|
-
return this.getAncestry().map((p) => p.node);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Get the semantic path from root as an array of keys.
|
|
161
|
-
* Returns logical document keys (property names, array indices) rather than
|
|
162
|
-
* internal ApiDOM structure keys.
|
|
163
|
-
*
|
|
164
|
-
* @example
|
|
165
|
-
* // For a path to $.paths['/pets'].get in an OpenAPI document:
|
|
166
|
-
* ```
|
|
167
|
-
* path.getPathKeys(); // => ['paths', '/pets', 'get']
|
|
168
|
-
* ```
|
|
169
|
-
*/
|
|
170
|
-
getPathKeys(): PropertyKey[] {
|
|
171
|
-
const keys: PropertyKey[] = [];
|
|
172
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
173
|
-
let current: Path<TNode> | null = this;
|
|
174
|
-
|
|
175
|
-
while (current !== null && current.key !== undefined) {
|
|
176
|
-
const { key, parent, parentPath } = current;
|
|
177
|
-
|
|
178
|
-
if (isMemberElement(parent) && key === 'value') {
|
|
179
|
-
// Inside MemberElement.value → push the member's key
|
|
180
|
-
if (!isStringElement(parent.key)) {
|
|
181
|
-
throw new TypeError('MemberElement.key must be a StringElement');
|
|
182
|
-
}
|
|
183
|
-
keys.unshift(parent.key.toValue() as PropertyKey);
|
|
184
|
-
} else if (isArrayElement(parentPath?.node) && typeof key === 'number') {
|
|
185
|
-
// Inside ArrayElement → push the numeric index
|
|
186
|
-
keys.unshift(key);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
current = current.parentPath;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return keys;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Format path as RFC 6901 JSON Pointer or RFC 9535 Normalized JSONPath.
|
|
197
|
-
*
|
|
198
|
-
* @param pathFormat - Output format: "jsonpointer" (default) or "jsonpath"
|
|
199
|
-
* @returns JSONPointer string like "/paths/~1pets/get/responses/200"
|
|
200
|
-
* or Normalized JSONPath like "$['paths']['/pets']['get']['responses']['200']"
|
|
201
|
-
*
|
|
202
|
-
* @example
|
|
203
|
-
* ```
|
|
204
|
-
* // JSON Pointer examples:
|
|
205
|
-
* path.formatPath(); // "" (root)
|
|
206
|
-
* path.formatPath(); // "/info"
|
|
207
|
-
* path.formatPath(); // "/paths/~1pets/get"
|
|
208
|
-
* path.formatPath(); // "/paths/~1users~1{id}/parameters/0"
|
|
209
|
-
* ```
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* ```
|
|
213
|
-
* // JSONPath examples:
|
|
214
|
-
* path.formatPath('jsonpath'); // "$" (root)
|
|
215
|
-
* path.formatPath('jsonpath'); // "$['info']"
|
|
216
|
-
* path.formatPath('jsonpath'); // "$['paths']['/pets']['get']"
|
|
217
|
-
* path.formatPath('jsonpath'); // "$['paths']['/users/{id}']['parameters'][0]"
|
|
218
|
-
* ```
|
|
219
|
-
*/
|
|
220
|
-
formatPath(pathFormat: 'jsonpointer' | 'jsonpath' = 'jsonpointer'): string {
|
|
221
|
-
const parts = this.getPathKeys();
|
|
222
|
-
|
|
223
|
-
// Root node
|
|
224
|
-
if (parts.length === 0) {
|
|
225
|
-
return pathFormat === 'jsonpath' ? '$' : '';
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (pathFormat === 'jsonpath') {
|
|
229
|
-
// RFC 9535 Normalized JSONPath
|
|
230
|
-
return NormalizedPath.from(parts as (string | number)[]);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// RFC 6901 JSON Pointer
|
|
234
|
-
return compileJSONPointer(parts as string[]);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Find the closest ancestor path that satisfies the predicate.
|
|
239
|
-
*/
|
|
240
|
-
findParent(predicate: (path: Path<TNode>) => boolean): Path<TNode> | null {
|
|
241
|
-
let current: Path<TNode> | null = this.parentPath;
|
|
242
|
-
while (current !== null) {
|
|
243
|
-
if (predicate(current)) {
|
|
244
|
-
return current;
|
|
245
|
-
}
|
|
246
|
-
current = current.parentPath;
|
|
247
|
-
}
|
|
248
|
-
return null;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Find the closest path (including this one) that satisfies the predicate.
|
|
253
|
-
*/
|
|
254
|
-
find(predicate: (path: Path<TNode>) => boolean): Path<TNode> | null {
|
|
255
|
-
if (predicate(this)) {
|
|
256
|
-
return this;
|
|
257
|
-
}
|
|
258
|
-
return this.findParent(predicate);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// ===========================================================================
|
|
262
|
-
// Nested traversal
|
|
263
|
-
// ===========================================================================
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Traverse into the current node with a new visitor.
|
|
267
|
-
* Populated by the traversal module to avoid circular imports.
|
|
268
|
-
*/
|
|
269
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
270
|
-
declare traverse: (visitor: any, options?: any) => TNode;
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Async version of traverse.
|
|
274
|
-
*/
|
|
275
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
276
|
-
declare traverseAsync: (visitor: any, options?: any) => Promise<TNode>;
|
|
277
|
-
|
|
278
|
-
// ===========================================================================
|
|
279
|
-
// Traversal control
|
|
280
|
-
// ===========================================================================
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Skip traversing the children of this node.
|
|
284
|
-
*/
|
|
285
|
-
skip(): void {
|
|
286
|
-
this.#shouldSkip = true;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Stop all traversal completely.
|
|
291
|
-
*/
|
|
292
|
-
stop(): void {
|
|
293
|
-
this.#shouldStop = true;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// ===========================================================================
|
|
297
|
-
// Modification
|
|
298
|
-
// ===========================================================================
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Replace this node with a new node.
|
|
302
|
-
*/
|
|
303
|
-
replaceWith(replacement: TNode): void {
|
|
304
|
-
if (this.#stale) {
|
|
305
|
-
console.warn(
|
|
306
|
-
'Warning: replaceWith() called on a stale Path. ' +
|
|
307
|
-
'This path belongs to a node whose visit has already completed. ' +
|
|
308
|
-
'The replacement will have no effect. ' +
|
|
309
|
-
"To replace a parent node, do so from the parent's own visitor.",
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
this.#replaced = true;
|
|
313
|
-
this.#replacementNode = replacement;
|
|
314
|
-
this.node = replacement;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Remove this node from the tree.
|
|
319
|
-
*/
|
|
320
|
-
remove(): void {
|
|
321
|
-
if (this.#stale) {
|
|
322
|
-
console.warn(
|
|
323
|
-
'Warning: remove() called on a stale Path. ' +
|
|
324
|
-
'This path belongs to a node whose visit has already completed. ' +
|
|
325
|
-
'The removal will have no effect. ' +
|
|
326
|
-
"To remove a parent node, do so from the parent's own visitor.",
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
this.#removed = true;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// ===========================================================================
|
|
333
|
-
// Internal methods for traversal engine
|
|
334
|
-
// ===========================================================================
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* @internal
|
|
338
|
-
*/
|
|
339
|
-
_getReplacementNode(): TNode | undefined {
|
|
340
|
-
return this.#replacementNode;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* @internal
|
|
345
|
-
*/
|
|
346
|
-
_wasReplaced(): boolean {
|
|
347
|
-
return this.#replaced;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* @internal
|
|
352
|
-
*/
|
|
353
|
-
_reset(): void {
|
|
354
|
-
this.#shouldSkip = false;
|
|
355
|
-
this.#shouldStop = false;
|
|
356
|
-
this.#removed = false;
|
|
357
|
-
this.#replaced = false;
|
|
358
|
-
this.#replacementNode = undefined;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Mark this path as stale (visit completed).
|
|
363
|
-
* @internal
|
|
364
|
-
*/
|
|
365
|
-
_markStale(): void {
|
|
366
|
-
this.#stale = true;
|
|
367
|
-
}
|
|
368
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Path } from './Path.ts';
|
|
2
|
-
import { traverse, traverseAsync } from './traversal.ts';
|
|
3
|
-
|
|
4
|
-
// Wire up Path.prototype.traverse methods to avoid circular imports
|
|
5
|
-
Path.prototype.traverse = function (this: Path<any>, visitor: any, options?: any) {
|
|
6
|
-
return traverse(this.node, visitor, options);
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
Path.prototype.traverseAsync = function (this: Path<any>, visitor: any, options?: any) {
|
|
10
|
-
return traverseAsync(this.node, visitor, options);
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export { Path };
|
|
14
|
-
export type { VisitorFn, VisitorResult } from './Path.ts';
|
|
15
|
-
export {
|
|
16
|
-
getNodeType,
|
|
17
|
-
getNodePrimitiveType,
|
|
18
|
-
isNode,
|
|
19
|
-
cloneNode,
|
|
20
|
-
mutateNode,
|
|
21
|
-
getNodeKeys,
|
|
22
|
-
getVisitFn,
|
|
23
|
-
mergeVisitors,
|
|
24
|
-
mergeVisitorsAsync,
|
|
25
|
-
} from './visitors.ts';
|
|
26
|
-
export type {
|
|
27
|
-
NodeVisitor,
|
|
28
|
-
MergeVisitorsOptions,
|
|
29
|
-
MergedVisitor,
|
|
30
|
-
MergedVisitorAsync,
|
|
31
|
-
} from './visitors.ts';
|
|
32
|
-
export { traverse, traverseAsync } from './traversal.ts';
|
|
33
|
-
export type { TraverseOptions } from './traversal.ts';
|
|
34
|
-
|
|
35
|
-
// Operations
|
|
36
|
-
export { default as filter } from './operations/filter.ts';
|
|
37
|
-
export { default as find } from './operations/find.ts';
|
|
38
|
-
export { default as some } from './operations/some.ts';
|
|
39
|
-
export { default as reject } from './operations/reject.ts';
|
|
40
|
-
export { default as forEach } from './operations/for-each.ts';
|
|
41
|
-
export type { Callback as ForEachCallback, ForEachOptions } from './operations/for-each.ts';
|
|
42
|
-
export { default as parents } from './operations/parents.ts';
|
|
43
|
-
export { default as findAtOffset } from './operations/find-at-offset.ts';
|
|
44
|
-
export type { FindAtOffsetOptions } from './operations/find-at-offset.ts';
|
package/src/operations/filter.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Element } from '@speclynx/apidom-datamodel';
|
|
2
|
-
|
|
3
|
-
import { traverse } from '../traversal.ts';
|
|
4
|
-
import type { Path } from '../Path.ts';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Finds all paths whose elements match the predicate.
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
const filter = <T extends Element>(
|
|
11
|
-
element: T,
|
|
12
|
-
predicate: (path: Path<Element>) => boolean,
|
|
13
|
-
): Path<Element>[] => {
|
|
14
|
-
const result: Path<Element>[] = [];
|
|
15
|
-
|
|
16
|
-
traverse(element, {
|
|
17
|
-
enter(path: Path<Element>) {
|
|
18
|
-
if (predicate(path)) {
|
|
19
|
-
result.push(path);
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
return result;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default filter;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Element, hasElementSourceMap } from '@speclynx/apidom-datamodel';
|
|
2
|
-
|
|
3
|
-
import { traverse } from '../traversal.ts';
|
|
4
|
-
import type { Path } from '../Path.ts';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @public
|
|
8
|
-
*/
|
|
9
|
-
export interface FindAtOffsetOptions {
|
|
10
|
-
offset: number;
|
|
11
|
-
includeRightBound?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Finds the path of the most inner node at the given offset.
|
|
16
|
-
* If includeRightBound is set, also finds nodes that end at the given offset.
|
|
17
|
-
* @public
|
|
18
|
-
*/
|
|
19
|
-
const findAtOffset = <T extends Element>(
|
|
20
|
-
element: T,
|
|
21
|
-
options: number | FindAtOffsetOptions,
|
|
22
|
-
): Path<Element> | undefined => {
|
|
23
|
-
let offset: number;
|
|
24
|
-
let includeRightBound: boolean;
|
|
25
|
-
|
|
26
|
-
if (typeof options === 'number') {
|
|
27
|
-
offset = options;
|
|
28
|
-
includeRightBound = false;
|
|
29
|
-
} else {
|
|
30
|
-
offset = options.offset ?? 0;
|
|
31
|
-
includeRightBound = options.includeRightBound ?? false;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const result: Path<Element>[] = [];
|
|
35
|
-
|
|
36
|
-
traverse(element, {
|
|
37
|
-
enter(path: Path<Element>) {
|
|
38
|
-
const node = path.node;
|
|
39
|
-
|
|
40
|
-
if (!hasElementSourceMap(node)) {
|
|
41
|
-
return; // dive in
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const startOffset: number = node.startOffset!;
|
|
45
|
-
const endOffset: number = node.endOffset!;
|
|
46
|
-
const isWithinOffsetRange =
|
|
47
|
-
offset >= startOffset && (offset < endOffset || (includeRightBound && offset <= endOffset));
|
|
48
|
-
|
|
49
|
-
if (isWithinOffsetRange) {
|
|
50
|
-
result.push(path);
|
|
51
|
-
return; // push to stack and dive in
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
path.skip(); // skip entire sub-tree
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return result.at(-1);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export default findAtOffset;
|
package/src/operations/find.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Element } from '@speclynx/apidom-datamodel';
|
|
2
|
-
|
|
3
|
-
import { traverse } from '../traversal.ts';
|
|
4
|
-
import type { Path } from '../Path.ts';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Finds first path whose element satisfies the provided predicate.
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
const find = <T extends Element>(
|
|
11
|
-
element: T,
|
|
12
|
-
predicate: (path: Path<Element>) => boolean,
|
|
13
|
-
): Path<Element> | undefined => {
|
|
14
|
-
let result: Path<Element> | undefined;
|
|
15
|
-
|
|
16
|
-
traverse(element, {
|
|
17
|
-
enter(path: Path<Element>) {
|
|
18
|
-
if (predicate(path)) {
|
|
19
|
-
result = path;
|
|
20
|
-
path.stop();
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
return result;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export default find;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Element } from '@speclynx/apidom-datamodel';
|
|
2
|
-
|
|
3
|
-
import { traverse } from '../traversal.ts';
|
|
4
|
-
import type { Path } from '../Path.ts';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @public
|
|
8
|
-
*/
|
|
9
|
-
export type Callback = (path: Path<Element>) => void;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @public
|
|
13
|
-
*/
|
|
14
|
-
export interface ForEachOptions {
|
|
15
|
-
callback?: Callback;
|
|
16
|
-
predicate?: (path: Path<Element>) => boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Executes the callback on this element's path and all descendant paths.
|
|
21
|
-
* @public
|
|
22
|
-
*/
|
|
23
|
-
const forEach = <T extends Element>(element: T, options: Callback | ForEachOptions): void => {
|
|
24
|
-
let callback: Callback;
|
|
25
|
-
let predicate: (path: Path<Element>) => boolean;
|
|
26
|
-
|
|
27
|
-
if (typeof options === 'function') {
|
|
28
|
-
callback = options;
|
|
29
|
-
predicate = () => true;
|
|
30
|
-
} else {
|
|
31
|
-
callback = options.callback ?? (() => {});
|
|
32
|
-
predicate = options.predicate ?? (() => true);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
traverse(element, {
|
|
36
|
-
enter(path: Path<Element>) {
|
|
37
|
-
if (predicate(path)) {
|
|
38
|
-
callback(path);
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export default forEach;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Element } from '@speclynx/apidom-datamodel';
|
|
2
|
-
|
|
3
|
-
import { traverse } from '../traversal.ts';
|
|
4
|
-
import type { Path } from '../Path.ts';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Computes upwards edges from every child to its parent.
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
const parents = <T extends Element>(element: T): WeakMap<Element, Element | undefined> => {
|
|
11
|
-
const parentEdges = new WeakMap<Element, Element | undefined>();
|
|
12
|
-
|
|
13
|
-
traverse(element, {
|
|
14
|
-
enter(path: Path<Element>) {
|
|
15
|
-
// Use parentPath.node to get the actual Element parent.
|
|
16
|
-
// path.parent could be an array (ArraySlice) when inside ArrayElement/ObjectElement content.
|
|
17
|
-
parentEdges.set(path.node, path.parentPath?.node);
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
return parentEdges;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default parents;
|
package/src/operations/reject.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Element } from '@speclynx/apidom-datamodel';
|
|
2
|
-
|
|
3
|
-
import filter from './filter.ts';
|
|
4
|
-
import type { Path } from '../Path.ts';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Complement of filter. Finds all paths whose elements do NOT match the predicate.
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
const reject = <T extends Element>(
|
|
11
|
-
element: T,
|
|
12
|
-
predicate: (path: Path<Element>) => boolean,
|
|
13
|
-
): Path<Element>[] => {
|
|
14
|
-
return filter(element, (path) => !predicate(path));
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default reject;
|
package/src/operations/some.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Element } from '@speclynx/apidom-datamodel';
|
|
2
|
-
|
|
3
|
-
import find from './find.ts';
|
|
4
|
-
import type { Path } from '../Path.ts';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Tests whether at least one path's element passes the predicate.
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
const some = <T extends Element>(
|
|
11
|
-
element: T,
|
|
12
|
-
predicate: (path: Path<Element>) => boolean,
|
|
13
|
-
): boolean => {
|
|
14
|
-
return find(element, predicate) !== undefined;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default some;
|