repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
scripts/lib/esast.ts
Rawimport { parseSync } from "oxc-parser";
// Shared ESTree helpers for the repository's structural checks. The native
// TypeScript (Go) toolchain ships no programmatic compiler API, so scripts parse
// with oxc-parser and walk the ESTree result directly.
/** Minimal ESTree node shape used by the structural checks. */
export type EsNode = { type: string; [key: string]: unknown };
/** Parse a source file into its ESTree program; language is inferred from the path. */
export function parseModule(file: string, text: string): EsNode {
return parseSync(file, text).program as unknown as EsNode;
}
/**
* The string value of a string `Literal` or a no-substitution template literal,
* else undefined. Minified output rewrites `import("x")` to `` import(`x`) ``, so
* import-specifier checks must accept both.
*/
export function literalString(
node: EsNode | null | undefined,
): string | undefined {
if (!node) return undefined;
if (node.type === "Literal" && typeof node["value"] === "string")
return node["value"];
if (
node.type === "TemplateLiteral" &&
Array.isArray(node["expressions"]) &&
node["expressions"].length === 0
) {
const quasi = (node["quasis"] as EsNode[] | undefined)?.[0];
const cooked = (quasi?.["value"] as { cooked?: unknown } | undefined)
?.cooked;
return typeof cooked === "string" ? cooked : undefined;
}
return undefined;
}
/** Depth-first visit of every typed node in an ESTree tree. */
export function walkEs(node: unknown, visit: (node: EsNode) => void): void {
if (!node || typeof node !== "object") return;
if (Array.isArray(node)) {
for (const child of node) walkEs(child, visit);
return;
}
const record = node as Record<string, unknown>;
if (typeof record["type"] === "string") visit(record as EsNode);
for (const key of Object.keys(record))
if (key !== "type") walkEs(record[key], visit);
}