From dd6c13eac6d6e40f86bde2a4ca2082f49fdfb5e8 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <32807958+zhiyuanzmj@users.noreply.github.com> Date: Fri, 10 May 2024 15:58:22 +0800 Subject: [PATCH] fix(language-core): support defineModel for generic component (#4345) --- .../lib/codegen/script/scriptSetup.ts | 41 +++++++++++-------- .../tsc/tests/__snapshots__/dts.spec.ts.snap | 30 +++++++++++--- .../component-meta/generic/component.vue | 1 + .../generic/custom-extension-component.cext | 1 + .../component-meta/generic/main.vue | 10 +++++ test-workspace/tsc/vue3/defineProp_B/main.vue | 2 +- 6 files changed, 60 insertions(+), 25 deletions(-) create mode 100644 test-workspace/component-meta/generic/main.vue diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 71abfabc85..015c2f3297 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -46,12 +46,19 @@ export function* generateScriptSetup( + ` __VLS_expose?: NonNullable>['expose'],${newLine}` + ` __VLS_setup = (async () => {${newLine}`; yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, undefined, definePropMirrors); + + const emitTypes = ['typeof __VLS_modelEmitsType']; + + if (scriptSetupRanges.emits.define) { + emitTypes.unshift(`typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'}`); + } + yield ` return {} as {${newLine}` + ` props: ${ctx.helperTypes.Prettify.name} & __VLS_BuiltInPublicProps,${newLine}` + ` expose(exposed: import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,${newLine}` + ` attrs: any,${newLine}` + ` slots: ReturnType,${newLine}` - + ` emit: typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'} & typeof __VLS_modelEmitsType,${newLine}` + + ` emit: ${emitTypes.join(' & ')},${newLine}` + ` }${endOfLine}`; yield ` })(),${newLine}`; // __VLS_setup = (async () => { yield `) => ({} as import('${options.vueCompilerOptions.lib}').VNode & { __ctx?: Awaited }))`; @@ -263,27 +270,25 @@ function* generateComponentProps( scriptSetupRanges: ScriptSetupRanges, definePropMirrors: Map, ): Generator { - if (scriptSetupRanges.props.define?.arg || scriptSetupRanges.emits.define) { - yield `const __VLS_fnComponent = ` - + `(await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; - if (scriptSetupRanges.props.define?.arg) { - yield ` props: `; - yield generateSfcBlockSection(scriptSetup, scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end, codeFeatures.navigation); - yield `,${newLine}`; - } + yield `const __VLS_fnComponent = ` + + `(await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; + if (scriptSetupRanges.props.define?.arg) { + yield ` props: `; + yield generateSfcBlockSection(scriptSetup, scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end, codeFeatures.navigation); + yield `,${newLine}`; + } + if (scriptSetupRanges.emits.define || scriptSetupRanges.defineProp.some(p => p.isModel)) { + yield ` emits: ({} as __VLS_NormalizeEmits),${newLine}`; } - yield `})${endOfLine}`; - yield `let __VLS_functionalComponentProps!: `; - yield `${ctx.helperTypes.OmitKeepDiscriminatedUnion.name}['$props'], keyof __VLS_BuiltInPublicProps>`; - yield endOfLine; - } - else { - yield `let __VLS_functionalComponentProps!: {}${endOfLine}`; + yield `>),${newLine}`; } + yield `})${endOfLine}`; + yield `let __VLS_functionalComponentProps!: `; + yield `${ctx.helperTypes.OmitKeepDiscriminatedUnion.name}['$props'], keyof __VLS_BuiltInPublicProps>`; + yield endOfLine; yield `type __VLS_BuiltInPublicProps =${newLine}` + ` import('${options.vueCompilerOptions.lib}').VNodeProps${newLine}` diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index 67094f61fe..d9c9f94ec2 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -75,7 +75,9 @@ type __VLS_Prettify = { exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.vue.d.ts 1`] = ` "declare const _default: (__VLS_props: { + "onUpdate:title"?: (title: string) => any; onBar?: (data: number) => any; + title?: string; foo: number; } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, __VLS_ctx?: { slots: Readonly<{ @@ -88,12 +90,14 @@ exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.v }): any; }; attrs: any; - emit: (e: 'bar', data: number) => void; + emit: ((e: 'bar', data: number) => void) & ((evt: "update:title", title: string) => void); }, __VLS_expose?: (exposed: import("vue").ShallowUnwrapRef<{ baz: number; }>) => void, __VLS_setup?: Promise<{ props: { + "onUpdate:title"?: (title: string) => any; onBar?: (data: number) => any; + title?: string; foo: number; } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps; expose(exposed: import("vue").ShallowUnwrapRef<{ @@ -109,13 +113,15 @@ exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.v foo: number; }): any; }; - emit: (e: 'bar', data: number) => void; + emit: ((e: 'bar', data: number) => void) & ((evt: "update:title", title: string) => void); }>) => import("vue").VNode & { __ctx?: { props: { + "onUpdate:title"?: (title: string) => any; onBar?: (data: number) => any; + title?: string; foo: number; } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps; expose(exposed: import("vue").ShallowUnwrapRef<{ @@ -131,7 +137,7 @@ exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.v foo: number; }): any; }; - emit: (e: 'bar', data: number) => void; + emit: ((e: 'bar', data: number) => void) & ((evt: "update:title", title: string) => void); }; }; export default _default; @@ -143,7 +149,9 @@ type __VLS_Prettify = { exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: generic/custom-extension-component.cext.d.ts 1`] = ` "declare const _default: (__VLS_props: { + "onUpdate:title"?: (title: string) => any; onBar?: (data: number) => any; + title?: string; foo: number; } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, __VLS_ctx?: { slots: Readonly<{ @@ -156,12 +164,14 @@ exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: g }): any; }; attrs: any; - emit: (e: 'bar', data: number) => void; + emit: ((e: 'bar', data: number) => void) & ((evt: "update:title", title: string) => void); }, __VLS_expose?: (exposed: import("vue").ShallowUnwrapRef<{ baz: number; }>) => void, __VLS_setup?: Promise<{ props: { + "onUpdate:title"?: (title: string) => any; onBar?: (data: number) => any; + title?: string; foo: number; } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps; expose(exposed: import("vue").ShallowUnwrapRef<{ @@ -177,13 +187,15 @@ exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: g foo: number; }): any; }; - emit: (e: 'bar', data: number) => void; + emit: ((e: 'bar', data: number) => void) & ((evt: "update:title", title: string) => void); }>) => import("vue").VNode & { __ctx?: { props: { + "onUpdate:title"?: (title: string) => any; onBar?: (data: number) => any; + title?: string; foo: number; } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps; expose(exposed: import("vue").ShallowUnwrapRef<{ @@ -199,7 +211,7 @@ exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: g foo: number; }): any; }; - emit: (e: 'bar', data: number) => void; + emit: ((e: 'bar', data: number) => void) & ((evt: "update:title", title: string) => void); }; }; export default _default; @@ -209,6 +221,12 @@ type __VLS_Prettify = { " `; +exports[`vue-tsc-dts > Input: generic/main.vue, Output: generic/main.vue.d.ts 1`] = ` +"declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; +export default _default; +" +`; + exports[`vue-tsc-dts > Input: non-component/component.ts, Output: non-component/component.d.ts 1`] = ` "declare const _default: {}; export default _default; diff --git a/test-workspace/component-meta/generic/component.vue b/test-workspace/component-meta/generic/component.vue index 39664c35ac..d5b2eebe11 100644 --- a/test-workspace/component-meta/generic/component.vue +++ b/test-workspace/component-meta/generic/component.vue @@ -3,4 +3,5 @@ defineProps<{ foo: number }>(); defineEmits<{ (e: 'bar', data: number): void }>(); defineExpose({ baz: {} as number }); defineSlots<{ default?(data: { foo: number }): any }>(); +defineModel('title'); diff --git a/test-workspace/component-meta/generic/custom-extension-component.cext b/test-workspace/component-meta/generic/custom-extension-component.cext index 39664c35ac..d5b2eebe11 100644 --- a/test-workspace/component-meta/generic/custom-extension-component.cext +++ b/test-workspace/component-meta/generic/custom-extension-component.cext @@ -3,4 +3,5 @@ defineProps<{ foo: number }>(); defineEmits<{ (e: 'bar', data: number): void }>(); defineExpose({ baz: {} as number }); defineSlots<{ default?(data: { foo: number }): any }>(); +defineModel('title'); diff --git a/test-workspace/component-meta/generic/main.vue b/test-workspace/component-meta/generic/main.vue new file mode 100644 index 0000000000..3da133f0d5 --- /dev/null +++ b/test-workspace/component-meta/generic/main.vue @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/test-workspace/tsc/vue3/defineProp_B/main.vue b/test-workspace/tsc/vue3/defineProp_B/main.vue index 5f8b0e80ca..9f2733aa2a 100644 --- a/test-workspace/tsc/vue3/defineProp_B/main.vue +++ b/test-workspace/tsc/vue3/defineProp_B/main.vue @@ -31,7 +31,7 @@ declare const ScriptSetupGenericExact: ( } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, attrs: any, slots: {}, - emit: any, + emit: {}, expose(_exposed: {}): void, }> ) => import('vue').VNode & { __ctx?: Awaited; };