unplugin-ast
Version:
Manipulate the AST to transform your code.
117 lines (112 loc) • 3.36 kB
JavaScript
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 };