source: node_modules/estree-walker/dist/esm/estree-walker.js@ 3d60932

Last change on this file since 3d60932 was 57e58a3, checked in by ste08 <sjovanoska@…>, 4 months ago

Initial commit

  • Property mode set to 100644
File size: 7.0 KB
Line 
1// @ts-check
2/** @typedef { import('estree').BaseNode} BaseNode */
3
4/** @typedef {{
5 skip: () => void;
6 remove: () => void;
7 replace: (node: BaseNode) => void;
8}} WalkerContext */
9
10class WalkerBase {
11 constructor() {
12 /** @type {boolean} */
13 this.should_skip = false;
14
15 /** @type {boolean} */
16 this.should_remove = false;
17
18 /** @type {BaseNode | null} */
19 this.replacement = null;
20
21 /** @type {WalkerContext} */
22 this.context = {
23 skip: () => (this.should_skip = true),
24 remove: () => (this.should_remove = true),
25 replace: (node) => (this.replacement = node)
26 };
27 }
28
29 /**
30 *
31 * @param {any} parent
32 * @param {string} prop
33 * @param {number} index
34 * @param {BaseNode} node
35 */
36 replace(parent, prop, index, node) {
37 if (parent) {
38 if (index !== null) {
39 parent[prop][index] = node;
40 } else {
41 parent[prop] = node;
42 }
43 }
44 }
45
46 /**
47 *
48 * @param {any} parent
49 * @param {string} prop
50 * @param {number} index
51 */
52 remove(parent, prop, index) {
53 if (parent) {
54 if (index !== null) {
55 parent[prop].splice(index, 1);
56 } else {
57 delete parent[prop];
58 }
59 }
60 }
61}
62
63// @ts-check
64
65/** @typedef { import('estree').BaseNode} BaseNode */
66/** @typedef { import('./walker.js').WalkerContext} WalkerContext */
67
68/** @typedef {(
69 * this: WalkerContext,
70 * node: BaseNode,
71 * parent: BaseNode,
72 * key: string,
73 * index: number
74 * ) => void} SyncHandler */
75
76class SyncWalker extends WalkerBase {
77 /**
78 *
79 * @param {SyncHandler} enter
80 * @param {SyncHandler} leave
81 */
82 constructor(enter, leave) {
83 super();
84
85 /** @type {SyncHandler} */
86 this.enter = enter;
87
88 /** @type {SyncHandler} */
89 this.leave = leave;
90 }
91
92 /**
93 *
94 * @param {BaseNode} node
95 * @param {BaseNode} parent
96 * @param {string} [prop]
97 * @param {number} [index]
98 * @returns {BaseNode}
99 */
100 visit(node, parent, prop, index) {
101 if (node) {
102 if (this.enter) {
103 const _should_skip = this.should_skip;
104 const _should_remove = this.should_remove;
105 const _replacement = this.replacement;
106 this.should_skip = false;
107 this.should_remove = false;
108 this.replacement = null;
109
110 this.enter.call(this.context, node, parent, prop, index);
111
112 if (this.replacement) {
113 node = this.replacement;
114 this.replace(parent, prop, index, node);
115 }
116
117 if (this.should_remove) {
118 this.remove(parent, prop, index);
119 }
120
121 const skipped = this.should_skip;
122 const removed = this.should_remove;
123
124 this.should_skip = _should_skip;
125 this.should_remove = _should_remove;
126 this.replacement = _replacement;
127
128 if (skipped) return node;
129 if (removed) return null;
130 }
131
132 for (const key in node) {
133 const value = node[key];
134
135 if (typeof value !== "object") {
136 continue;
137 } else if (Array.isArray(value)) {
138 for (let i = 0; i < value.length; i += 1) {
139 if (value[i] !== null && typeof value[i].type === 'string') {
140 if (!this.visit(value[i], node, key, i)) {
141 // removed
142 i--;
143 }
144 }
145 }
146 } else if (value !== null && typeof value.type === "string") {
147 this.visit(value, node, key, null);
148 }
149 }
150
151 if (this.leave) {
152 const _replacement = this.replacement;
153 const _should_remove = this.should_remove;
154 this.replacement = null;
155 this.should_remove = false;
156
157 this.leave.call(this.context, node, parent, prop, index);
158
159 if (this.replacement) {
160 node = this.replacement;
161 this.replace(parent, prop, index, node);
162 }
163
164 if (this.should_remove) {
165 this.remove(parent, prop, index);
166 }
167
168 const removed = this.should_remove;
169
170 this.replacement = _replacement;
171 this.should_remove = _should_remove;
172
173 if (removed) return null;
174 }
175 }
176
177 return node;
178 }
179}
180
181// @ts-check
182
183/** @typedef { import('estree').BaseNode} BaseNode */
184/** @typedef { import('./walker').WalkerContext} WalkerContext */
185
186/** @typedef {(
187 * this: WalkerContext,
188 * node: BaseNode,
189 * parent: BaseNode,
190 * key: string,
191 * index: number
192 * ) => Promise<void>} AsyncHandler */
193
194class AsyncWalker extends WalkerBase {
195 /**
196 *
197 * @param {AsyncHandler} enter
198 * @param {AsyncHandler} leave
199 */
200 constructor(enter, leave) {
201 super();
202
203 /** @type {AsyncHandler} */
204 this.enter = enter;
205
206 /** @type {AsyncHandler} */
207 this.leave = leave;
208 }
209
210 /**
211 *
212 * @param {BaseNode} node
213 * @param {BaseNode} parent
214 * @param {string} [prop]
215 * @param {number} [index]
216 * @returns {Promise<BaseNode>}
217 */
218 async visit(node, parent, prop, index) {
219 if (node) {
220 if (this.enter) {
221 const _should_skip = this.should_skip;
222 const _should_remove = this.should_remove;
223 const _replacement = this.replacement;
224 this.should_skip = false;
225 this.should_remove = false;
226 this.replacement = null;
227
228 await this.enter.call(this.context, node, parent, prop, index);
229
230 if (this.replacement) {
231 node = this.replacement;
232 this.replace(parent, prop, index, node);
233 }
234
235 if (this.should_remove) {
236 this.remove(parent, prop, index);
237 }
238
239 const skipped = this.should_skip;
240 const removed = this.should_remove;
241
242 this.should_skip = _should_skip;
243 this.should_remove = _should_remove;
244 this.replacement = _replacement;
245
246 if (skipped) return node;
247 if (removed) return null;
248 }
249
250 for (const key in node) {
251 const value = node[key];
252
253 if (typeof value !== "object") {
254 continue;
255 } else if (Array.isArray(value)) {
256 for (let i = 0; i < value.length; i += 1) {
257 if (value[i] !== null && typeof value[i].type === 'string') {
258 if (!(await this.visit(value[i], node, key, i))) {
259 // removed
260 i--;
261 }
262 }
263 }
264 } else if (value !== null && typeof value.type === "string") {
265 await this.visit(value, node, key, null);
266 }
267 }
268
269 if (this.leave) {
270 const _replacement = this.replacement;
271 const _should_remove = this.should_remove;
272 this.replacement = null;
273 this.should_remove = false;
274
275 await this.leave.call(this.context, node, parent, prop, index);
276
277 if (this.replacement) {
278 node = this.replacement;
279 this.replace(parent, prop, index, node);
280 }
281
282 if (this.should_remove) {
283 this.remove(parent, prop, index);
284 }
285
286 const removed = this.should_remove;
287
288 this.replacement = _replacement;
289 this.should_remove = _should_remove;
290
291 if (removed) return null;
292 }
293 }
294
295 return node;
296 }
297}
298
299// @ts-check
300
301/** @typedef { import('estree').BaseNode} BaseNode */
302/** @typedef { import('./sync.js').SyncHandler} SyncHandler */
303/** @typedef { import('./async.js').AsyncHandler} AsyncHandler */
304
305/**
306 *
307 * @param {BaseNode} ast
308 * @param {{
309 * enter?: SyncHandler
310 * leave?: SyncHandler
311 * }} walker
312 * @returns {BaseNode}
313 */
314function walk(ast, { enter, leave }) {
315 const instance = new SyncWalker(enter, leave);
316 return instance.visit(ast, null);
317}
318
319/**
320 *
321 * @param {BaseNode} ast
322 * @param {{
323 * enter?: AsyncHandler
324 * leave?: AsyncHandler
325 * }} walker
326 * @returns {Promise<BaseNode>}
327 */
328async function asyncWalk(ast, { enter, leave }) {
329 const instance = new AsyncWalker(enter, leave);
330 return await instance.visit(ast, null);
331}
332
333export { asyncWalk, walk };
Note: See TracBrowser for help on using the repository browser.