From b3e592a3982c58f72a4128cb43a3e61b780eaf1b Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Tue, 11 Oct 2022 18:17:24 +0800 Subject: [PATCH] refactor: export script setup component as functional component type --- .../src/generators/script.ts | 87 ++++++++----------- .../src/generators/template.ts | 36 ++++---- .../vue-language-core/src/utils/localTypes.ts | 4 +- 3 files changed, 61 insertions(+), 66 deletions(-) diff --git a/vue-language-tools/vue-language-core/src/generators/script.ts b/vue-language-tools/vue-language-core/src/generators/script.ts index 9927072e9..0bed8ecd5 100644 --- a/vue-language-tools/vue-language-core/src/generators/script.ts +++ b/vue-language-tools/vue-language-core/src/generators/script.ts @@ -173,7 +173,7 @@ export function generate( addVirtualCode('script', scriptRanges.exportDefault.end, sfc.script.content.length); } } - function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { + function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end?: number) { codeGen.push([ sfc[vueTag]!.content.substring(start, end), vueTag, @@ -234,10 +234,7 @@ export function generate( if (sfc.scriptSetup && scriptSetupRanges) { - if (scriptRanges?.exportDefault) { - codeGen.push('(() => {\n'); - } - else { + if (!scriptRanges?.exportDefault) { // fix https://github.com/johnsoncodehk/volar/issues/1127 codeGen.push([ '', @@ -245,29 +242,26 @@ export function generate( 0, { diagnostic: true }, ]); - codeGen.push('export default ('); - if (vueCompilerOptions.experimentalRfc436 && sfc.scriptSetup.generic) { - codeGen.push(`<${sfc.scriptSetup.generic}>`); - } - codeGen.push('() => {\n'); + codeGen.push('export default '); } - + if (vueCompilerOptions.experimentalRfc436 && sfc.scriptSetup.generic) { + codeGen.push(`<${sfc.scriptSetup.generic}>`); + } + codeGen.push('('); + if (scriptSetupRanges.propsTypeArg) { + codeGen.push('__VLS_props: '); + addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); + } + codeGen.push(') => {\n'); codeGen.push('const __VLS_setup = async () => {\n'); - - codeGen.push([ - sfc.scriptSetup.content.substring(scriptSetupRanges.importSectionEndOffset), - 'scriptSetup', - scriptSetupRanges.importSectionEndOffset, - { - hover: true, - references: true, - definition: true, - diagnostic: true, - rename: true, - completion: true, - semanticTokens: true, - }, - ]); + if (scriptSetupRanges.propsTypeArg) { + addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset, scriptSetupRanges.propsTypeArg.start); + codeGen.push('typeof __VLS_props'); + addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.end); + } + else { + addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset); + } if (scriptSetupRanges.propsTypeArg && scriptSetupRanges.withDefaultsArg) { // fix https://github.com/johnsoncodehk/volar/issues/1187 @@ -299,9 +293,7 @@ export function generate( codeGen.push(`__VLS_WithDefaults<`); } - codeGen.push(`__VLS_TypePropsToRuntimeProps<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - codeGen.push(`>`); + codeGen.push(`__VLS_TypePropsToRuntimeProps`); if (scriptSetupRanges.withDefaultsArg) { codeGen.push(`, typeof __VLS_withDefaultsArg`); @@ -381,34 +373,29 @@ export function generate( writeTemplate(); - codeGen.push(`return {} as typeof __VLS_Component`); - codeGen.push(` & (new `); - if (vueCompilerOptions.experimentalRfc436 && sfc.scriptSetup.generic) { - if (scriptSetupRanges.propsTypeArg) { - codeGen.push(`<${sfc.scriptSetup.generic}>(__VLS_props: `); - addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - codeGen.push(`) => {\n`); - codeGen.push(`$props: typeof __VLS_props,\n`); - } - else { - codeGen.push(`<${sfc.scriptSetup.generic}>() => {\n`); - } - if (scriptSetupRanges.emitsTypeArg) { - codeGen.push(`$emit: `); - addVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); - codeGen.push(`,\n`); - } + codeGen.push(`return {} as Omit & Omit, '$slots' | '$emit'>`); + codeGen.push(` & {\n`); + if (scriptSetupRanges.propsTypeArg) { + codeGen.push(`props: typeof __VLS_props,\n`); } else { - codeGen.push(`() => {\n`); + codeGen.push(`props: InstanceType['$props'],\n`); } + codeGen.push(`$emit: `); + if (scriptSetupRanges.emitsTypeArg) { + addVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); + } + else { + codeGen.push(`InstanceType['$emit']`); + } + codeGen.push(`,\n`); if (htmlGen?.slotsNum) { - codeGen.push(`${getSlotsPropertyName(vueVersion)}: ReturnType,\n`); + codeGen.push(`children: ReturnType,\n`); } - codeGen.push(`});\n`); + codeGen.push(`};\n`); codeGen.push(`};\n`); codeGen.push(`return {} as unknown as Awaited>;\n`); - codeGen.push(`})()`); + codeGen.push(`}`); if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.end !== scriptRanges.exportDefault.end) { addVirtualCode('script', scriptRanges.exportDefault.expression.end, scriptRanges.exportDefault.end); } diff --git a/vue-language-tools/vue-language-core/src/generators/template.ts b/vue-language-tools/vue-language-core/src/generators/template.ts index d33dcf08a..9d575edad 100644 --- a/vue-language-tools/vue-language-core/src/generators/template.ts +++ b/vue-language-tools/vue-language-core/src/generators/template.ts @@ -660,7 +660,6 @@ export function generate( function writeEvents(node: CompilerDOM.ElementNode) { let _varComponentInstance: string | undefined; - let writedInstance = false; for (const prop of node.props) { if ( @@ -802,20 +801,23 @@ export function generate( function tryWriteInstance() { - if (writedInstance) { - return _varComponentInstance; - } - - const componentVar = componentVars[node.tag]; + if (!_varComponentInstance) { + const componentVar = componentVars[node.tag]; - if (componentVar) { - _varComponentInstance = `__VLS_${elementIndex++}`; - codeGen.push(`const ${_varComponentInstance} = new ${componentVar}({ `); - writeProps(node, 'class', 'slots'); - codeGen.push(`});\n`); + if (componentVar) { + const _varComponentInstanceA = `__VLS_${elementIndex++}`; + const _varComponentInstanceB = `__VLS_${elementIndex++}`; + _varComponentInstance = `__VLS_${elementIndex++}`; + codeGen.push(`const ${_varComponentInstanceA} = new ${componentVar}({ `); + writeProps(node, 'class', 'slots'); + codeGen.push(`});\n`); + codeGen.push(`const ${_varComponentInstanceB} = ${componentVar}({ `); + writeProps(node, 'class', 'slots'); + codeGen.push(`});\n`); + codeGen.push(`let ${_varComponentInstance}!: import('./__VLS_types.js').PickNotAny;\n`); + } } - writedInstance = true; return _varComponentInstance; } } @@ -1233,15 +1235,19 @@ export function generate( && prop.name === 'slot' ) { - const varComponentInstance = `__VLS_${elementIndex++}`; + const varComponentInstanceA = `__VLS_${elementIndex++}`; + const varComponentInstanceB = `__VLS_${elementIndex++}`; const varSlots = `__VLS_${elementIndex++}`; if (componentVar && parentEl) { - codeGen.push(`const ${varComponentInstance} = new ${componentVar}({ `); + codeGen.push(`const ${varComponentInstanceA} = new ${componentVar}({ `); + writeProps(parentEl, 'class', 'slots'); + codeGen.push(`});\n`); + codeGen.push(`const ${varComponentInstanceB} = ${componentVar}({ `); writeProps(parentEl, 'class', 'slots'); codeGen.push(`});\n`); writeInterpolationVarsExtraCompletion(); - codeGen.push(`let ${varSlots}!: import('./__VLS_types.js').ExtractComponentSlots;\n`); + codeGen.push(`let ${varSlots}!: import('./__VLS_types.js').ExtractComponentSlots>;\n`); } if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { diff --git a/vue-language-tools/vue-language-core/src/utils/localTypes.ts b/vue-language-tools/vue-language-core/src/utils/localTypes.ts index d9d72df94..3043a3ac6 100644 --- a/vue-language-tools/vue-language-core/src/utils/localTypes.ts +++ b/vue-language-tools/vue-language-core/src/utils/localTypes.ts @@ -53,16 +53,18 @@ export declare function directiveFunction(dir: T): export declare function withScope(ctx: T, scope: K): ctx is T & K; export declare function makeOptional(t: T): { [K in keyof T]?: T[K] }; +// TODO: make it stricter between class component type and functional component type export type ExtractComponentSlots = IsAny extends true ? Record : T extends { ${slots}?: infer S } ? { [K in keyof S]-?: S[K] extends ((obj: infer O) => any) | undefined ? O : any } + : T extends { children?: infer S } ? { [K in keyof S]-?: S[K] extends ((obj: infer O) => any) | undefined ? O : any } : Record; export type FillingEventArg_ParametersLength any> = IsAny> extends true ? -1 : Parameters['length']; export type FillingEventArg = E extends (...args: any) => any ? FillingEventArg_ParametersLength extends 0 ? ($event?: undefined) => ReturnType : E : E; export type ExtractProps = - T extends FunctionalComponent ? P + T extends (...args: any) => { props: infer Props } ? Props : T extends new (...args: any) => { $props: infer Props } ? Props : T; // IntrinsicElement export type ReturnVoid = T extends (...payload: infer P) => any ? (...payload: P) => void : (...args: any) => void;