UNPKG

unplugin-ast

Version:

Manipulate the AST to transform your code.

117 lines (112 loc) 3.36 kB
import { toArray } from "./dist-BUfbFn_l.js"; import { babelParse, getLang, walkASTAsync } from "ast-kit"; import { createUnplugin } from "unplugin"; import { MagicStringAST, generateTransform } from "magic-string-ast"; //#region src/core/options.ts function resolveOptions(options) { return { include: options.include || [/\.[jt]sx?$/], exclude: options.exclude || void 0, enforce: options.enforce || void 0, parserOptions: options.parserOptions || {}, transformer: options.transformer ? toArray(options.transformer) : [] }; } //#endregion //#region src/core/utils.ts function useNodeRef() { const nodeRefs = /* @__PURE__ */ new Map(); function getNodeRef(node) { if (nodeRefs.has(node)) return nodeRefs.get(node); const ref = { value: node, set(node$1) { this.value = node$1; } }; nodeRefs.set(node, ref); return ref; } return { nodeRefs, getNodeRef }; } //#endregion //#region src/core/transform.ts async function getTransformersByFile(transformer, id) { const transformers = (await Promise.all(transformer.map(async (t) => { if (t.transformInclude && !await t.transformInclude(id)) return void 0; return { transformer: t, nodes: [] }; }))).filter((t) => !!t); return transformers; } async function transform(code, id, options) { const { getNodeRef } = useNodeRef(); const transformers = await getTransformersByFile(options.transformer, id); if (transformers.length === 0) return; const program = babelParse(code, getLang(id), options.parserOptions); await walkASTAsync(program, { async enter(node, parent, key, index) { for (const { transformer, nodes } of transformers) { if (transformer.onNode) { const bool = await transformer.onNode?.(node, parent, index); if (!bool) continue; } nodes.push(getNodeRef(node)); } } }); const s = new MagicStringAST(code); for (const { transformer, nodes } of transformers) for (const node of nodes) { const value = node.value; if (!value) continue; const result = await transformer.transform(value, code, { id }); if (result) { let newAST; if (typeof result === "string") { s.overwriteNode(value, result); newAST = babelParse(`{${result}}`, getLang(id), options.parserOptions).body[0].body[0]; if (newAST.type === "ExpressionStatement") newAST = newAST.expression; newAST.start = value.start; newAST.end = value.end; } else { let { default: generate } = await import("@babel/generator"); generate = generate.default || generate; const generated = generate(result); let code$1 = generated.code; if (result.type.endsWith("Expression")) code$1 = `(${code$1})`; s.overwriteNode(value, code$1); newAST = result; } node.set(newAST); } else if (result === false) { node.set(void 0); s.removeNode(value); } } for (const { transformer } of transformers) await transformer.finalize?.(s); return generateTransform(s, id); } //#endregion //#region src/index.ts const AST = createUnplugin((userOptions = {}) => { const { include, exclude, enforce,...options } = resolveOptions(userOptions); const name = "unplugin-ast"; return { name, enforce, transform: { filter: { id: { include, exclude } }, handler(code, id) { return transform(code, id, options); } } }; }); //#endregion export { AST, resolveOptions, transform };