UNPKG

@vue.ts/tsx-auto-props

Version:

Automatically add props definition for Vue 3 TSX.

98 lines (94 loc) 3.35 kB
import { createUnplugin } from "unplugin"; import { ensureLanguage, getLanguage } from "@vue.ts/language"; import { createOptionsResolver, normalizePath } from "@vue.ts/shared"; import MagicString from "magic-string"; import ts from "typescript"; //#region src/core/utils.ts const defaultOptions = { root: process.cwd(), include: ["**/*.vue", "**/*.tsx"], exclude: ["node_modules/**"], tsconfigPath: "tsconfig.json" }; const resolveOptions = createOptionsResolver(defaultOptions); function getNodeAssignNode(node) { let parent = null; let variableList = null; let variable = null; while (node) { if (parent) { if (ts.isVariableDeclarationList(parent)) variableList = parent; if (ts.isVariableDeclaration(parent)) variable = parent; } parent = node; node = node.parent; } return { variableList, variable }; } //#endregion //#region src/core/transform.ts const generateDefineProps = (name, props) => `\n;Object.defineProperty(${name}, "props", { value: ${JSON.stringify(props)}, });`; const DEFINE_COMPONENT = "defineComponent"; function transform(code, id) { const s = new MagicString(code); const normalizedFilepath = normalizePath(id); const language = getLanguage(); const checker = language.tsLs.getProgram().getTypeChecker(); const ast = language.getVirtualFileOrTsAst(normalizedFilepath); if (!ast) return; language.traverseAst(ast, (node) => { if (!ts.isCallExpression(node)) return; const identifier = node.expression; if (checker.getSymbolAtLocation(identifier)?.declarations?.some((d) => ts.isImportSpecifier(d) && (d.propertyName ?? d.name)?.text === DEFINE_COMPONENT)) { const arg = node.arguments[0]; let setupFn; if (ts.isObjectLiteralExpression(arg)) { const props$1 = arg.properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.escapedText === "props"); const setup = arg.properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.escapedText === "setup"); if (!props$1 && setup && ts.isPropertyAssignment(setup)) { const value = setup.initializer; if (ts.isFunctionLike(value)) setupFn = value; } } else if (ts.isFunctionLike(arg)) setupFn = arg; else throw new Error("[@vue.ts/tsx-auto-props] Invalid defineComponent argument"); const props = setupFn?.parameters[0]; if (props) { const propsList = checker.getTypeAtLocation(props).getProperties().map((p) => p.name); const { variableList, variable } = getNodeAssignNode(node); if (propsList.length > 0 && variableList && variable) s.appendRight(variableList.getEnd() + 1, generateDefineProps(variable.name.getText(ast), propsList)); } } }); return { code: s.toString(), map: s.generateMap({ hires: true }) }; } //#endregion //#region src/index.ts const unpluginFactory = (options = {}) => { const resolvedOptions = resolveOptions(options); return { name: "@vue.ts/tsx-auto-props", enforce: "pre", buildStart() { ensureLanguage(resolveOptions(options).tsconfigPath); }, transform: { filter: { id: { include: resolvedOptions.include, exclude: resolvedOptions.exclude } }, handler: transform } }; }; const unplugin = /* @__PURE__ */ createUnplugin(unpluginFactory); var src_default = unplugin; //#endregion export { unplugin as n, unpluginFactory as r, src_default as t };