mirror of
https://github.com/Sevichecc/Urara-Blog.git
synced 2025-05-01 21:49:30 +08:00
144 lines
2.8 KiB
Text
144 lines
2.8 KiB
Text
import { BaseNode } from "estree";
|
|
|
|
type WalkerContext = {
|
|
skip: () => void;
|
|
remove: () => void;
|
|
replace: (node: BaseNode) => void;
|
|
};
|
|
|
|
type WalkerHandler = (
|
|
this: WalkerContext,
|
|
node: BaseNode,
|
|
parent: BaseNode,
|
|
key: string,
|
|
index: number
|
|
) => void
|
|
|
|
type Walker = {
|
|
enter?: WalkerHandler;
|
|
leave?: WalkerHandler;
|
|
}
|
|
|
|
export function walk(ast: BaseNode, { enter, leave }: Walker) {
|
|
return visit(ast, null, enter, leave);
|
|
}
|
|
|
|
let should_skip = false;
|
|
let should_remove = false;
|
|
let replacement: BaseNode = null;
|
|
const context: WalkerContext = {
|
|
skip: () => should_skip = true,
|
|
remove: () => should_remove = true,
|
|
replace: (node: BaseNode) => replacement = node
|
|
};
|
|
|
|
function replace(parent: any, prop: string, index: number, node: BaseNode) {
|
|
if (parent) {
|
|
if (index !== null) {
|
|
parent[prop][index] = node;
|
|
} else {
|
|
parent[prop] = node;
|
|
}
|
|
}
|
|
}
|
|
|
|
function remove(parent: any, prop: string, index: number) {
|
|
if (parent) {
|
|
if (index !== null) {
|
|
parent[prop].splice(index, 1);
|
|
} else {
|
|
delete parent[prop];
|
|
}
|
|
}
|
|
}
|
|
|
|
function visit(
|
|
node: BaseNode,
|
|
parent: BaseNode,
|
|
enter: WalkerHandler,
|
|
leave: WalkerHandler,
|
|
prop?: string,
|
|
index?: number
|
|
) {
|
|
if (node) {
|
|
if (enter) {
|
|
const _should_skip = should_skip;
|
|
const _should_remove = should_remove;
|
|
const _replacement = replacement;
|
|
should_skip = false;
|
|
should_remove = false;
|
|
replacement = null;
|
|
|
|
enter.call(context, node, parent, prop, index);
|
|
|
|
if (replacement) {
|
|
node = replacement;
|
|
replace(parent, prop, index, node);
|
|
}
|
|
|
|
if (should_remove) {
|
|
remove(parent, prop, index);
|
|
}
|
|
|
|
const skipped = should_skip;
|
|
const removed = should_remove;
|
|
|
|
should_skip = _should_skip;
|
|
should_remove = _should_remove;
|
|
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 j = 0, k = 0; j < value.length; j += 1, k += 1) {
|
|
if (value[j] !== null && typeof value[j].type === 'string') {
|
|
if (!visit(value[j], node, enter, leave, key, k)) {
|
|
// removed
|
|
j--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (value !== null && typeof value.type === 'string') {
|
|
visit(value, node, enter, leave, key, null);
|
|
}
|
|
}
|
|
|
|
if (leave) {
|
|
const _replacement = replacement;
|
|
const _should_remove = should_remove;
|
|
replacement = null;
|
|
should_remove = false;
|
|
|
|
leave.call(context, node, parent, prop, index);
|
|
|
|
if (replacement) {
|
|
node = replacement;
|
|
replace(parent, prop, index, node);
|
|
}
|
|
|
|
if (should_remove) {
|
|
remove(parent, prop, index);
|
|
}
|
|
|
|
const removed = should_remove;
|
|
|
|
replacement = _replacement;
|
|
should_remove = _should_remove;
|
|
|
|
if (removed) return null;
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|