@vue-macros/volar
Version:
Volar plugin for Vue Macros.
157 lines (155 loc) • 6.26 kB
JavaScript
import { addProps } from "./common-BTTMZY21.js";
import { getText } from "ts-macro";
import { DEFINE_PROP, DEFINE_PROP_DOLLAR, createFilter } from "@vue-macros/common";
//#region src/define-prop.ts
function transformDefineProp({ codes, defineProps, version, edition }) {
addProps(codes, defineProps.map((defineProp) => {
let result = defineProp.name ?? "modelValue";
if (!defineProp.required) result += "?";
result += ": ";
let type = "any";
if (defineProp.type) type = defineProp.type;
else if (defineProp.defaultValue && defineProp.prop) type = `NonNullable<typeof ${defineProp.prop}${defineProp.isReactivityTransform ? "" : `['value']`}>`;
result += type;
return result;
}), version);
if (edition === "kevinEdition") codes.push(`
type __VLS_PropOptions<T> = Exclude<import('vue').Prop<T>, import('vue').PropType<T>>;
declare function defineProp<T>(
name: string,
options:
| ({ default: T } & __VLS_PropOptions<T>)
| ({ required: true } & __VLS_PropOptions<T>)
): import('vue').ComputedRef<T>;
declare function defineProp<T>(
name?: string,
options?: __VLS_PropOptions<T>
): import('vue').ComputedRef<T | undefined>;
declare function $defineProp<T>(
name: string,
options:
| ({ default: T } & __VLS_PropOptions<T>)
| ({ required: true } & __VLS_PropOptions<T>)
): T;
declare function $defineProp<T>(
name?: string,
options?: __VLS_PropOptions<T>
): T | undefined;
`);
else if (edition === "johnsonEdition") codes.push(`
type __VLS_Widen<T> = T extends number | string | boolean | symbol
? ReturnType<T['valueOf']>
: T;
type __VLS_PropOptions<T> = Omit<
Omit<
Exclude<import('vue').Prop<T>, import('vue').PropType<T>>,
'default'
>,
'required'
>;
declare function defineProp<T>(
value: T | (() => T) | undefined,
required: true,
options?: __VLS_PropOptions<T>
): import('vue').ComputedRef<T>;
declare function defineProp<T>(
value: T | (() => T),
required?: boolean,
options?: __VLS_PropOptions<T>
): import('vue').ComputedRef<T>;
declare function defineProp<T>(
value?: T | (() => T),
required?: boolean,
options?: __VLS_PropOptions<T>
): | import('vue').ComputedRef<T | undefined>;
declare function $defineProp<T>(
value: T | (() => T) | undefined,
required: true,
options?: __VLS_PropOptions<T>
): __VLS_Widen<T>;
declare function $defineProp<T>(
value: T | (() => T),
required?: boolean,
options?: __VLS_PropOptions<T>
): __VLS_Widen<T>;
declare function $defineProp<T>(
value?: T | (() => T),
required?: boolean,
options?: __VLS_PropOptions<T>
): | __VLS_Widen<T>
| undefined;
`);
}
function getDefineProp(ts, sfc, edition) {
const defineProps = [];
function visitNode(node, parent, isReactivityTransform = false) {
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && [DEFINE_PROP, DEFINE_PROP_DOLLAR].includes(node.expression.escapedText)) {
if (edition === "kevinEdition") {
const type = node.typeArguments?.length ? getText(node.typeArguments[0], ast, ts) : void 0;
const name = node.arguments[0] && ts.isStringLiteral(node.arguments[0]) ? node.arguments[0].text : ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name) ? getText(parent.name, ast, ts) : void 0;
const prop = ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name) ? getText(parent.name, ast, ts) : void 0;
const optionArg = node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) ? node.arguments[0] : node.arguments[1] && ts.isObjectLiteralExpression(node.arguments[1]) ? node.arguments[1] : void 0;
let required = false;
let defaultValue;
if (optionArg) {
for (const property of optionArg.properties) if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name)) {
if (getText(property.name, ast, ts) === "required" && property.initializer.kind === ts.SyntaxKind.TrueKeyword) required = true;
if (ts.isIdentifier(property.name) && getText(property.name, ast, ts) === "default") defaultValue = getText(property.initializer, ast, ts);
}
}
defineProps.push({
name,
prop,
type,
defaultValue,
required,
isReactivityTransform: isReactivityTransform || node.expression.escapedText === DEFINE_PROP_DOLLAR
});
} else if (edition === "johnsonEdition" && ts.isVariableDeclaration(parent)) {
const name = ts.isIdentifier(parent.name) ? getText(parent.name, ast, ts) : void 0;
defineProps.push({
name,
prop: name,
defaultValue: node.arguments.length > 0 ? getText(node.arguments[0], ast, ts) : void 0,
type: node.typeArguments?.length ? getText(node.typeArguments[0], ast, ts) : void 0,
required: node.arguments.length >= 2 && node.arguments[1].kind === ts.SyntaxKind.TrueKeyword,
isReactivityTransform: isReactivityTransform || node.expression.escapedText === DEFINE_PROP_DOLLAR
});
}
}
}
const ast = sfc.scriptSetup.ast;
ts.forEachChild(ast, (node) => {
if (ts.isExpressionStatement(node)) visitNode(node.expression, ast);
else if (ts.isVariableStatement(node)) ts.forEachChild(node.declarationList, (decl) => {
if (!ts.isVariableDeclaration(decl) || !decl.initializer) return;
if (ts.isCallExpression(decl.initializer) && ts.isIdentifier(decl.initializer.expression) && decl.initializer.expression.escapedText === "$" && decl.initializer.arguments.length > 0) visitNode(decl.initializer.arguments[0], decl, true);
else visitNode(decl.initializer, decl);
});
});
return defineProps;
}
const plugin = (ctx, options = {}) => {
if (!options) return [];
const filter = createFilter(options);
const { modules: { typescript: ts }, vueCompilerOptions: { target } } = ctx;
return {
name: "vue-macros-define-prop",
version: 2.1,
resolveEmbeddedCode(fileName, sfc, embeddedFile) {
if (!filter(fileName) || !["ts", "tsx"].includes(embeddedFile.lang) || !sfc.scriptSetup?.ast) return;
const edition = options.edition || "kevinEdition";
const defineProps = getDefineProp(ts, sfc, edition);
if (defineProps.length === 0) return;
transformDefineProp({
codes: embeddedFile.content,
defineProps,
version: target,
edition
});
}
};
};
var define_prop_default = plugin;
//#endregion
export { define_prop_default, plugin };