UNPKG

unplugin-vue-define-options

Version:

Add defineOptions macro for Vue <script setup>.

66 lines (63 loc) 2.84 kB
import { DEFINE_OPTIONS, MagicStringAST, addNormalScript, checkInvalidScopeReference, generateTransform, importHelperFn, isCallOf, parseSFC } from "@vue-macros/common"; import { walkAST } from "ast-walker-scope"; //#region src/core/utils.ts function filterMacro(stmts) { return stmts.map((raw) => { let node = raw; if (raw.type === "ExpressionStatement") node = raw.expression; return isCallOf(node, DEFINE_OPTIONS) ? node : void 0; }).filter((node) => !!node); } function hasPropsOrEmits(node) { return node.properties.some((prop) => (prop.type === "ObjectProperty" || prop.type === "ObjectMethod") && prop.key.type === "Identifier" && (prop.key.name === "props" || prop.key.name === "emits" || prop.key.name === "expose" || prop.key.name === "slots")); } //#endregion //#region src/core/index.ts function transformDefineOptions(code, id) { if (!code.includes(DEFINE_OPTIONS)) return; const sfc = parseSFC(code, id); if (!sfc.scriptSetup) return; const { scriptSetup, getSetupAst, getScriptAst } = sfc; const setupOffset = scriptSetup.loc.start.offset; const setupAst = getSetupAst(); const nodes = filterMacro(setupAst.body); if (nodes.length === 0) return; else if (nodes.length > 1) throw new SyntaxError(`duplicate ${DEFINE_OPTIONS}() call`); const scriptAst = getScriptAst(); if (scriptAst) checkDefaultExport(scriptAst.body); const setupBindings = getRootScopeIds(setupAst); const s = new MagicStringAST(code); const [node] = nodes; const [arg] = node.arguments; if (arg) { const normalScript = addNormalScript(sfc, s); const scriptOffset = normalScript.start(); s.appendLeft(scriptOffset, `\nexport default /*#__PURE__*/ ${importHelperFn(s, scriptOffset, "defineComponent")}(`); if (arg.type === "ObjectExpression" && hasPropsOrEmits(arg)) throw new SyntaxError(`${DEFINE_OPTIONS}() please use defineProps, defineEmits, defineExpose, or defineSlots instead.`); checkInvalidScopeReference(arg, DEFINE_OPTIONS, setupBindings); s.moveNode(arg, scriptOffset, { offset: setupOffset }); s.remove(setupOffset + node.start, setupOffset + arg.start); s.remove(setupOffset + arg.end, setupOffset + node.end); s.appendRight(scriptOffset, ");"); normalScript.end(); } else s.removeNode(node, { offset: setupOffset }); return generateTransform(s, id); } function checkDefaultExport(stmts) { if (stmts.some((node) => node.type === "ExportDefaultDeclaration")) throw new SyntaxError(`${DEFINE_OPTIONS} cannot be used with default export within <script>.`); } function getRootScopeIds(program) { let ids = []; walkAST(program, { enter(node) { if (node.type === "BlockStatement") this.skip(); }, leave(node) { if (node.type !== "Program") return; ids = Object.keys(this.scope); } }); return ids; } //#endregion export { filterMacro, hasPropsOrEmits, transformDefineOptions };