| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 | // @ts-check/** @typedef { import('estree').BaseNode} BaseNode *//** @typedef {{	skip: () => void;	remove: () => void;	replace: (node: BaseNode) => void;}} WalkerContext */class WalkerBase {	constructor() {		/** @type {boolean} */		this.should_skip = false;		/** @type {boolean} */		this.should_remove = false;		/** @type {BaseNode | null} */		this.replacement = null;		/** @type {WalkerContext} */		this.context = {			skip: () => (this.should_skip = true),			remove: () => (this.should_remove = true),			replace: (node) => (this.replacement = node)		};	}	/**	 *	 * @param {any} parent	 * @param {string} prop	 * @param {number} index	 * @param {BaseNode} node	 */	replace(parent, prop, index, node) {		if (parent) {			if (index !== null) {				parent[prop][index] = node;			} else {				parent[prop] = node;			}		}	}	/**	 *	 * @param {any} parent	 * @param {string} prop	 * @param {number} index	 */	remove(parent, prop, index) {		if (parent) {			if (index !== null) {				parent[prop].splice(index, 1);			} else {				delete parent[prop];			}		}	}}// @ts-check/** @typedef { import('estree').BaseNode} BaseNode *//** @typedef { import('./walker.js').WalkerContext} WalkerContext *//** @typedef {( *    this: WalkerContext, *    node: BaseNode, *    parent: BaseNode, *    key: string, *    index: number * ) => void} SyncHandler */class SyncWalker extends WalkerBase {	/**	 *	 * @param {SyncHandler} enter	 * @param {SyncHandler} leave	 */	constructor(enter, leave) {		super();		/** @type {SyncHandler} */		this.enter = enter;		/** @type {SyncHandler} */		this.leave = leave;	}	/**	 *	 * @param {BaseNode} node	 * @param {BaseNode} parent	 * @param {string} [prop]	 * @param {number} [index]	 * @returns {BaseNode}	 */	visit(node, parent, prop, index) {		if (node) {			if (this.enter) {				const _should_skip = this.should_skip;				const _should_remove = this.should_remove;				const _replacement = this.replacement;				this.should_skip = false;				this.should_remove = false;				this.replacement = null;				this.enter.call(this.context, node, parent, prop, index);				if (this.replacement) {					node = this.replacement;					this.replace(parent, prop, index, node);				}				if (this.should_remove) {					this.remove(parent, prop, index);				}				const skipped = this.should_skip;				const removed = this.should_remove;				this.should_skip = _should_skip;				this.should_remove = _should_remove;				this.replacement = _replacement;				if (skipped) return node;				if (removed) return null;			}			for (const key in node) {				const value = node[key];				if (typeof value !== "object") {					continue;				} else if (Array.isArray(value)) {					for (let i = 0; i < value.length; i += 1) {						if (value[i] !== null && typeof value[i].type === 'string') {							if (!this.visit(value[i], node, key, i)) {								// removed								i--;							}						}					}				} else if (value !== null && typeof value.type === "string") {					this.visit(value, node, key, null);				}			}			if (this.leave) {				const _replacement = this.replacement;				const _should_remove = this.should_remove;				this.replacement = null;				this.should_remove = false;				this.leave.call(this.context, node, parent, prop, index);				if (this.replacement) {					node = this.replacement;					this.replace(parent, prop, index, node);				}				if (this.should_remove) {					this.remove(parent, prop, index);				}				const removed = this.should_remove;				this.replacement = _replacement;				this.should_remove = _should_remove;				if (removed) return null;			}		}		return node;	}}// @ts-check/** @typedef { import('estree').BaseNode} BaseNode *//** @typedef { import('./walker').WalkerContext} WalkerContext *//** @typedef {( *    this: WalkerContext, *    node: BaseNode, *    parent: BaseNode, *    key: string, *    index: number * ) => Promise<void>} AsyncHandler */class AsyncWalker extends WalkerBase {	/**	 *	 * @param {AsyncHandler} enter	 * @param {AsyncHandler} leave	 */	constructor(enter, leave) {		super();		/** @type {AsyncHandler} */		this.enter = enter;		/** @type {AsyncHandler} */		this.leave = leave;	}	/**	 *	 * @param {BaseNode} node	 * @param {BaseNode} parent	 * @param {string} [prop]	 * @param {number} [index]	 * @returns {Promise<BaseNode>}	 */	async visit(node, parent, prop, index) {		if (node) {			if (this.enter) {				const _should_skip = this.should_skip;				const _should_remove = this.should_remove;				const _replacement = this.replacement;				this.should_skip = false;				this.should_remove = false;				this.replacement = null;				await this.enter.call(this.context, node, parent, prop, index);				if (this.replacement) {					node = this.replacement;					this.replace(parent, prop, index, node);				}				if (this.should_remove) {					this.remove(parent, prop, index);				}				const skipped = this.should_skip;				const removed = this.should_remove;				this.should_skip = _should_skip;				this.should_remove = _should_remove;				this.replacement = _replacement;				if (skipped) return node;				if (removed) return null;			}			for (const key in node) {				const value = node[key];				if (typeof value !== "object") {					continue;				} else if (Array.isArray(value)) {					for (let i = 0; i < value.length; i += 1) {						if (value[i] !== null && typeof value[i].type === 'string') {							if (!(await this.visit(value[i], node, key, i))) {								// removed								i--;							}						}					}				} else if (value !== null && typeof value.type === "string") {					await this.visit(value, node, key, null);				}			}			if (this.leave) {				const _replacement = this.replacement;				const _should_remove = this.should_remove;				this.replacement = null;				this.should_remove = false;				await this.leave.call(this.context, node, parent, prop, index);				if (this.replacement) {					node = this.replacement;					this.replace(parent, prop, index, node);				}				if (this.should_remove) {					this.remove(parent, prop, index);				}				const removed = this.should_remove;				this.replacement = _replacement;				this.should_remove = _should_remove;				if (removed) return null;			}		}		return node;	}}// @ts-check/** @typedef { import('estree').BaseNode} BaseNode *//** @typedef { import('./sync.js').SyncHandler} SyncHandler *//** @typedef { import('./async.js').AsyncHandler} AsyncHandler *//** * * @param {BaseNode} ast * @param {{ *   enter?: SyncHandler *   leave?: SyncHandler * }} walker * @returns {BaseNode} */function walk(ast, { enter, leave }) {	const instance = new SyncWalker(enter, leave);	return instance.visit(ast, null);}/** * * @param {BaseNode} ast * @param {{ *   enter?: AsyncHandler *   leave?: AsyncHandler * }} walker * @returns {Promise<BaseNode>} */async function asyncWalk(ast, { enter, leave }) {	const instance = new AsyncWalker(enter, leave);	return await instance.visit(ast, null);}export { asyncWalk, walk };
 |