From a78acac01eb23562d9ce28906a4a185911c57131 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Mon, 16 May 2022 01:58:12 +0800 Subject: [PATCH] feat: add option to simplify script setup virtual code for js close #1192 --- .../schemas/vue-tsconfig.schema.json | 8 + .../vue-code-gen/src/generators/script.ts | 138 ++++++++++++------ packages/vue-code-gen/src/index.ts | 2 + .../src/parsers/scriptSetupRanges.ts | 25 +++- packages/vue-typescript/src/types.ts | 1 + .../vue-typescript/src/use/useSfcScriptGen.ts | 3 + 6 files changed, 131 insertions(+), 46 deletions(-) diff --git a/extensions/vscode-vue-language-features/schemas/vue-tsconfig.schema.json b/extensions/vscode-vue-language-features/schemas/vue-tsconfig.schema.json index 0b0ace4db..9402e4a9f 100644 --- a/extensions/vscode-vue-language-features/schemas/vue-tsconfig.schema.json +++ b/extensions/vscode-vue-language-features/schemas/vue-tsconfig.schema.json @@ -24,6 +24,14 @@ ], "markdownDescription": "Implicit wrap object literal component options export with `defineComponent()`." }, + "experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup": { + "enum": [ + true, + false, + "onlyJs" + ], + "markdownDescription": "https://github.com/johnsoncodehk/volar/issues/1192" + }, "experimentalTemplateCompilerOptions": { "type": "object", "markdownDescription": "https://github.com/johnsoncodehk/volar/issues/576" diff --git a/packages/vue-code-gen/src/generators/script.ts b/packages/vue-code-gen/src/generators/script.ts index b2259dc34..7829704f0 100644 --- a/packages/vue-code-gen/src/generators/script.ts +++ b/packages/vue-code-gen/src/generators/script.ts @@ -23,6 +23,7 @@ export function generate( getStyleBindTexts: () => string[], vueLibName: string, shimComponentOptions: boolean, + downgradePropsAndEmitsToSetupReturnOnScriptSetup: boolean, ) { const codeGen = new CodeGen(); @@ -268,7 +269,7 @@ export function generate( }, ); - if (scriptSetupRanges?.withDefaultsArg) { + if (scriptSetupRanges.propsTypeArg && scriptSetupRanges?.withDefaultsArg) { // fix https://github.com/johnsoncodehk/volar/issues/1187 codeGen.addText(`const __VLS_withDefaultsArg = ((t: T) => t)(`); addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); @@ -285,42 +286,44 @@ export function generate( codeGen.addText(`),\n`); } if (scriptSetup && scriptSetupRanges) { - if (scriptSetupRanges.propsRuntimeArg || scriptSetupRanges.propsTypeArg) { - codeGen.addText(`props: (`); - if (scriptSetupRanges.propsTypeArg) { + if (!downgradePropsAndEmitsToSetupReturnOnScriptSetup) { + if (scriptSetupRanges.propsRuntimeArg || scriptSetupRanges.propsTypeArg) { + codeGen.addText(`props: (`); + if (scriptSetupRanges.propsTypeArg) { - usedTypes.DefinePropsToOptions = true; - codeGen.addText(`{} as `); + usedTypes.DefinePropsToOptions = true; + codeGen.addText(`{} as `); - if (scriptSetupRanges.withDefaultsArg) { - usedTypes.mergePropDefaults = true; - codeGen.addText(`__VLS_WithDefaults<`); - } + if (scriptSetupRanges.withDefaultsArg) { + usedTypes.mergePropDefaults = true; + codeGen.addText(`__VLS_WithDefaults<`); + } - codeGen.addText(`__VLS_TypePropsToRuntimeProps<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - codeGen.addText(`>`); - - if (scriptSetupRanges.withDefaultsArg) { - codeGen.addText(`, typeof __VLS_withDefaultsArg`); + codeGen.addText(`__VLS_TypePropsToRuntimeProps<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); codeGen.addText(`>`); + + if (scriptSetupRanges.withDefaultsArg) { + codeGen.addText(`, typeof __VLS_withDefaultsArg`); + codeGen.addText(`>`); + } } + else if (scriptSetupRanges.propsRuntimeArg) { + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); + } + codeGen.addText(`),\n`); } - else if (scriptSetupRanges.propsRuntimeArg) { - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); + if (scriptSetupRanges.emitsTypeArg) { + usedTypes.ConstructorOverloads = true; + codeGen.addText(`emits: ({} as __VLS_UnionToIntersection<__VLS_ConstructorOverloads<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); + codeGen.addText(`>>),\n`); + } + else if (scriptSetupRanges.emitsRuntimeArg) { + codeGen.addText(`emits: (`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); + codeGen.addText(`),\n`); } - codeGen.addText(`),\n`); - } - if (scriptSetupRanges.emitsTypeArg) { - usedTypes.ConstructorOverloads = true; - codeGen.addText(`emits: ({} as __VLS_UnionToIntersection<__VLS_ConstructorOverloads<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); - codeGen.addText(`>>),\n`); - } - else if (scriptSetupRanges.emitsRuntimeArg) { - codeGen.addText(`emits: (`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); - codeGen.addText(`),\n`); } const bindingsArr: { bindings: { start: number, end: number; }[], @@ -352,24 +355,72 @@ export function generate( } writeTemplate(); codeGen.addText(`};\n`); + } - if (scriptSetupRanges.exposeTypeArg) { - codeGen.addText(`return { } as `); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeTypeArg.start, scriptSetupRanges.exposeTypeArg.end); - codeGen.addText(`;\n`); + codeGen.addText(`return {\n`); + + if (downgradePropsAndEmitsToSetupReturnOnScriptSetup) { + // fill $props + if (scriptSetupRanges.propsTypeArg) { + // NOTE: defineProps is inaccurate for $props + codeGen.addText(`$props: defineProps<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); + codeGen.addText(`>(),\n`); } - else if (scriptSetupRanges.exposeRuntimeArg) { - codeGen.addText(`return `); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end); - codeGen.addText(`;\n`); + else if (scriptSetupRanges.propsRuntimeArg) { + // NOTE: defineProps is inaccurate for $props + codeGen.addText(`$props: defineProps(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); + codeGen.addText(`),\n`); + } + // fill $emit + if (scriptSetupRanges.emitsAssignName) { + codeGen.addText(`$emit: ${scriptSetupRanges.emitsAssignName},\n`); + } + else if (scriptSetupRanges.emitsTypeArg) { + codeGen.addText(`$emit: defineEmits<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); + codeGen.addText(`>(),\n`); + } + else if (scriptSetupRanges.emitsRuntimeArg) { + codeGen.addText(`$emit: defineEmits(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); + codeGen.addText(`),\n`); } - else { - codeGen.addText(`return { };\n`); - }; } + if (lsType === 'script') { + if (scriptSetupRanges.exposeRuntimeArg) { + codeGen.addText(`...(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end); + codeGen.addText(`),\n`); + } + if (scriptSetupRanges.exposeTypeArg) { + codeGen.addText(`...({} as `); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeTypeArg.start, scriptSetupRanges.exposeTypeArg.end); + codeGen.addText(`),\n`); + } + } if (lsType === 'template') { - codeGen.addText(`return {\n`); + // fill ctx from props + if (downgradePropsAndEmitsToSetupReturnOnScriptSetup) { + if (scriptSetupRanges.propsAssignName) { + codeGen.addText(`...${scriptSetupRanges.propsAssignName},\n`); + } + else if (scriptSetupRanges.withDefaultsArg && scriptSetupRanges.propsTypeArg) { + codeGen.addText(`...withDefaults(defineProps<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); + codeGen.addText(`>(), `); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); + codeGen.addText(`),\n`); + } + else if (scriptSetupRanges.propsRuntimeArg) { + codeGen.addText(`...defineProps(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); + codeGen.addText(`),\n`); + } + } + // bindings for (const { bindings, content } of bindingsArr) { for (const expose of bindings) { const varName = content.substring(expose.start, expose.end); @@ -401,8 +452,9 @@ export function generate( }); } } - codeGen.addText(`};\n`); } + codeGen.addText(`};\n`); + codeGen.addText(`},\n`); } diff --git a/packages/vue-code-gen/src/index.ts b/packages/vue-code-gen/src/index.ts index 0a792d4bd..407606c84 100644 --- a/packages/vue-code-gen/src/index.ts +++ b/packages/vue-code-gen/src/index.ts @@ -19,6 +19,7 @@ export function generateSFCScriptTypeCheckCode( scriptCode: string | undefined, scriptSetupCode: string | undefined, shimComponentOptions: boolean, + downgradePropsAndEmitsToSetupReturnOnScriptSetup: boolean, templateAst?: CompilerDOM.RootNode, cssVars?: string[], vueLibName = 'vue', @@ -45,6 +46,7 @@ export function generateSFCScriptTypeCheckCode( () => cssVars ?? [], vueLibName, shimComponentOptions, + downgradePropsAndEmitsToSetupReturnOnScriptSetup, ); return { diff --git a/packages/vue-code-gen/src/parsers/scriptSetupRanges.ts b/packages/vue-code-gen/src/parsers/scriptSetupRanges.ts index f28c90ebd..05d902c02 100644 --- a/packages/vue-code-gen/src/parsers/scriptSetupRanges.ts +++ b/packages/vue-code-gen/src/parsers/scriptSetupRanges.ts @@ -9,8 +9,10 @@ export function parseScriptSetupRanges(ts: typeof import('typescript/lib/tsserve let notOnTopTypeExports: TextRange[] = []; let importSectionEndOffset = 0; let withDefaultsArg: TextRange | undefined; + let propsAssignName: string | undefined; let propsRuntimeArg: TextRange | undefined; let propsTypeArg: TextRange | undefined; + let emitsAssignName: string | undefined; let emitsRuntimeArg: TextRange | undefined; let emitsTypeArg: TextRange | undefined; let exposeRuntimeArg: TextRange | undefined; @@ -37,7 +39,7 @@ export function parseScriptSetupRanges(ts: typeof import('typescript/lib/tsserve notOnTopTypeExports.push(_getStartEnd(node)); } }); - ast.forEachChild(visitNode); + ast.forEachChild(child => visitNode(child, ast)); return { importSectionEndOffset, @@ -45,8 +47,10 @@ export function parseScriptSetupRanges(ts: typeof import('typescript/lib/tsserve bindings, typeBindings, withDefaultsArg, + propsAssignName, propsRuntimeArg, propsTypeArg, + emitsAssignName, emitsRuntimeArg, emitsTypeArg, emitsTypeNums, @@ -57,7 +61,7 @@ export function parseScriptSetupRanges(ts: typeof import('typescript/lib/tsserve function _getStartEnd(node: ts.Node) { return getStartEnd(node, ast); } - function visitNode(node: ts.Node) { + function visitNode(node: ts.Node, parent: ts.Node) { if ( ts.isCallExpression(node) && ts.isIdentifier(node.expression) @@ -68,9 +72,15 @@ export function parseScriptSetupRanges(ts: typeof import('typescript/lib/tsserve const runtimeArg = node.arguments[0]; if (callText === 'defineProps') { propsRuntimeArg = _getStartEnd(runtimeArg); + if (ts.isVariableDeclaration(parent)) { + propsAssignName = parent.name.getText(ast); + } } else if (callText === 'defineEmits') { emitsRuntimeArg = _getStartEnd(runtimeArg); + if (ts.isVariableDeclaration(parent)) { + emitsAssignName = parent.name.getText(ast); + } } else if (callText === 'defineExpose') { exposeRuntimeArg = _getStartEnd(runtimeArg); @@ -80,12 +90,18 @@ export function parseScriptSetupRanges(ts: typeof import('typescript/lib/tsserve const typeArg = node.typeArguments[0]; if (callText === 'defineProps') { propsTypeArg = _getStartEnd(typeArg); + if (ts.isVariableDeclaration(parent)) { + propsAssignName = parent.name.getText(ast); + } } else if (callText === 'defineEmits') { emitsTypeArg = _getStartEnd(typeArg); if (ts.isTypeLiteralNode(typeArg)) { emitsTypeNums = typeArg.members.length; } + if (ts.isVariableDeclaration(parent)) { + emitsAssignName = parent.name.getText(ast); + } } else if (callText === 'defineExpose') { exposeTypeArg = _getStartEnd(typeArg); @@ -97,9 +113,12 @@ export function parseScriptSetupRanges(ts: typeof import('typescript/lib/tsserve const arg = node.arguments[1]; withDefaultsArg = _getStartEnd(arg); } + if (ts.isVariableDeclaration(parent)) { + propsAssignName = parent.name.getText(ast); + } } } - node.forEachChild(child => visitNode(child)); + node.forEachChild(child => visitNode(child, node)); } } diff --git a/packages/vue-typescript/src/types.ts b/packages/vue-typescript/src/types.ts index 8739a93e4..0f29167d0 100644 --- a/packages/vue-typescript/src/types.ts +++ b/packages/vue-typescript/src/types.ts @@ -13,6 +13,7 @@ export interface ITemplateScriptData { export interface VueCompilerOptions { experimentalCompatMode?: 2 | 3; experimentalImplicitWrapComponentOptionsWithDefineComponent?: boolean | 'onlyJs'; + experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup?: boolean | 'onlyJs'; experimentalTemplateCompilerOptions?: any; experimentalTemplateCompilerOptionsRequirePath?: string; experimentalDisableTemplateSupport?: boolean; diff --git a/packages/vue-typescript/src/use/useSfcScriptGen.ts b/packages/vue-typescript/src/use/useSfcScriptGen.ts index 3f408fd60..f84254472 100644 --- a/packages/vue-typescript/src/use/useSfcScriptGen.ts +++ b/packages/vue-typescript/src/use/useSfcScriptGen.ts @@ -49,6 +49,9 @@ export function useSfcScriptGen( (compilerOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent ?? 'onlyJs') === 'onlyJs' ? lang.value === 'js' || lang.value === 'jsx' : !!compilerOptions.experimentalImplicitWrapComponentOptionsWithDefineComponent, + (compilerOptions.experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup ?? 'onlyJs') === 'onlyJs' + ? lang.value === 'js' || lang.value === 'jsx' + : !!compilerOptions.experimentalDowngradePropsAndEmitsToSetupReturnOnScriptSetup, ) ); const file = computed(() => {