Skip to content

Commit 7dd0bbd

Browse files
authored
feat(ast): introduce async version of visitor merging mechanism (#3993)
Refs #3832
1 parent 3050713 commit 7dd0bbd

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

packages/apidom-ast/src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,10 @@ export { default as ParseResult } from './ParseResult';
7171
export { isParseResult, isLiteral, isPoint, isPosition } from './predicates';
7272
// AST traversal related exports
7373
export {
74-
getVisitFn,
74+
customPromisifySymbol,
7575
BREAK,
7676
mergeAll as mergeAllVisitors,
77+
getVisitFn,
7778
visit,
7879
getNodeType,
7980
isNode,

packages/apidom-ast/src/traversal/visitor.ts

+98-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { ApiDOMStructuredError } from '@swagger-api/apidom-error';
66
* SPDX-License-Identifier: MIT
77
*/
88

9+
export const customPromisifySymbol: unique symbol = Symbol.for('nodejs.util.promisify.custom');
10+
911
// getVisitFn :: (Visitor, String, Boolean) -> Function
1012
export const getVisitFn = (visitor: any, type: string, isLeaving: boolean) => {
1113
const typeVisitor = visitor[type];
@@ -57,7 +59,31 @@ export const cloneNode = (node: any) =>
5759
* If a prior visitor edits a node, no following visitors will see that node.
5860
* `exposeEdits=true` can be used to exoise the edited node from the previous visitors.
5961
*/
60-
export const mergeAll = (
62+
63+
interface MergeAllBase {
64+
(
65+
visitors: any[],
66+
options?: {
67+
visitFnGetter?: typeof getVisitFn;
68+
nodeTypeGetter?: typeof getNodeType;
69+
breakSymbol?: typeof BREAK;
70+
deleteNodeSymbol?: any;
71+
skipVisitingNodeSymbol?: boolean;
72+
exposeEdits?: boolean;
73+
},
74+
): {
75+
enter: (node: any, ...rest: any[]) => any;
76+
leave: (node: any, ...rest: any[]) => any;
77+
};
78+
}
79+
80+
interface MergeAllPromisify {
81+
[customPromisifySymbol]: MergeAllBase;
82+
}
83+
84+
type MergeAll = MergeAllBase & MergeAllPromisify;
85+
86+
export const mergeAll: MergeAll = ((
6187
visitors: any[],
6288
{
6389
visitFnGetter = getVisitFn,
@@ -121,6 +147,77 @@ export const mergeAll = (
121147
}
122148
}
123149

150+
return undefined;
151+
},
152+
};
153+
}) as MergeAll;
154+
155+
mergeAll[customPromisifySymbol] = (
156+
visitors: any[],
157+
{
158+
visitFnGetter = getVisitFn,
159+
nodeTypeGetter = getNodeType,
160+
breakSymbol = BREAK,
161+
deleteNodeSymbol = null,
162+
skipVisitingNodeSymbol = false,
163+
exposeEdits = false,
164+
} = {},
165+
) => {
166+
const skipSymbol = Symbol('skip');
167+
const skipping = new Array(visitors.length).fill(skipSymbol);
168+
169+
return {
170+
async enter(node: any, ...rest: any[]) {
171+
let currentNode = node;
172+
let hasChanged = false;
173+
174+
for (let i = 0; i < visitors.length; i += 1) {
175+
if (skipping[i] === skipSymbol) {
176+
const visitFn = visitFnGetter(visitors[i], nodeTypeGetter(currentNode), false);
177+
178+
if (typeof visitFn === 'function') {
179+
// eslint-disable-next-line no-await-in-loop
180+
const result: any = await visitFn.call(visitors[i], currentNode, ...rest);
181+
182+
if (result === skipVisitingNodeSymbol) {
183+
skipping[i] = node;
184+
} else if (result === breakSymbol) {
185+
skipping[i] = breakSymbol;
186+
} else if (result === deleteNodeSymbol) {
187+
return result;
188+
} else if (result !== undefined) {
189+
if (exposeEdits) {
190+
currentNode = result;
191+
hasChanged = true;
192+
} else {
193+
return result;
194+
}
195+
}
196+
}
197+
}
198+
}
199+
200+
return hasChanged ? currentNode : undefined;
201+
},
202+
async leave(node: any, ...rest: any[]) {
203+
for (let i = 0; i < visitors.length; i += 1) {
204+
if (skipping[i] === skipSymbol) {
205+
const visitFn = visitFnGetter(visitors[i], nodeTypeGetter(node), true);
206+
207+
if (typeof visitFn === 'function') {
208+
// eslint-disable-next-line no-await-in-loop
209+
const result = await visitFn.call(visitors[i], node, ...rest);
210+
if (result === breakSymbol) {
211+
skipping[i] = breakSymbol;
212+
} else if (result !== undefined && result !== skipVisitingNodeSymbol) {
213+
return result;
214+
}
215+
}
216+
} else if (skipping[i] === node) {
217+
skipping[i] = skipSymbol;
218+
}
219+
}
220+
124221
return undefined;
125222
},
126223
};

0 commit comments

Comments
 (0)