@speclynx/apidom-traverse 3.2.0 → 4.0.0

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