blob: bddf34416124af5d4160d7cb79f4f7e3a355e28c [file] [log] [blame]
import { WalkerBase, WalkerContext } from "./walker";
import { BaseNode } from "estree";
export type AsyncWalker = {
enter?: AsyncWalkerHandler;
leave?: AsyncWalkerHandler;
};
export type AsyncWalkerHandler = (
this: WalkerContext,
node: BaseNode,
parent: BaseNode,
key: string,
index: number
) => Promise<void>;
export class AsyncWalkerClass extends WalkerBase {
protected enter: AsyncWalker["enter"];
protected leave: AsyncWalker["leave"];
constructor(walker: AsyncWalker) {
super();
this.enter = walker.enter;
this.leave = walker.leave;
}
public async visit(
node: BaseNode,
parent: BaseNode,
enter: AsyncWalkerHandler,
leave: AsyncWalkerHandler,
prop?: string,
index?: number
): Promise<BaseNode> {
if (node) {
if (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 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 as any)[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, enter, leave, key, i))) {
// removed
i--;
}
}
}
} else if (value !== null && typeof value.type === "string") {
await this.visit(value, node, enter, leave, key, null);
}
}
if (leave) {
const _replacement = this.replacement;
const _should_remove = this.should_remove;
this.replacement = null;
this.should_remove = false;
await 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;
}
}