diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index fa49ad1791..457ba0c133 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -142,7 +142,7 @@ export function* generatePropsOption( optionExpCodes.push(generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation)); } if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) { - let attrsType = `ReturnType['attrs']`; + let attrsType = `typeof __VLS_templateResult['attrs']`; if (hasEmitsOption) { attrsType = `Omit<${attrsType}, \`on\${string}\`>`; } diff --git a/packages/language-core/lib/codegen/script/globalTypes.ts b/packages/language-core/lib/codegen/script/globalTypes.ts index 9638adef0f..541a1512d3 100644 --- a/packages/language-core/lib/codegen/script/globalTypes.ts +++ b/packages/language-core/lib/codegen/script/globalTypes.ts @@ -66,11 +66,7 @@ declare global { function __VLS_makeOptional(t: T): { [K in keyof T]?: T[K] }; function __VLS_nonNullable(t: T): T extends null | undefined ? never : T; - type __VLS_SelfComponent = string extends N ? {} : N extends string ? { [P in N]: C } : {}; - type __VLS_WithComponent = - N1 extends keyof Ctx ? N1 extends N0 ? Pick : { [K in N0]: Ctx[N1] } : - N2 extends keyof Ctx ? N2 extends N0 ? Pick : { [K in N0]: Ctx[N2] } : - N3 extends keyof Ctx ? N3 extends N0 ? Pick : { [K in N0]: Ctx[N3] } : + type __VLS_WithComponent = N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index 92da84d736..322eaebd01 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -101,9 +101,16 @@ export function* generateScript(options: ScriptCodegenOptions): Generator {`; + yield* generateTemplate(options, ctx, true); + yield `},${newLine}`; + yield generateSfcBlockSection(options.sfc.script, classBlockEnd, options.sfc.script.content.length, codeFeatures.all); + } } else { yield generateSfcBlockSection(options.sfc.script, 0, options.sfc.script.content.length, codeFeatures.all); @@ -118,12 +125,7 @@ export function* generateScript(options: ScriptCodegenOptions): Generator { if (options.sfc.scriptSetup && options.scriptSetupRanges) { - yield `let __VLS_defineComponent!: typeof import('${options.vueCompilerOptions.lib}').defineComponent${endOfLine}`; - yield `const __VLS_internalComponent = __VLS_defineComponent({${newLine}`; + yield `const __VLS_internalComponent = (await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; yield `setup() {${newLine}`; yield `return {${newLine}`; if (ctx.bypassDefineComponent) { diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 6c6c014597..0e99fa781c 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -280,8 +280,8 @@ function* generateSetupFunction( yield* generateModelEmits(options, scriptSetup, scriptSetupRanges); yield* generateStyleModules(options, ctx); yield* generateTemplate(options, ctx, false); - yield `type __VLS_Refs = ReturnType['refs']${endOfLine}`; - yield `type __VLS_Slots = ReturnType['slots']${endOfLine}`; + yield `type __VLS_Refs = typeof __VLS_templateResult['refs']${endOfLine}`; + yield `type __VLS_Slots = typeof __VLS_templateResult['slots']${endOfLine}`; if (syntax) { if (!options.vueCompilerOptions.skipTemplateCodegen && (options.templateCodegen?.hasSlot || scriptSetupRanges?.slots.define)) { diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index c7c8a9cbcf..7507e2dffd 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -4,114 +4,99 @@ import { getSlotsPropertyName, hyphenateTag } from '../../utils/shared'; import { endOfLine, newLine } from '../common'; import { TemplateCodegenContext, createTemplateCodegenContext } from '../template/context'; import { forEachInterpolationSegment } from '../template/interpolation'; +import { generateStyleScopedClasses } from '../template/styleScopedClasses'; import type { ScriptCodegenContext } from './context'; import { codeFeatures, type ScriptCodegenOptions } from './index'; import { generateInternalComponent } from './internalComponent'; -import { generateStyleScopedClasses } from '../template/styleScopedClasses'; - -export function* generateTemplate( - options: ScriptCodegenOptions, - ctx: ScriptCodegenContext, - isClassComponent: boolean -): Generator { - ctx.generatedTemplate = true; - if (!options.vueCompilerOptions.skipTemplateCodegen) { - if (isClassComponent) { - yield `__VLS_template = (() => {${newLine}`; - } - else { - yield `const __VLS_template = (() => {${newLine}`; - } - const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), edited: options.edited }); - yield `const __VLS_template_return = () => {${newLine}`; - yield* generateCtx(options, isClassComponent); - yield* generateTemplateContext(options, templateCodegenCtx); - yield* generateExportOptions(options); - yield* generateConstNameOption(options); - yield `}${endOfLine}`; - yield* generateInternalComponent(options, ctx, templateCodegenCtx); - yield `return __VLS_template_return${endOfLine}`; - yield `})()${endOfLine}`; +export function* generateTemplateCtx(options: ScriptCodegenOptions, isClassComponent: boolean): Generator { + const types = []; + if (isClassComponent) { + types.push(`typeof this`); } else { - yield `function __VLS_template() {${newLine}`; - const templateUsageVars = [...getTemplateUsageVars(options, ctx)]; - yield `// @ts-ignore${newLine}`; - yield `[${templateUsageVars.join(', ')}]${newLine}`; - yield `return { slots: {}, refs: {}, attrs: {} }${endOfLine}`; - yield `}${newLine}`; + types.push(`InstanceType<__VLS_PickNotAny {}>>`); } + if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileBaseName.endsWith(ext))) { + types.push(`typeof globalThis`); + } + if (options.sfc.styles.some(style => style.module)) { + types.push(`__VLS_StyleModules`); + } + yield `let __VLS_ctx!: ${types.join(' & ')}${endOfLine}`; } -function* generateExportOptions(options: ScriptCodegenOptions): Generator { - yield newLine; - yield `const __VLS_componentsOption = `; +export function* generateTemplateComponents(options: ScriptCodegenOptions): Generator { + const exps: Code[] = []; + if (options.sfc.script && options.scriptRanges?.exportDefault?.componentsOption) { - const componentsOption = options.scriptRanges.exportDefault.componentsOption; - yield [ + const { componentsOption } = options.scriptRanges.exportDefault; + exps.push([ options.sfc.script.content.substring(componentsOption.start, componentsOption.end), 'script', componentsOption.start, codeFeatures.navigation, - ]; - } - else { - yield `{}`; + ]); } - yield endOfLine; -} -function* generateConstNameOption(options: ScriptCodegenOptions): Generator { + let nameType: Code | undefined; if (options.sfc.script && options.scriptRanges?.exportDefault?.nameOption) { - const nameOption = options.scriptRanges.exportDefault.nameOption; - yield `const __VLS_name = `; - yield `${options.sfc.script.content.substring(nameOption.start, nameOption.end)} as const`; - yield endOfLine; + const { nameOption } = options.scriptRanges.exportDefault; + nameType = options.sfc.script.content.substring(nameOption.start, nameOption.end); } else if (options.sfc.scriptSetup) { yield `let __VLS_name!: '${options.scriptSetupRanges?.options.name ?? options.fileBaseName.substring(0, options.fileBaseName.lastIndexOf('.'))}'${endOfLine}`; + nameType = 'typeof __VLS_name'; } - else { - yield `const __VLS_name = undefined${endOfLine}`; + if (nameType) { + exps.push(`{} as { + [K in ${nameType}]: typeof __VLS_internalComponent + & (new () => { + ${getSlotsPropertyName(options.vueCompilerOptions.target)}: typeof ${options.scriptSetupRanges?.slots?.name ?? '__VLS_slots'} + }) + }`); + } + + exps.push(`{} as NonNullable`); + exps.push(`{} as __VLS_GlobalComponents`); + exps.push(`{} as typeof __VLS_ctx`); + + yield `const __VLS_components = {${newLine}`; + for (const type of exps) { + yield `...`; + yield type; + yield `,${newLine}`; } + yield `}${endOfLine}`; } -function* generateCtx( +export function* generateTemplate( options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, isClassComponent: boolean ): Generator { - yield `let __VLS_ctx!: `; - if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileBaseName.endsWith(ext))) { - yield `typeof globalThis & `; - } - if (!isClassComponent) { - yield `InstanceType<__VLS_PickNotAny {}>>`; + ctx.generatedTemplate = true; + + if (!options.vueCompilerOptions.skipTemplateCodegen) { + const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), edited: options.edited }); + yield* generateTemplateCtx(options, isClassComponent); + yield* generateTemplateComponents(options); + yield* generateTemplateBody(options, templateCodegenCtx); + yield* generateInternalComponent(options, ctx, templateCodegenCtx); } else { - yield `typeof this`; - } - /* CSS Module */ - if (options.sfc.styles.some(style => style.module)) { - yield ` & __VLS_StyleModules`; + const templateUsageVars = [...getTemplateUsageVars(options, ctx)]; + yield `// @ts-ignore${newLine}`; + yield `[${templateUsageVars.join(', ')}]${newLine}`; + yield `const __VLS_templateResult { slots: {}, refs: {}, attrs: {} }${endOfLine}`; } - yield endOfLine; } -function* generateTemplateContext( +function* generateTemplateBody( options: ScriptCodegenOptions, templateCodegenCtx: TemplateCodegenContext ): Generator { - /* Components */ - yield `/* Components */${newLine}`; - yield `let __VLS_otherComponents!: NonNullable & typeof __VLS_componentsOption${endOfLine}`; - yield `let __VLS_own!: __VLS_SelfComponent { ${getSlotsPropertyName(options.vueCompilerOptions.target)}: typeof ${options.scriptSetupRanges?.slots?.name ?? '__VLS_slots'} })>${endOfLine}`; - yield `let __VLS_localComponents!: typeof __VLS_otherComponents & Omit${endOfLine}`; - yield `let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents & typeof __VLS_ctx${endOfLine}`; // for html completion, TS references... - - /* Style Scoped */ const firstClasses = new Set(); - yield `/* Style Scoped */${newLine}`; yield `let __VLS_styleScopedClasses!: {}`; for (let i = 0; i < options.sfc.styles.length; i++) { const style = options.sfc.styles[i]; @@ -155,7 +140,7 @@ function* generateTemplateContext( } } - yield `return {${newLine}`; + yield `const __VLS_templateResult = {`; yield `slots: ${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'},${newLine}`; yield `refs: __VLS_refs as __VLS_PickRefsExpose,${newLine}`; yield `attrs: {} as Partial,${newLine}`; diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index b6aa1f43ce..c68a820da3 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -120,7 +120,7 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator `"${name}"`) .join(', '); diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index d708893965..878dcd52ac 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -634,24 +634,30 @@ export {}; `; exports[`vue-tsc-dts > Input: template-slots/component.vue, Output: template-slots/component.vue.d.ts 1`] = ` -"declare const __VLS_template: () => { +"declare var __VLS_0: {}; +declare var __VLS_1: { + num: number; +}; +declare var __VLS_2: { + str: string; +}; +declare var __VLS_3: { + num: number; + str: string; +}; +declare var __VLS_inheritedAttrs: {}; +declare const __VLS_refs: {}; +declare const __VLS_templateResult: { slots: { - "no-bind"?(_: {}): any; - default?(_: { - num: number; - }): any; - "named-slot"?(_: { - str: string; - }): any; - vbind?(_: { - num: number; - str: string; - }): any; + "no-bind"?(_: typeof __VLS_0): any; + default?(_: typeof __VLS_1): any; + "named-slot"?(_: typeof __VLS_2): any; + vbind?(_: typeof __VLS_3): any; }; - refs: __VLS_PickRefsExpose<{}>; - attrs: Partial<{}>; + refs: __VLS_PickRefsExpose; + attrs: Partial; }; -type __VLS_Slots = ReturnType['slots']; +type __VLS_Slots = typeof __VLS_templateResult['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; declare const _default: __VLS_WithTemplateSlots; export default _default; @@ -665,7 +671,9 @@ type __VLS_WithTemplateSlots = T & { exports[`vue-tsc-dts > Input: template-slots/component-define-slots.vue, Output: template-slots/component-define-slots.vue.d.ts 1`] = ` "import { VNode } from 'vue'; -declare const __VLS_template: () => { +declare var __VLS_inheritedAttrs: {}; +declare const __VLS_refs: {}; +declare const __VLS_templateResult: { slots: Readonly<{ default: (props: { num: number; @@ -691,10 +699,10 @@ declare const __VLS_template: () => { }) => VNode[]; 'no-bind': () => VNode[]; }; - refs: __VLS_PickRefsExpose<{}>; - attrs: Partial<{}>; + refs: __VLS_PickRefsExpose; + attrs: Partial; }; -type __VLS_Slots = ReturnType['slots']; +type __VLS_Slots = typeof __VLS_templateResult['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; declare const _default: __VLS_WithTemplateSlots; export default _default; @@ -707,7 +715,9 @@ type __VLS_WithTemplateSlots = T & { `; exports[`vue-tsc-dts > Input: template-slots/component-destructuring.vue, Output: template-slots/component-destructuring.vue.d.ts 1`] = ` -"declare const __VLS_template: () => { +"declare var __VLS_inheritedAttrs: {}; +declare const __VLS_refs: {}; +declare const __VLS_templateResult: { slots: Readonly<{ bottom: (props: { num: number; @@ -717,10 +727,10 @@ exports[`vue-tsc-dts > Input: template-slots/component-destructuring.vue, Output num: number; }) => any[]; }; - refs: __VLS_PickRefsExpose<{}>; - attrs: Partial<{}>; + refs: __VLS_PickRefsExpose; + attrs: Partial; }; -type __VLS_Slots = ReturnType['slots']; +type __VLS_Slots = typeof __VLS_templateResult['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; declare const _default: __VLS_WithTemplateSlots; export default _default; @@ -733,24 +743,30 @@ type __VLS_WithTemplateSlots = T & { `; exports[`vue-tsc-dts > Input: template-slots/component-no-script.vue, Output: template-slots/component-no-script.vue.d.ts 1`] = ` -"declare const __VLS_template: () => { +"declare var __VLS_0: {}; +declare var __VLS_1: { + num: number; +}; +declare var __VLS_2: { + str: string; +}; +declare var __VLS_3: { + num: number; + str: string; +}; +declare var __VLS_inheritedAttrs: {}; +declare const __VLS_refs: {}; +declare const __VLS_templateResult: { slots: { - "no-bind"?(_: {}): any; - default?(_: { - num: number; - }): any; - "named-slot"?(_: { - str: string; - }): any; - vbind?(_: { - num: number; - str: string; - }): any; + "no-bind"?(_: typeof __VLS_0): any; + default?(_: typeof __VLS_1): any; + "named-slot"?(_: typeof __VLS_2): any; + vbind?(_: typeof __VLS_3): any; }; - refs: __VLS_PickRefsExpose<{}>; - attrs: Partial<{}>; + refs: __VLS_PickRefsExpose; + attrs: Partial; }; -type __VLS_Slots = ReturnType['slots']; +type __VLS_Slots = typeof __VLS_templateResult['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; declare const _default: __VLS_WithTemplateSlots; export default _default;