From 7cbcb3f9adc7e0e00b816c26cbb1cde5bfffcf44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Fri, 18 Jul 2025 23:32:34 +0800 Subject: [PATCH 01/32] refactor: use variable `__VLS_export` to receive component and export it by default --- .../language-core/lib/codegen/localTypes.ts | 4 +- .../language-core/lib/codegen/script/index.ts | 128 +++++++++++------- .../lib/codegen/script/scriptSetup.ts | 27 ++-- .../lib/codegen/template/element.ts | 6 +- packages/language-core/lib/languagePlugin.ts | 19 +-- .../lib/parsers/scriptSetupRanges.ts | 2 +- packages/language-core/lib/plugins/vue-tsx.ts | 24 ++-- .../tsc/tests/__snapshots__/dts.spec.ts.snap | 54 +++++--- packages/tsc/tests/typecheck.spec.ts | 1 - .../lib/requests/getComponentEvents.ts | 2 +- .../lib/requests/getComponentNames.ts | 11 +- .../lib/requests/getComponentProps.ts | 2 +- .../typescript-plugin/lib/requests/utils.ts | 5 +- 13 files changed, 158 insertions(+), 127 deletions(-) diff --git a/packages/language-core/lib/codegen/localTypes.ts b/packages/language-core/lib/codegen/localTypes.ts index c41cd55cde..5f04f0f0fa 100644 --- a/packages/language-core/lib/codegen/localTypes.ts +++ b/packages/language-core/lib/codegen/localTypes.ts @@ -115,7 +115,7 @@ type __VLS_TypePropsToOption = { }, }; - function* generate(names: string[]) { + function* generate(...names: string[]) { const generated = new Set(); while (names.length) { used.clear(); @@ -123,7 +123,7 @@ type __VLS_TypePropsToOption = { if (generated.has(name)) { continue; } - const helper = helpers[name as keyof typeof helpers]; + const helper = helpers[name]; yield helper.generate(); generated.add(name); } diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index 19fcb2a789..c4b66af210 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -2,10 +2,11 @@ import * as path from 'path-browserify'; import type * as ts from 'typescript'; import type { ScriptRanges } from '../../parsers/scriptRanges'; import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges'; -import type { Code, Sfc, VueCompilerOptions } from '../../types'; +import type { Code, Sfc, SfcBlock, VueCompilerOptions } from '../../types'; import { codeFeatures } from '../codeFeatures'; import type { TemplateCodegenContext } from '../template/context'; import { endOfLine, generateSfcBlockSection, newLine } from '../utils'; +import { wrapWith } from '../utils/wrapWith'; import { generateComponentSelf } from './componentSelf'; import { createScriptCodegenContext, type ScriptCodegenContext } from './context'; import { generateScriptSetup, generateScriptSetupImports } from './scriptSetup'; @@ -29,7 +30,7 @@ export interface ScriptCodegenOptions { export function* generateScript(options: ScriptCodegenOptions): Generator { const ctx = createScriptCodegenContext(options); - yield* generateGlobalTypesPath(options); + yield* generateGlobalTypesReference(options); if (options.sfc.script?.src) { yield* generateSrc(options.sfc.script.src); @@ -43,14 +44,8 @@ export function* generateScript(options: ScriptCodegenOptions): Generator= 2 - ? options.vueCompilerOptions.optionsWrapper[1] - : '[Missing optionsWrapper[1]]', - }); - yield generateSfcBlockSection(options.sfc.script, 0, exportDefault.expression.start, codeFeatures.all); - yield options.vueCompilerOptions.optionsWrapper[0]; + else if (exportDefault) { + let wrapLeft: string | undefined; + let wrapRight: string | undefined; + if (isExportRawObject && options.vueCompilerOptions.optionsWrapper.length) { + [wrapLeft, wrapRight] = options.vueCompilerOptions.optionsWrapper; + ctx.inlayHints.push({ + blockName: options.sfc.script.name, + offset: exportDefault.expression.start, + setting: 'vue.inlayHints.optionsWrapper', + label: wrapLeft || '[Missing optionsWrapper[0]]', + tooltip: [ + 'This is virtual code that is automatically wrapped for type support, it does not affect your runtime behavior, you can customize it via `vueCompilerOptions.optionsWrapper` option in tsconfig / jsconfig.', + 'To hide it, you can set `"vue.inlayHints.optionsWrapper": false` in IDE settings.', + ].join('\n\n'), + }, { + blockName: options.sfc.script.name, + offset: exportDefault.expression.end, + setting: 'vue.inlayHints.optionsWrapper', + label: wrapRight || '[Missing optionsWrapper[1]]', + }); + } + + yield generateSfcBlockSection(options.sfc.script, 0, exportDefault.start, codeFeatures.all); + yield* generateConstExport(options.sfc.script); + if (wrapLeft) { + yield wrapLeft; + } yield generateSfcBlockSection( options.sfc.script, exportDefault.expression.start, exportDefault.expression.end, codeFeatures.all, ); - yield options.vueCompilerOptions.optionsWrapper[1]; - yield generateSfcBlockSection( - options.sfc.script, - exportDefault.expression.end, - options.sfc.script.content.length, - codeFeatures.all, - ); + if (wrapRight) { + yield wrapRight; + } } else if (classBlockEnd !== undefined) { if (options.vueCompilerOptions.skipTemplateCodegen) { @@ -129,21 +125,14 @@ export function* generateScript(options: ScriptCodegenOptions): Generator { const globalTypesPath = options.vueCompilerOptions.globalTypesPath(options.fileName); @@ -176,6 +165,45 @@ function* generateGlobalTypesPath( } } +export function* generateConstExport(block: SfcBlock): Generator { + yield `const `; + yield* wrapWith( + 0, + block.content.length, + block.name, + codeFeatures.verification, + `__VLS_export`, + ); + yield ` = `; +} + +function* generateExportDefault(options: ScriptCodegenOptions): Generator { + let prefix: Code; + let suffix: Code; + if (options.sfc.script && options.scriptRanges?.exportDefault) { + const { exportDefault } = options.scriptRanges; + prefix = generateSfcBlockSection( + options.sfc.script, + exportDefault.start, + exportDefault.expression.start, + codeFeatures.all, + ); + suffix = generateSfcBlockSection( + options.sfc.script, + exportDefault.expression.end, + options.sfc.script.content.length, + codeFeatures.all, + ); + } + else { + prefix = `export default `; + suffix = endOfLine; + } + yield prefix; + yield `{} as typeof __VLS_export`; + yield suffix; +} + export function* generateScriptSectionPartiallyEnding( source: string, end: number, @@ -183,6 +211,6 @@ export function* generateScriptSectionPartiallyEnding( delimiter = 'debugger', ): Generator { yield delimiter; - yield ['', source, end, codeFeatures.verification]; + yield [``, source, end, codeFeatures.verification]; yield `/* PartiallyEnd: ${mark} */${newLine}`; } diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 83af4ed7ff..2c4c45803b 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -8,7 +8,7 @@ import { wrapWith } from '../utils/wrapWith'; import { generateComponent, generateEmitsOption } from './component'; import { generateComponentSelf } from './componentSelf'; import type { ScriptCodegenContext } from './context'; -import { generateScriptSectionPartiallyEnding, type ScriptCodegenOptions } from './index'; +import { generateConstExport, generateScriptSectionPartiallyEnding, type ScriptCodegenOptions } from './index'; import { generateTemplate } from './template'; export function* generateScriptSetupImports( @@ -33,11 +33,7 @@ export function* generateScriptSetup( scriptSetupRanges: ScriptSetupRanges, ): Generator { if (scriptSetup.generic) { - if (!options.scriptRanges?.exportDefault) { - // #4569 - yield ['', 'scriptSetup', 0, codeFeatures.verification]; - yield `export default `; - } + yield* generateConstExport(scriptSetup); yield `(`; if (typeof scriptSetup.generic === 'object') { yield `<`; @@ -78,19 +74,17 @@ export function* generateScriptSetup( + ` emit: ${emitTypes.length ? emitTypes.join(' & ') : `{}`},${newLine}` + `}${endOfLine}`; yield `})(),${newLine}`; // __VLS_setup = (async () => { - yield `) => ({} as import('${options.vueCompilerOptions.lib}').VNode & { __ctx?: Awaited }))`; + yield `) => ({} as import('${options.vueCompilerOptions.lib}').VNode & { __ctx?: Awaited }))${endOfLine}`; } else if (!options.sfc.script) { // no script block, generate script setup code at root yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, 'export default'); } else { - if (!options.scriptRanges?.exportDefault) { - yield `export default `; - } + yield* generateConstExport(scriptSetup); yield `await (async () => {${newLine}`; yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, 'return'); - yield `})()`; + yield `})()${endOfLine}`; } } @@ -312,19 +306,22 @@ function* generateSetupFunction( yield* generateComponentSelf(options, ctx, templateCodegenCtx); if (syntax) { + const prefix = syntax === 'return' + ? [`return `] + : generateConstExport(scriptSetup); if ( scriptSetupRanges.defineSlots || options.templateCodegen?.slots.length || options.templateCodegen?.dynamicSlots.length ) { - yield `const __VLS_component = `; + yield `const __VLS_base = `; yield* generateComponent(options, ctx, scriptSetup, scriptSetupRanges); yield endOfLine; - yield `${syntax} `; - yield `{} as ${ctx.localTypes.WithSlots}${endOfLine}`; + yield* prefix; + yield `{} as ${ctx.localTypes.WithSlots}${endOfLine}`; } else { - yield `${syntax} `; + yield* prefix; yield* generateComponent(options, ctx, scriptSetup, scriptSetupRanges); yield endOfLine; } diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index 55e28e1b0e..93daad9919 100644 --- a/packages/language-core/lib/codegen/template/element.ts +++ b/packages/language-core/lib/codegen/template/element.ts @@ -1,7 +1,7 @@ import * as CompilerDOM from '@vue/compiler-dom'; import { camelize, capitalize } from '@vue/shared'; import type { Code, VueCodeInformation } from '../../types'; -import { getSlotsPropertyName, hyphenateTag } from '../../utils/shared'; +import { hyphenateTag } from '../../utils/shared'; import { codeFeatures } from '../codeFeatures'; import { createVBindShorthandInlayHintInfo } from '../inlayHints'; import { endOfLine, identifierRegex, newLine, normalizeAttributeValue } from '../utils'; @@ -138,9 +138,7 @@ export function* generateComponent( getCanonicalComponentName(node.tag) }', __VLS_LocalComponents, `; if (options.selfComponentName && possibleOriginalNames.includes(options.selfComponentName)) { - yield `typeof __VLS_self & (new () => { ` - + getSlotsPropertyName(options.vueCompilerOptions.target) - + `: __VLS_Slots }), `; + yield `typeof __VLS_export, `; } else { yield `void, `; diff --git a/packages/language-core/lib/languagePlugin.ts b/packages/language-core/lib/languagePlugin.ts index 9e9c21882d..fc9d186399 100644 --- a/packages/language-core/lib/languagePlugin.ts +++ b/packages/language-core/lib/languagePlugin.ts @@ -139,16 +139,11 @@ export function createVueLanguagePlugin( } export function getAllExtensions(options: VueCompilerOptions) { - const result = new Set(); - for (const key in options) { - if (key === 'extensions' || key.endsWith('Extensions')) { - const value = options[key as keyof VueCompilerOptions]; - if (Array.isArray(value) && value.every(v => typeof v === 'string')) { - for (const ext of value) { - result.add(ext); - } - } - } - } - return [...result]; + return [ + ...new Set(([ + 'extensions', + 'vitePressExtensions', + 'petiteVueExtensions', + ] as const).flatMap(key => options[key])), + ]; } diff --git a/packages/language-core/lib/parsers/scriptSetupRanges.ts b/packages/language-core/lib/parsers/scriptSetupRanges.ts index 886de3ff79..e137be0cc5 100644 --- a/packages/language-core/lib/parsers/scriptSetupRanges.ts +++ b/packages/language-core/lib/parsers/scriptSetupRanges.ts @@ -3,7 +3,7 @@ import type { TextRange, VueCompilerOptions } from '../types'; import { collectBindingIdentifiers, collectBindingRanges } from '../utils/collectBindings'; import { getNodeText, getStartEnd } from '../utils/shared'; -const tsCheckReg = /^\/\/\s*@ts-(?:no)?check($|\s)/; +const tsCheckReg = /^\/\/\s*@ts-(?:no)?check(?:$|\s)/; type CallExpressionRange = { callExp: TextRange; diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index 6735c55380..b7b6318651 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -161,17 +161,25 @@ function createTsx( }); const getComponentSelfName = computed(() => { + let name: string; const { exportDefault } = getScriptRanges() ?? {}; if (sfc.script && exportDefault?.nameOption) { - const { nameOption } = exportDefault; - return sfc.script.content.slice(nameOption.start + 1, nameOption.end - 1); + name = sfc.script.content.slice( + exportDefault.nameOption.start + 1, + exportDefault.nameOption.end - 1, + ); } - const { defineOptions } = getScriptSetupRanges() ?? {}; - if (sfc.scriptSetup && defineOptions?.name) { - return defineOptions.name; + else { + const { defineOptions } = getScriptSetupRanges() ?? {}; + if (sfc.scriptSetup && defineOptions?.name) { + name = defineOptions.name; + } + else { + const baseName = path.basename(fileName); + name = baseName.slice(0, baseName.lastIndexOf('.')); + } } - const baseName = path.basename(fileName); - return capitalize(camelize(baseName.slice(0, baseName.lastIndexOf('.')))); + return capitalize(camelize(name)); }); const getGeneratedTemplate = computed(() => { @@ -215,7 +223,7 @@ function createTsx( ts, compilerOptions: ctx.compilerOptions, vueCompilerOptions: getResolvedOptions(), - sfc: sfc, + sfc, fileName, lang: getLang(), scriptRanges: getScriptRanges(), diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index 26ac39b893..d0e955f3f7 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -4,7 +4,7 @@ exports[`vue-tsc-dts > Input: #4577/main.vue, Output: #4577/main.vue.d.ts 1`] = "export type BaseRow = { value: string; }; -declare const _default: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ +declare const __VLS_export: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ props: __VLS_PrettifyLocal & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & { nonGeneric: string; rows: Row[]; @@ -20,6 +20,7 @@ declare const _default: (__VLS_props: NonNullable) => import("vue").VNode & { __ctx?: Awaited; }; +declare const _default: typeof __VLS_export; export default _default; type __VLS_PrettifyLocal = { [K in keyof T as K]: T[K]; @@ -28,7 +29,7 @@ type __VLS_PrettifyLocal = { `; exports[`vue-tsc-dts > Input: empty-component/component.vue, Output: empty-component/component.vue.d.ts 1`] = ` -"declare const _default: import("vue").DefineComponent2<{ +"declare const __VLS_export: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -49,12 +50,13 @@ exports[`vue-tsc-dts > Input: empty-component/component.vue, Output: empty-compo __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; exports[`vue-tsc-dts > Input: empty-component/custom-extension-component.cext, Output: empty-component/custom-extension-component.cext.d.ts 1`] = ` -"declare const _default: import("vue").DefineComponent2<{ +"declare const __VLS_export: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -75,12 +77,13 @@ exports[`vue-tsc-dts > Input: empty-component/custom-extension-component.cext, O __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.vue.d.ts 1`] = ` -"declare const _default: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ +"declare const __VLS_export: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ props: __VLS_PrettifyLocal & Omit<{ readonly "onUpdate:title"?: (value: string) => any; readonly onBar?: (data: number) => any; @@ -102,6 +105,7 @@ exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.v }>) => import("vue").VNode & { __ctx?: Awaited; }; +declare const _default: typeof __VLS_export; export default _default; type __VLS_PrettifyLocal = { [K in keyof T as K]: T[K]; @@ -110,7 +114,7 @@ type __VLS_PrettifyLocal = { `; exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: generic/custom-extension-component.cext.d.ts 1`] = ` -"declare const _default: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ +"declare const __VLS_export: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ props: __VLS_PrettifyLocal & Omit<{ readonly "onUpdate:title"?: (value: string) => any; readonly onBar?: (data: number) => any; @@ -132,6 +136,7 @@ exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: g }>) => import("vue").VNode & { __ctx?: Awaited; }; +declare const _default: typeof __VLS_export; export default _default; type __VLS_PrettifyLocal = { [K in keyof T as K]: T[K]; @@ -140,7 +145,7 @@ type __VLS_PrettifyLocal = { `; exports[`vue-tsc-dts > Input: generic/main.vue, Output: generic/main.vue.d.ts 1`] = ` -"declare const _default: import("vue").DefineComponent2<{ +"declare const __VLS_export: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -161,6 +166,7 @@ exports[`vue-tsc-dts > Input: generic/main.vue, Output: generic/main.vue.d.ts 1` __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; @@ -242,7 +248,7 @@ export default _default; exports[`vue-tsc-dts > Input: reference-type-events/component.vue, Output: reference-type-events/component.vue.d.ts 1`] = ` "import type { MyEvents } from './my-events'; -declare const _default: import("vue").DefineComponent2<{ +declare const __VLS_export: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -263,6 +269,7 @@ declare const _default: import("vue").DefineComponent2<{ __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; @@ -287,7 +294,7 @@ export {}; `; exports[`vue-tsc-dts > Input: reference-type-exposed/component.vue, Output: reference-type-exposed/component.vue.d.ts 1`] = ` -"declare const _default: import("vue").DefineComponent2<{ +"declare const __VLS_export: import("vue").DefineComponent2<{ setup(): { /** * a counter string @@ -313,6 +320,7 @@ exports[`vue-tsc-dts > Input: reference-type-exposed/component.vue, Output: refe __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; @@ -338,7 +346,7 @@ type __VLS_ModelEmit = { 'update:foo': [value: boolean]; 'update:bar': [value: string | undefined]; }; -declare const _default: import("vue").DefineComponent2<{ +declare const __VLS_export: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -359,13 +367,14 @@ declare const _default: import("vue").DefineComponent2<{ __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; exports[`vue-tsc-dts > Input: reference-type-props/component.vue, Output: reference-type-props/component.vue.d.ts 1`] = ` "import { MyProps } from './my-props'; -declare const _default: import("vue").DefineComponent2<{ +declare const __VLS_export: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -389,6 +398,7 @@ declare const _default: import("vue").DefineComponent2<{ baz: () => string[]; }; }>; +declare const _default: typeof __VLS_export; export default _default; " `; @@ -397,7 +407,7 @@ exports[`vue-tsc-dts > Input: reference-type-props/component-destructure.vue, Ou "type __VLS_Props = { text: string; }; -declare const _default: import("vue").DefineComponent2<{ +declare const __VLS_export: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -418,12 +428,13 @@ declare const _default: import("vue").DefineComponent2<{ __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; exports[`vue-tsc-dts > Input: reference-type-props/component-js.vue, Output: reference-type-props/component-js.vue.d.ts 1`] = ` -"declare const _default: import("vue").DefineComponent2<{ +"declare const __VLS_export: import("vue").DefineComponent2<{ setup(): {}; data(): {}; props: { @@ -467,12 +478,13 @@ exports[`vue-tsc-dts > Input: reference-type-props/component-js.vue, Output: ref __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; exports[`vue-tsc-dts > Input: reference-type-props/component-js-setup.vue, Output: reference-type-props/component-js-setup.vue.d.ts 1`] = ` -"declare const _default: import("vue").DefineComponent2<{ +"declare const __VLS_export: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: { @@ -533,6 +545,7 @@ exports[`vue-tsc-dts > Input: reference-type-props/component-js-setup.vue, Outpu __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; @@ -666,7 +679,7 @@ type __VLS_Slots = {} & { } & { vbind?: (props: typeof __VLS_7) => any; }; -declare const __VLS_component: import("vue").DefineComponent2<{ +declare const __VLS_base: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -687,7 +700,8 @@ declare const __VLS_component: import("vue").DefineComponent2<{ __typeEl: any; __defaults: unknown; }>; -declare const _default: __VLS_WithSlots; +declare const __VLS_export: __VLS_WithSlots; +declare const _default: typeof __VLS_export; export default _default; type __VLS_WithSlots = T & { new (): { @@ -712,7 +726,7 @@ type __VLS_Slots = { }) => VNode[]; 'no-bind': () => VNode[]; }; -declare const __VLS_component: import("vue").DefineComponent2<{ +declare const __VLS_base: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -733,7 +747,8 @@ declare const __VLS_component: import("vue").DefineComponent2<{ __typeEl: any; __defaults: unknown; }>; -declare const _default: __VLS_WithSlots; +declare const __VLS_export: __VLS_WithSlots; +declare const _default: typeof __VLS_export; export default _default; type __VLS_WithSlots = T & { new (): { @@ -761,7 +776,7 @@ type __VLS_Slots = {} & { } & { vbind?: (props: typeof __VLS_7) => any; }; -declare const __VLS_component: import("vue").DefineComponent2<{ +declare const __VLS_base: import("vue").DefineComponent2<{ setup(): void; data(): {}; props: {}; @@ -782,7 +797,8 @@ declare const __VLS_component: import("vue").DefineComponent2<{ __typeEl: any; __defaults: unknown; }>; -declare const _default: __VLS_WithSlots; +declare const __VLS_export: __VLS_WithSlots; +declare const _default: typeof __VLS_export; export default _default; type __VLS_WithSlots = T & { new (): { diff --git a/packages/tsc/tests/typecheck.spec.ts b/packages/tsc/tests/typecheck.spec.ts index aea249b044..46fed129d6 100644 --- a/packages/tsc/tests/typecheck.spec.ts +++ b/packages/tsc/tests/typecheck.spec.ts @@ -12,7 +12,6 @@ describe(`vue-tsc`, () => { "test-workspace/tsc/failureFixtures/#3632/both.vue(7,1): error TS1109: Expression expected.", "test-workspace/tsc/failureFixtures/#3632/script.vue(3,1): error TS1109: Expression expected.", "test-workspace/tsc/failureFixtures/#3632/scriptSetup.vue(3,1): error TS1109: Expression expected.", - "test-workspace/tsc/failureFixtures/#5071/withScript.vue(1,19): error TS1005: ';' expected.", "test-workspace/tsc/failureFixtures/#5071/withoutScript.vue(2,26): error TS1005: ';' expected.", "test-workspace/tsc/failureFixtures/directives/main.vue(12,2): error TS2578: Unused '@ts-expect-error' directive.", "test-workspace/tsc/failureFixtures/directives/main.vue(4,6): error TS2339: Property 'notExist' does not exist on type 'CreateComponentPublicInstanceWithMixins, { exist: {}; }, {}, {}, {}, {}, {}, {}, PublicProps, {}, true, {}, {}, GlobalComponents, GlobalDirectives, ... 12 more ..., {}>'.", diff --git a/packages/typescript-plugin/lib/requests/getComponentEvents.ts b/packages/typescript-plugin/lib/requests/getComponentEvents.ts index ce602429f0..c924c31aa0 100644 --- a/packages/typescript-plugin/lib/requests/getComponentEvents.ts +++ b/packages/typescript-plugin/lib/requests/getComponentEvents.ts @@ -20,7 +20,7 @@ export function getComponentEvents( return []; } - const componentType = getComponentType(ts, languageService, vueCode, components, fileName, tag); + const componentType = getComponentType(ts, languageService, vueCode, components, tag); if (!componentType) { return []; } diff --git a/packages/typescript-plugin/lib/requests/getComponentNames.ts b/packages/typescript-plugin/lib/requests/getComponentNames.ts index 37e80b2de3..2a6db75d66 100644 --- a/packages/typescript-plugin/lib/requests/getComponentNames.ts +++ b/packages/typescript-plugin/lib/requests/getComponentNames.ts @@ -1,5 +1,4 @@ import { VueVirtualCode } from '@vue/language-core'; -import type * as ts from 'typescript'; import type { RequestContext } from './types'; import { getSelfComponentName, getVariableType } from './utils'; @@ -13,15 +12,7 @@ export function getComponentNames( return; } const vueCode = volarFile.generated.root; - return _getComponentNames(ts, languageService, vueCode); -} - -export function _getComponentNames( - ts: typeof import('typescript'), - tsLs: ts.LanguageService, - vueCode: VueVirtualCode, -) { - const names = getVariableType(ts, tsLs, vueCode, '__VLS_components') + const names = getVariableType(ts, languageService, vueCode, '__VLS_components') ?.type ?.getProperties() .map(c => c.name) diff --git a/packages/typescript-plugin/lib/requests/getComponentProps.ts b/packages/typescript-plugin/lib/requests/getComponentProps.ts index 56123ea006..a2c311882a 100644 --- a/packages/typescript-plugin/lib/requests/getComponentProps.ts +++ b/packages/typescript-plugin/lib/requests/getComponentProps.ts @@ -28,7 +28,7 @@ export function getComponentProps( return []; } - const componentType = getComponentType(ts, languageService, vueCode, components, fileName, tag); + const componentType = getComponentType(ts, languageService, vueCode, components, tag); if (!componentType) { return []; } diff --git a/packages/typescript-plugin/lib/requests/utils.ts b/packages/typescript-plugin/lib/requests/utils.ts index bdabe5a0c4..4a486182af 100644 --- a/packages/typescript-plugin/lib/requests/utils.ts +++ b/packages/typescript-plugin/lib/requests/utils.ts @@ -8,7 +8,6 @@ export function getComponentType( languageService: ts.LanguageService, vueCode: VueVirtualCode, components: NonNullable>, - fileName: string, tag: string, ) { const program = languageService.getProgram()!; @@ -21,9 +20,9 @@ export function getComponentType( let componentType: ts.Type | undefined; if (!componentSymbol) { - const name = getSelfComponentName(fileName); + const name = getSelfComponentName(vueCode.fileName); if (name === capitalize(camelize(tag))) { - componentType = getVariableType(ts, languageService, vueCode, '__VLS_self')?.type; + componentType = getVariableType(ts, languageService, vueCode, '__VLS_export')?.type; } } else { From 72f9dccdce8e1448ddeaa772fc55f9fd3bd364e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Sat, 19 Jul 2025 03:07:40 +0800 Subject: [PATCH 02/32] perf: drop internal component --- .../lib/codegen/script/componentSelf.ts | 72 -------------- .../language-core/lib/codegen/script/index.ts | 8 +- .../lib/codegen/script/scriptSetup.ts | 18 +++- .../lib/codegen/script/template.ts | 97 ++++++++++++++++--- .../lib/codegen/template/index.ts | 2 +- .../lib/parsers/scriptSetupRanges.ts | 4 +- packages/language-core/lib/plugins/vue-tsx.ts | 6 +- packages/tsc/tests/typecheck.spec.ts | 4 +- .../tsc/passedFixtures/vue3/#3779/main.vue | 2 +- .../tsc/passedFixtures/vue3/#3779/named.vue | 2 +- .../passedFixtures/vue3/withDefaults/main.vue | 2 +- 11 files changed, 115 insertions(+), 102 deletions(-) delete mode 100644 packages/language-core/lib/codegen/script/componentSelf.ts diff --git a/packages/language-core/lib/codegen/script/componentSelf.ts b/packages/language-core/lib/codegen/script/componentSelf.ts deleted file mode 100644 index 08c22cc454..0000000000 --- a/packages/language-core/lib/codegen/script/componentSelf.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as path from 'path-browserify'; -import type { Code } from '../../types'; -import { codeFeatures } from '../codeFeatures'; -import type { TemplateCodegenContext } from '../template/context'; -import { endOfLine, generateSfcBlockSection, newLine } from '../utils'; -import { generateComponentSetupReturns, generateEmitsOption, generatePropsOption } from './component'; -import type { ScriptCodegenContext } from './context'; -import type { ScriptCodegenOptions } from './index'; -import { getTemplateUsageVars } from './template'; - -export function* generateComponentSelf( - options: ScriptCodegenOptions, - ctx: ScriptCodegenContext, - templateCodegenCtx: TemplateCodegenContext, -): Generator { - if (options.sfc.scriptSetup && options.scriptSetupRanges) { - yield `const __VLS_self = (await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; - yield `setup() {${newLine}`; - yield `return {${newLine}`; - if (ctx.bypassDefineComponent) { - yield* generateComponentSetupReturns(options.scriptSetupRanges); - } - // bindings - const templateUsageVars = getTemplateUsageVars(options, ctx); - for ( - const [content, bindings] of [ - [options.sfc.scriptSetup.content, options.scriptSetupRanges.bindings] as const, - options.sfc.script && options.scriptRanges - ? [options.sfc.script.content, options.scriptRanges.bindings] as const - : ['', []] as const, - ] - ) { - for (const { range } of bindings) { - const varName = content.slice(range.start, range.end); - if (!templateUsageVars.has(varName) && !templateCodegenCtx.accessExternalVariables.has(varName)) { - continue; - } - - const token = Symbol(varName.length); - yield ['', undefined, 0, { __linkedToken: token }]; - yield `${varName}: ${varName} as typeof `; - yield ['', undefined, 0, { __linkedToken: token }]; - yield `${varName},${newLine}`; - } - } - yield `}${endOfLine}`; // return { - yield `},${newLine}`; // setup() { - if (options.sfc.scriptSetup && options.scriptSetupRanges && !ctx.bypassDefineComponent) { - const emitOptionCodes = [...generateEmitsOption(options, options.scriptSetupRanges)]; - yield* emitOptionCodes; - yield* generatePropsOption( - options, - ctx, - options.sfc.scriptSetup, - options.scriptSetupRanges, - !!emitOptionCodes.length, - false, - ); - } - if (options.sfc.script && options.scriptRanges?.exportDefault?.args) { - const { args } = options.scriptRanges.exportDefault; - yield generateSfcBlockSection(options.sfc.script, args.start + 1, args.end - 1, codeFeatures.all); - } - yield `})${endOfLine}`; // defineComponent { - } - else if (options.sfc.script) { - yield `let __VLS_self!: typeof import('./${path.basename(options.fileName)}').default${endOfLine}`; - } - else { - yield `const __VLS_self = (await import('${options.vueCompilerOptions.lib}')).defineComponent({})${endOfLine}`; - } -} diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index c4b66af210..5311fc12c0 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -7,7 +7,6 @@ import { codeFeatures } from '../codeFeatures'; import type { TemplateCodegenContext } from '../template/context'; import { endOfLine, generateSfcBlockSection, newLine } from '../utils'; import { wrapWith } from '../utils/wrapWith'; -import { generateComponentSelf } from './componentSelf'; import { createScriptCodegenContext, type ScriptCodegenContext } from './context'; import { generateScriptSetup, generateScriptSetupImports } from './scriptSetup'; import { generateSrc } from './src'; @@ -93,6 +92,7 @@ export function* generateScript(options: ScriptCodegenOptions): Generator {${newLine}`; - const templateCodegenCtx = yield* generateTemplate(options, ctx); - yield* generateComponentSelf(options, ctx, templateCodegenCtx); + yield* generateTemplate(options, ctx); yield `}${endOfLine}`; yield generateSfcBlockSection( options.sfc.script, @@ -126,8 +125,7 @@ export function* generateScript(options: ScriptCodegenOptions): Generator { +): Generator { ctx.generatedTemplate = true; const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), }); - yield* generateTemplateCtx(options); + yield* generateBindings(options, ctx); + yield* generateTemplateCtx(options, ctx); yield* generateTemplateElements(); yield* generateTemplateComponents(options); yield* generateTemplateDirectives(options); yield* generateTemplateBody(options, templateCodegenCtx); - return templateCodegenCtx; + + if (options.sfc.script && options.scriptRanges?.exportDefault) { + yield `const __VLS_self = (await import('${options.vueCompilerOptions.lib}')).defineComponent(`; + const { args } = options.scriptRanges.exportDefault; + yield generateSfcBlockSection(options.sfc.script, args.start, args.end, codeFeatures.all); + yield `)${endOfLine}`; + } } -function* generateTemplateCtx(options: ScriptCodegenOptions): Generator { - const exps = []; +function* generateTemplateCtx( + options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, +): Generator { + const exps: Code[] = []; - exps.push(`{} as InstanceType<__VLS_PickNotAny {}>>`); + if (options.sfc.script && options.scriptRanges?.exportDefault) { + exps.push(`{} as InstanceType<__VLS_PickNotAny {}>>`); + } + else { + exps.push(`{} as import('${options.vueCompilerOptions.lib}').ComponentPublicInstance`); + } if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileName.endsWith(ext))) { exps.push(`globalThis`); @@ -40,6 +55,34 @@ function* generateTemplateCtx(options: ScriptCodegenOptions): Generator { exps.push(`{} as __VLS_StyleModules`); } + if (ctx.generatedPropsType || options.scriptSetupRanges?.defineProps) { + yield `type __VLS_InternalProps = `; + const { defineProps } = options.scriptSetupRanges ?? {}; + if (defineProps) { + yield `__VLS_SpreadMerge<__VLS_PublicProps, typeof ${defineProps.name ?? `__VLS_props`}>`; + } + else { + yield `__VLS_PublicProps`; + } + yield endOfLine; + exps.push(`{} as __VLS_InternalProps`); + exps.push(`{} as { $props: __VLS_InternalProps }`); + } + + const emitTypes: Code[] = []; + if (options.scriptSetupRanges?.defineEmits) { + emitTypes.push(`typeof ${options.scriptSetupRanges.defineEmits.name ?? `__VLS_emit`}`); + } + if (options.scriptSetupRanges?.defineModel.length) { + emitTypes.push(`typeof __VLS_modelEmit`); + } + if (emitTypes.length) { + yield `type __VLS_ResolvedEmit = ${emitTypes.join(' & ')}${endOfLine}`; + exps.push(`{} as { $emit: __VLS_ResolvedEmit }`); + } + + exps.push(`{} as import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef`); + yield `const __VLS_ctx = `; yield* generateSpreadMerge(exps); yield endOfLine; @@ -65,14 +108,14 @@ function* generateTemplateComponents(options: ScriptCodegenOptions): Generator { +function* generateTemplateDirectives(options: ScriptCodegenOptions): Generator { const types: Code[] = [`typeof __VLS_ctx`]; if (options.sfc.script && options.scriptRanges?.exportDefault?.directivesOption) { @@ -88,7 +131,7 @@ export function* generateTemplateDirectives(options: ScriptCodegenOptions): Gene types.push(`__VLS_ResolveDirectives`); } - yield `type __VLS_LocalDirectives =`; + yield `type __VLS_LocalDirectives = `; yield* generateIntersectMerge(types); yield endOfLine; @@ -138,7 +181,39 @@ function* generateCssVars(options: ScriptCodegenOptions, ctx: TemplateCodegenCon yield `// CSS variable injection end ${newLine}`; } -export function getTemplateUsageVars(options: ScriptCodegenOptions, ctx: ScriptCodegenContext) { +function* generateBindings( + options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, +): Generator { + yield `const __VLS_bindings = {${newLine}`; + if (options.sfc.scriptSetup && options.scriptSetupRanges) { + const templateUsageVars = getTemplateUsageVars(options, ctx); + for ( + const [content, bindings] of [ + [options.sfc.scriptSetup.content, options.scriptSetupRanges.bindings] as const, + options.sfc.script && options.scriptRanges + ? [options.sfc.script.content, options.scriptRanges.bindings] as const + : ['', []] as const, + ] + ) { + for (const { range } of bindings) { + const varName = content.slice(range.start, range.end); + if (!templateUsageVars.has(varName)) { + continue; + } + + const token = Symbol(varName.length); + yield ['', undefined, 0, { __linkedToken: token }]; + yield `${varName}: ${varName} as typeof `; + yield ['', undefined, 0, { __linkedToken: token }]; + yield `${varName},${newLine}`; + } + } + } + yield `}${endOfLine}`; +} + +function getTemplateUsageVars(options: ScriptCodegenOptions, ctx: ScriptCodegenContext) { const usageVars = new Set(); const components = new Set(options.sfc.template?.ast?.components); diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index c545c258e5..2d09d575cd 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -19,8 +19,8 @@ export interface TemplateCodegenOptions { destructuredPropNames: Set; templateRefNames: Set; hasDefineSlots?: boolean; - slotsAssignName?: string; propsAssignName?: string; + slotsAssignName?: string; inheritAttrs: boolean; selfComponentName?: string; } diff --git a/packages/language-core/lib/parsers/scriptSetupRanges.ts b/packages/language-core/lib/parsers/scriptSetupRanges.ts index e137be0cc5..2f596d720d 100644 --- a/packages/language-core/lib/parsers/scriptSetupRanges.ts +++ b/packages/language-core/lib/parsers/scriptSetupRanges.ts @@ -349,7 +349,9 @@ export function parseScriptSetupRanges( function parseCallExpressionAssignment(node: ts.CallExpression, parent: ts.Node) { return { - name: ts.isVariableDeclaration(parent) ? _getNodeText(parent.name) : undefined, + name: ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name) + ? _getNodeText(parent.name) + : undefined, ...parseCallExpression(node), }; } diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index b7b6318651..e36b207aad 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -150,10 +150,10 @@ function createTsx( const setupHasDefineSlots = computed(() => !!getScriptSetupRanges()?.defineSlots); - const getSetupSlotsAssignName = computed(() => getScriptSetupRanges()?.defineSlots?.name); - const getSetupPropsAssignName = computed(() => getScriptSetupRanges()?.defineProps?.name); + const getSetupSlotsAssignName = computed(() => getScriptSetupRanges()?.defineSlots?.name); + const getSetupInheritAttrs = computed(() => { const value = getScriptSetupRanges()?.defineOptions?.inheritAttrs ?? getScriptRanges()?.exportDefault?.inheritAttrsOption; @@ -198,8 +198,8 @@ function createTsx( destructuredPropNames: getSetupDestructuredPropNames(), templateRefNames: getSetupTemplateRefNames(), hasDefineSlots: setupHasDefineSlots(), - slotsAssignName: getSetupSlotsAssignName(), propsAssignName: getSetupPropsAssignName(), + slotsAssignName: getSetupSlotsAssignName(), inheritAttrs: getSetupInheritAttrs(), selfComponentName: getComponentSelfName(), }); diff --git a/packages/tsc/tests/typecheck.spec.ts b/packages/tsc/tests/typecheck.spec.ts index 46fed129d6..711c248e1e 100644 --- a/packages/tsc/tests/typecheck.spec.ts +++ b/packages/tsc/tests/typecheck.spec.ts @@ -14,8 +14,8 @@ describe(`vue-tsc`, () => { "test-workspace/tsc/failureFixtures/#3632/scriptSetup.vue(3,1): error TS1109: Expression expected.", "test-workspace/tsc/failureFixtures/#5071/withoutScript.vue(2,26): error TS1005: ';' expected.", "test-workspace/tsc/failureFixtures/directives/main.vue(12,2): error TS2578: Unused '@ts-expect-error' directive.", - "test-workspace/tsc/failureFixtures/directives/main.vue(4,6): error TS2339: Property 'notExist' does not exist on type 'CreateComponentPublicInstanceWithMixins, { exist: {}; }, {}, {}, {}, {}, {}, {}, PublicProps, {}, true, {}, {}, GlobalComponents, GlobalDirectives, ... 12 more ..., {}>'.", - "test-workspace/tsc/failureFixtures/directives/main.vue(9,6): error TS2339: Property 'notExist' does not exist on type 'CreateComponentPublicInstanceWithMixins, { exist: {}; }, {}, {}, {}, {}, {}, {}, PublicProps, {}, true, {}, {}, GlobalComponents, GlobalDirectives, ... 12 more ..., {}>'.", + "test-workspace/tsc/failureFixtures/directives/main.vue(4,6): error TS2339: Property 'notExist' does not exist on type '{ exist: {}; $: ComponentInternalInstance; $data: {}; $props: {}; $attrs: Data; $refs: Data; $slots: Readonly; ... 8 more ...; $watch any)>(source: T, cb: T extends (...args: any) => infer R ? (args_0: R, args_1: R, args_2: OnCleanup) => any : (args_0: any, args_1...'.", + "test-workspace/tsc/failureFixtures/directives/main.vue(9,6): error TS2339: Property 'notExist' does not exist on type '{ exist: {}; $: ComponentInternalInstance; $data: {}; $props: {}; $attrs: Data; $refs: Data; $slots: Readonly; ... 8 more ...; $watch any)>(source: T, cb: T extends (...args: any) => infer R ? (args_0: R, args_1: R, args_2: OnCleanup) => any : (args_0: any, args_1...'.", ] `); }); diff --git a/test-workspace/tsc/passedFixtures/vue3/#3779/main.vue b/test-workspace/tsc/passedFixtures/vue3/#3779/main.vue index 5fcef0b240..dca94c63ae 100644 --- a/test-workspace/tsc/passedFixtures/vue3/#3779/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3/#3779/main.vue @@ -6,5 +6,5 @@ defineProps<{ diff --git a/test-workspace/tsc/passedFixtures/vue3/#3779/named.vue b/test-workspace/tsc/passedFixtures/vue3/#3779/named.vue index 5fcef0b240..dca94c63ae 100644 --- a/test-workspace/tsc/passedFixtures/vue3/#3779/named.vue +++ b/test-workspace/tsc/passedFixtures/vue3/#3779/named.vue @@ -6,5 +6,5 @@ defineProps<{ diff --git a/test-workspace/tsc/passedFixtures/vue3/withDefaults/main.vue b/test-workspace/tsc/passedFixtures/vue3/withDefaults/main.vue index a46cf5f520..9cf95ec07e 100644 --- a/test-workspace/tsc/passedFixtures/vue3/withDefaults/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3/withDefaults/main.vue @@ -13,6 +13,6 @@ withDefaults(defineProps(), { From c0b0b77a889feb444e93dcd8f1510d38f4bc19ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Sat, 19 Jul 2025 03:14:27 +0800 Subject: [PATCH 03/32] test: update snapshot --- .../language-server/tests/renaming.spec.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/language-server/tests/renaming.spec.ts b/packages/language-server/tests/renaming.spec.ts index 059022a9ce..720453d0a1 100644 --- a/packages/language-server/tests/renaming.spec.ts +++ b/packages/language-server/tests/renaming.spec.ts @@ -693,16 +693,6 @@ test('Component returns', async () => { { "file": "\${testWorkspacePath}/tsconfigProject/fixture.vue", "locs": [ - { - "end": { - "line": 3, - "offset": 11, - }, - "start": { - "line": 3, - "offset": 8, - }, - }, { "contextEnd": { "line": 12, @@ -721,6 +711,16 @@ test('Component returns', async () => { "offset": 7, }, }, + { + "end": { + "line": 3, + "offset": 11, + }, + "start": { + "line": 3, + "offset": 8, + }, + }, ], }, ], From 2c87740bc1a6efb3c29597d28405577f2a5c9215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Sat, 19 Jul 2025 03:36:47 +0800 Subject: [PATCH 04/32] refactor: extract `__VLS_ProxyRefs` --- packages/language-core/lib/codegen/globalTypes.ts | 1 + packages/language-core/lib/codegen/script/template.ts | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts index 5851bb1899..ff2a299f75 100644 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -133,6 +133,7 @@ export function generateGlobalTypes(options: VueCompilerOptions) { }; type __VLS_PrettifyGlobal = { [K in keyof T as K]: T[K]; } & {}; type __VLS_UseTemplateRef = Readonly>; + type __VLS_ProxyRefs = import('${lib}').ShallowUnwrapRef; function __VLS_getVForSourceType>(source: T): [ item: T extends number ? number diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index b39e223f47..151448e89d 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -77,11 +77,10 @@ function* generateTemplateCtx( emitTypes.push(`typeof __VLS_modelEmit`); } if (emitTypes.length) { - yield `type __VLS_ResolvedEmit = ${emitTypes.join(' & ')}${endOfLine}`; - exps.push(`{} as { $emit: __VLS_ResolvedEmit }`); + exps.push(`{} as { $emit: ${emitTypes.join(' & ')} }`); } - exps.push(`{} as import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef`); + exps.push(`{} as __VLS_Bindings`); yield `const __VLS_ctx = `; yield* generateSpreadMerge(exps); @@ -185,7 +184,7 @@ function* generateBindings( options: ScriptCodegenOptions, ctx: ScriptCodegenContext, ): Generator { - yield `const __VLS_bindings = {${newLine}`; + yield `type __VLS_Bindings = __VLS_ProxyRefs<{${newLine}`; if (options.sfc.scriptSetup && options.scriptSetupRanges) { const templateUsageVars = getTemplateUsageVars(options, ctx); for ( @@ -204,13 +203,13 @@ function* generateBindings( const token = Symbol(varName.length); yield ['', undefined, 0, { __linkedToken: token }]; - yield `${varName}: ${varName} as typeof `; + yield `${varName}: typeof `; yield ['', undefined, 0, { __linkedToken: token }]; yield `${varName},${newLine}`; } } } - yield `}${endOfLine}`; + yield `}>${endOfLine}`; } function getTemplateUsageVars(options: ScriptCodegenOptions, ctx: ScriptCodegenContext) { From 0a3d3274b1bb478f4299e149aea2421275a7cecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Sat, 19 Jul 2025 03:49:36 +0800 Subject: [PATCH 05/32] refactor: remove `__VLS_makeOptional` --- .../language-core/lib/codegen/globalTypes.ts | 1 - .../lib/codegen/script/component.ts | 27 +++++++------------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts index ff2a299f75..5fed8dea26 100644 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -155,7 +155,6 @@ export function generateGlobalTypes(options: VueCompilerOptions) { : T extends (...args: any) => any ? T : (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void; - function __VLS_makeOptional(t: T): { [K in keyof T]?: T[K] }; function __VLS_asFunctionalComponent any ? InstanceType : unknown>(t: T, instance?: K): T extends new (...args: any) => any ? __VLS_FunctionalComponent : T extends () => any ? (props: {}, ctx?: any) => ReturnType${ diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index f891b4ae8a..16518dc31d 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -32,10 +32,16 @@ export function* generateComponent( yield `setup() {${newLine}`; const returns: Code[] = []; if (ctx.bypassDefineComponent) { - yield* `const __VLS_returns = {${newLine}`; - yield* generateComponentSetupReturns(scriptSetupRanges); - yield `}${endOfLine}`; - returns.push(`typeof __VLS_returns`); + // fill $props + if (scriptSetupRanges.defineProps) { + const name = scriptSetupRanges.defineProps.name ?? `__VLS_props`; + // NOTE: defineProps is inaccurate for $props + returns.push(`typeof ${name} & { $props: Partial }`); + } + // fill $emit + if (scriptSetupRanges.defineEmits) { + returns.push(`{ $emit: typeof ${scriptSetupRanges.defineEmits.name ?? '__VLS_emit'} }`); + } } if (scriptSetupRanges.defineExpose) { returns.push(`typeof __VLS_exposed`); @@ -73,19 +79,6 @@ export function* generateComponent( yield `})`; } -export function* generateComponentSetupReturns(scriptSetupRanges: ScriptSetupRanges): Generator { - // fill $props - if (scriptSetupRanges.defineProps) { - // NOTE: defineProps is inaccurate for $props - yield `$props: __VLS_makeOptional(${scriptSetupRanges.defineProps.name ?? `__VLS_props`}),${newLine}`; - yield `...${scriptSetupRanges.defineProps.name ?? `__VLS_props`},${newLine}`; - } - // fill $emit - if (scriptSetupRanges.defineEmits) { - yield `$emit: ${scriptSetupRanges.defineEmits.name ?? '__VLS_emit'},${newLine}`; - } -} - export function* generateEmitsOption( options: ScriptCodegenOptions, scriptSetupRanges: ScriptSetupRanges, From 18a2ddbad2d60b78df7e7a807c8c6696f0cbc0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Sat, 19 Jul 2025 03:56:54 +0800 Subject: [PATCH 06/32] fix: generate bindings after template variable collection --- packages/language-core/lib/codegen/script/template.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 151448e89d..8ffeb1d2a7 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -20,12 +20,12 @@ export function* generateTemplate( const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), }); - yield* generateBindings(options, ctx); yield* generateTemplateCtx(options, ctx); yield* generateTemplateElements(); yield* generateTemplateComponents(options); yield* generateTemplateDirectives(options); yield* generateTemplateBody(options, templateCodegenCtx); + yield* generateBindings(options, ctx, templateCodegenCtx); if (options.sfc.script && options.scriptRanges?.exportDefault) { yield `const __VLS_self = (await import('${options.vueCompilerOptions.lib}')).defineComponent(`; @@ -183,6 +183,7 @@ function* generateCssVars(options: ScriptCodegenOptions, ctx: TemplateCodegenCon function* generateBindings( options: ScriptCodegenOptions, ctx: ScriptCodegenContext, + templateCodegenCtx: TemplateCodegenContext, ): Generator { yield `type __VLS_Bindings = __VLS_ProxyRefs<{${newLine}`; if (options.sfc.scriptSetup && options.scriptSetupRanges) { @@ -197,7 +198,7 @@ function* generateBindings( ) { for (const { range } of bindings) { const varName = content.slice(range.start, range.end); - if (!templateUsageVars.has(varName)) { + if (!templateUsageVars.has(varName) && !templateCodegenCtx.accessExternalVariables.has(varName)) { continue; } From fb1f1a30098cef0e43e19f9860a6089dfb40d7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Sat, 19 Jul 2025 20:16:59 +0800 Subject: [PATCH 07/32] feat: emits to props and drop internal component for generic --- .../language-core/lib/codegen/globalTypes.ts | 4 ++ .../language-core/lib/codegen/localTypes.ts | 13 ----- .../lib/codegen/script/scriptSetup.ts | 50 ++++++------------- .../lib/codegen/script/template.ts | 32 ++++++------ .../tsc/tests/__snapshots__/dts.spec.ts.snap | 18 +++---- .../passedFixtures/vue3/components/main.vue | 2 +- 6 files changed, 46 insertions(+), 73 deletions(-) diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts index 5fed8dea26..6bf17fcb28 100644 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -118,6 +118,10 @@ export function generateGlobalTypes(options: VueCompilerOptions) { } > >; + type __VLS_EmitsToProps = __VLS_PrettifyGlobal<{ + [K in string & keyof T as \`on\${Capitalize}\`]?: + (...args: T[K] extends (...args: infer P) => any ? P : T[K] extends null ? any[] : never) => any; + }>; type __VLS_ResolveEmits< Comp, Emits, diff --git a/packages/language-core/lib/codegen/localTypes.ts b/packages/language-core/lib/codegen/localTypes.ts index 5f04f0f0fa..8e36bdfc93 100644 --- a/packages/language-core/lib/codegen/localTypes.ts +++ b/packages/language-core/lib/codegen/localTypes.ts @@ -5,15 +5,6 @@ import { endOfLine } from './utils'; export function getLocalTypesGenerator(vueCompilerOptions: VueCompilerOptions) { const used = new Set(); - const OmitKeepDiscriminatedUnion = defineHelper( - `__VLS_OmitKeepDiscriminatedUnion`, - () => - ` -type __VLS_OmitKeepDiscriminatedUnion = T extends any - ? Pick> - : never; -`.trimStart(), - ); const WithDefaults = defineHelper( `__VLS_WithDefaults`, () => @@ -78,7 +69,6 @@ type __VLS_TypePropsToOption = { ); const helpers = { [PrettifyLocal.name]: PrettifyLocal, - [OmitKeepDiscriminatedUnion.name]: OmitKeepDiscriminatedUnion, [WithDefaults.name]: WithDefaults, [WithSlots.name]: WithSlots, [PropsChildren.name]: PropsChildren, @@ -95,9 +85,6 @@ type __VLS_TypePropsToOption = { get PrettifyLocal() { return PrettifyLocal.name; }, - get OmitKeepDiscriminatedUnion() { - return OmitKeepDiscriminatedUnion.name; - }, get WithDefaults() { return WithDefaults.name; }, diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 0df7a28926..f332361132 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -5,7 +5,7 @@ import { codeFeatures } from '../codeFeatures'; import { endOfLine, generateSfcBlockSection, identifierRegex, newLine } from '../utils'; import { generateCamelized } from '../utils/camelized'; import { wrapWith } from '../utils/wrapWith'; -import { generateComponent, generateEmitsOption } from './component'; +import { generateComponent } from './component'; import type { ScriptCodegenContext } from './context'; import { generateConstExport, generateScriptSectionPartiallyEnding, type ScriptCodegenOptions } from './index'; import { generateTemplate } from './template'; @@ -64,7 +64,19 @@ export function* generateScriptSetup( } yield `return {} as {${newLine}` - + ` props: ${ctx.localTypes.PrettifyLocal}<__VLS_OwnProps & __VLS_PublicProps & Partial<__VLS_InheritedAttrs>> & __VLS_BuiltInPublicProps,${newLine}` + + ` props: ${ctx.localTypes.PrettifyLocal}<${ + scriptSetupRanges.defineEmits || scriptSetupRanges.defineModel.length + ? `__VLS_EmitProps & ` + : `` + }__VLS_PublicProps & Partial<__VLS_InheritedAttrs>> & ${ + options.vueCompilerOptions.target >= 3.4 + ? `import('${options.vueCompilerOptions.lib}').PublicProps` + : options.vueCompilerOptions.target >= 3 + ? `import('${options.vueCompilerOptions.lib}').VNodeProps` + + ` & import('${options.vueCompilerOptions.lib}').AllowedComponentProps` + + ` & import('${options.vueCompilerOptions.lib}').ComponentCustomProps` + : `globalThis.JSX.IntrinsicAttributes` + },${newLine}` + ` expose(exposed: import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef<${ scriptSetupRanges.defineExpose ? 'typeof __VLS_exposed' : '{}' }>): void,${newLine}` @@ -421,40 +433,6 @@ function* generateComponentProps( scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges, ): Generator { - if (scriptSetup.generic) { - yield `const __VLS_fnComponent = (await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; - - if (scriptSetupRanges.defineProps?.arg) { - yield `props: `; - yield generateSfcBlockSection( - scriptSetup, - scriptSetupRanges.defineProps.arg.start, - scriptSetupRanges.defineProps.arg.end, - codeFeatures.navigation, - ); - yield `,${newLine}`; - } - - yield* generateEmitsOption(options, scriptSetupRanges); - - yield `})${endOfLine}`; - - yield `type __VLS_BuiltInPublicProps = ${ - options.vueCompilerOptions.target >= 3.4 - ? `import('${options.vueCompilerOptions.lib}').PublicProps` - : options.vueCompilerOptions.target >= 3 - ? `import('${options.vueCompilerOptions.lib}').VNodeProps` - + ` & import('${options.vueCompilerOptions.lib}').AllowedComponentProps` - + ` & import('${options.vueCompilerOptions.lib}').ComponentCustomProps` - : `globalThis.JSX.IntrinsicAttributes` - }`; - yield endOfLine; - - yield `type __VLS_OwnProps = `; - yield `${ctx.localTypes.OmitKeepDiscriminatedUnion}['$props'], keyof __VLS_BuiltInPublicProps>`; - yield endOfLine; - } - if (scriptSetupRanges.defineModel.length) { yield `const __VLS_defaults = {${newLine}`; for (const defineModel of scriptSetupRanges.defineModel) { diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 8ffeb1d2a7..86272c67f8 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -55,20 +55,6 @@ function* generateTemplateCtx( exps.push(`{} as __VLS_StyleModules`); } - if (ctx.generatedPropsType || options.scriptSetupRanges?.defineProps) { - yield `type __VLS_InternalProps = `; - const { defineProps } = options.scriptSetupRanges ?? {}; - if (defineProps) { - yield `__VLS_SpreadMerge<__VLS_PublicProps, typeof ${defineProps.name ?? `__VLS_props`}>`; - } - else { - yield `__VLS_PublicProps`; - } - yield endOfLine; - exps.push(`{} as __VLS_InternalProps`); - exps.push(`{} as { $props: __VLS_InternalProps }`); - } - const emitTypes: Code[] = []; if (options.scriptSetupRanges?.defineEmits) { emitTypes.push(`typeof ${options.scriptSetupRanges.defineEmits.name ?? `__VLS_emit`}`); @@ -77,9 +63,27 @@ function* generateTemplateCtx( emitTypes.push(`typeof __VLS_modelEmit`); } if (emitTypes.length) { + yield `type __VLS_EmitProps = __VLS_EmitsToProps<__VLS_NormalizeEmits<${emitTypes.join(' & ')}>>${endOfLine};`; exps.push(`{} as { $emit: ${emitTypes.join(' & ')} }`); } + if (options.scriptSetupRanges?.defineProps || ctx.generatedPropsType || emitTypes.length) { + yield `type __VLS_InternalProps =`; + const { defineProps } = options.scriptSetupRanges ?? {}; + if (defineProps) { + yield ` __VLS_SpreadMerge<__VLS_PublicProps, typeof ${defineProps.name ?? `__VLS_props`}>`; + } + else if (ctx.generatedPropsType) { + yield ` __VLS_PublicProps`; + } + if (emitTypes.length) { + yield ` & __VLS_EmitProps`; + } + yield endOfLine; + exps.push(`{} as { $props: __VLS_InternalProps }`); + exps.push(`{} as __VLS_InternalProps`); + } + exps.push(`{} as __VLS_Bindings`); yield `const __VLS_ctx = `; diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index d0e955f3f7..e5db6c300f 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -5,7 +5,7 @@ exports[`vue-tsc-dts > Input: #4577/main.vue, Output: #4577/main.vue.d.ts 1`] = value: string; }; declare const __VLS_export: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ - props: __VLS_PrettifyLocal & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & { + props: __VLS_PrettifyLocal<{ nonGeneric: string; rows: Row[]; } & Partial<{}>> & import("vue").PublicProps; @@ -84,10 +84,10 @@ export default _default; exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.vue.d.ts 1`] = ` "declare const __VLS_export: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ - props: __VLS_PrettifyLocal & Omit<{ - readonly "onUpdate:title"?: (value: string) => any; - readonly onBar?: (data: number) => any; - } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onUpdate:title" | "onBar"> & ({ + props: __VLS_PrettifyLocal<{ + "onUpdate:title"?: (value: string) => any; + onBar?: (data: number) => any; + } & ({ foo: number; } & { title?: string; @@ -115,10 +115,10 @@ type __VLS_PrettifyLocal = { exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: generic/custom-extension-component.cext.d.ts 1`] = ` "declare const __VLS_export: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ - props: __VLS_PrettifyLocal & Omit<{ - readonly "onUpdate:title"?: (value: string) => any; - readonly onBar?: (data: number) => any; - } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onUpdate:title" | "onBar"> & ({ + props: __VLS_PrettifyLocal<{ + "onUpdate:title"?: (value: string) => any; + onBar?: (data: number) => any; + } & ({ foo: number; } & { title?: string; diff --git a/test-workspace/tsc/passedFixtures/vue3/components/main.vue b/test-workspace/tsc/passedFixtures/vue3/components/main.vue index fe7dd1fca7..b047c695b6 100644 --- a/test-workspace/tsc/passedFixtures/vue3/components/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3/components/main.vue @@ -60,7 +60,7 @@ declare const ScriptSetupGenericExact: ( _expose?: NonNullable>['expose'], _setup?: Promise<{ props: { - readonly onBar?: ((data: T) => any) | undefined; + onBar?: ((data: T) => any) | undefined; foo: T; } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, attrs: any, From 7a9c120ee62726b5a73a6a1451c3def85ffa0cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Sun, 20 Jul 2025 01:37:31 +0800 Subject: [PATCH 08/32] refactor: simplify conditional logic --- .../lib/codegen/script/template.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 86272c67f8..609958d48e 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -41,23 +41,23 @@ function* generateTemplateCtx( ): Generator { const exps: Code[] = []; + if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileName.endsWith(ext))) { + exps.push(`globalThis`); + } if (options.sfc.script && options.scriptRanges?.exportDefault) { exps.push(`{} as InstanceType<__VLS_PickNotAny {}>>`); } else { exps.push(`{} as import('${options.vueCompilerOptions.lib}').ComponentPublicInstance`); } - - if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileName.endsWith(ext))) { - exps.push(`globalThis`); - } if (options.sfc.styles.some(style => style.module)) { exps.push(`{} as __VLS_StyleModules`); } - const emitTypes: Code[] = []; + const emitTypes: string[] = []; if (options.scriptSetupRanges?.defineEmits) { - emitTypes.push(`typeof ${options.scriptSetupRanges.defineEmits.name ?? `__VLS_emit`}`); + const { defineEmits } = options.scriptSetupRanges; + emitTypes.push(`typeof ${defineEmits.name ?? `__VLS_emit`}`); } if (options.scriptSetupRanges?.defineModel.length) { emitTypes.push(`typeof __VLS_modelEmit`); @@ -67,19 +67,19 @@ function* generateTemplateCtx( exps.push(`{} as { $emit: ${emitTypes.join(' & ')} }`); } - if (options.scriptSetupRanges?.defineProps || ctx.generatedPropsType || emitTypes.length) { - yield `type __VLS_InternalProps =`; - const { defineProps } = options.scriptSetupRanges ?? {}; - if (defineProps) { - yield ` __VLS_SpreadMerge<__VLS_PublicProps, typeof ${defineProps.name ?? `__VLS_props`}>`; - } - else if (ctx.generatedPropsType) { - yield ` __VLS_PublicProps`; - } - if (emitTypes.length) { - yield ` & __VLS_EmitProps`; - } - yield endOfLine; + const propTypes: string[] = []; + if (options.scriptSetupRanges?.defineProps) { + const { defineProps } = options.scriptSetupRanges; + propTypes.push(`__VLS_SpreadMerge<__VLS_PublicProps, typeof ${defineProps.name ?? `__VLS_props`}>`); + } + else if (ctx.generatedPropsType) { + propTypes.push(`__VLS_PublicProps`); + } + if (emitTypes.length) { + propTypes.push(`__VLS_EmitProps`); + } + if (propTypes.length) { + yield `type __VLS_InternalProps = ${propTypes.join(' & ')}${endOfLine}`; exps.push(`{} as { $props: __VLS_InternalProps }`); exps.push(`{} as __VLS_InternalProps`); } From e992097f3e606c4a1baa1b580cc9fcd4558c1c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Mon, 21 Jul 2025 03:33:07 +0800 Subject: [PATCH 09/32] fix: `withDefaults` and bypass boolean casting --- .../language-core/lib/codegen/globalTypes.ts | 5 +++++ .../language-core/lib/codegen/localTypes.ts | 12 +++++------ .../lib/codegen/script/component.ts | 4 ++-- .../lib/codegen/script/scriptSetup.ts | 6 +++--- .../lib/codegen/script/template.ts | 21 ++++++++++++++----- .../tsc/passedFixtures/vue3/#3779/main.vue | 2 +- .../tsc/passedFixtures/vue3/#3779/named.vue | 2 +- 7 files changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts index 6bf17fcb28..0d827fa735 100644 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -136,6 +136,11 @@ export function generateGlobalTypes(options: VueCompilerOptions) { [K in Exclude & string as \`v\${Capitalize}\`]: T[K]; }; type __VLS_PrettifyGlobal = { [K in keyof T as K]: T[K]; } & {}; + type __VLS_WithDefaultsGlobal = { + [K in keyof P as K extends keyof D ? K : never]-?: P[K]; + } & { + [K in keyof P as K extends keyof D ? never : K]: P[K]; + }; type __VLS_UseTemplateRef = Readonly>; type __VLS_ProxyRefs = import('${lib}').ShallowUnwrapRef; diff --git a/packages/language-core/lib/codegen/localTypes.ts b/packages/language-core/lib/codegen/localTypes.ts index 8e36bdfc93..aa2f6d5098 100644 --- a/packages/language-core/lib/codegen/localTypes.ts +++ b/packages/language-core/lib/codegen/localTypes.ts @@ -5,11 +5,11 @@ import { endOfLine } from './utils'; export function getLocalTypesGenerator(vueCompilerOptions: VueCompilerOptions) { const used = new Set(); - const WithDefaults = defineHelper( - `__VLS_WithDefaults`, + const WithDefaultsLocal = defineHelper( + `__VLS_WithDefaultsLocal`, () => ` -type __VLS_WithDefaults = { +type __VLS_WithDefaultsLocal = { [K in keyof Pick]: K extends keyof D ? ${PrettifyLocal.name} : P[K] @@ -69,7 +69,7 @@ type __VLS_TypePropsToOption = { ); const helpers = { [PrettifyLocal.name]: PrettifyLocal, - [WithDefaults.name]: WithDefaults, + [WithDefaultsLocal.name]: WithDefaultsLocal, [WithSlots.name]: WithSlots, [PropsChildren.name]: PropsChildren, [TypePropsToOption.name]: TypePropsToOption, @@ -85,8 +85,8 @@ type __VLS_TypePropsToOption = { get PrettifyLocal() { return PrettifyLocal.name; }, - get WithDefaults() { - return WithDefaults.name; + get WithDefaultsLocal() { + return WithDefaultsLocal.name; }, get WithSlots() { return WithSlots.name; diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index 16518dc31d..1f07885336 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( const propsType = `${ctx.localTypes.TypePropsToOption}<__VLS_PublicProps>`; return `{} as ` + ( scriptSetupRanges.withDefaults?.arg - ? `${ctx.localTypes.WithDefaults}<${propsType}, typeof __VLS_withDefaultsArg>` + ? `${ctx.localTypes.WithDefaultsLocal}<${propsType}, typeof __VLS_defaults>` : propsType ); }); @@ -163,7 +163,7 @@ export function* generatePropsOption( options.vueCompilerOptions.target >= 3.6 && scriptSetupRanges.withDefaults?.arg ) { - yield `__defaults: __VLS_withDefaultsArg,${newLine}`; + yield `__defaults: __VLS_defaults,${newLine}`; } yield `__typeProps: `; yield* generateSpreadMerge(typeOptionCodes); diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index f332361132..6529ec3617 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -301,7 +301,7 @@ function* generateSetupFunction( if (scriptSetupRanges.defineProps?.typeArg && scriptSetupRanges.withDefaults?.arg) { // fix https://github.com/vuejs/language-tools/issues/1187 - yield `const __VLS_withDefaultsArg = (function (t: T) { return t })(`; + yield `const __VLS_defaults = (function (t: T) { return t })(`; yield generateSfcBlockSection( scriptSetup, scriptSetupRanges.withDefaults.arg.start, @@ -434,7 +434,7 @@ function* generateComponentProps( scriptSetupRanges: ScriptSetupRanges, ): Generator { if (scriptSetupRanges.defineModel.length) { - yield `const __VLS_defaults = {${newLine}`; + yield `const __VLS_defaultModels = {${newLine}`; for (const defineModel of scriptSetupRanges.defineModel) { if (!defineModel.defaultValue) { continue; @@ -543,7 +543,7 @@ function* generateDefineModelType( } else if (defineModel.defaultValue && propName) { // Infer from defineModel({default: T}) - yield `typeof __VLS_defaults['${propName}']`; + yield `typeof __VLS_defaultModels['${propName}']`; } else { yield `any`; diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 609958d48e..673566d04e 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -67,13 +67,24 @@ function* generateTemplateCtx( exps.push(`{} as { $emit: ${emitTypes.join(' & ')} }`); } + const { defineProps, withDefaults } = options.scriptSetupRanges ?? {}; + const props = defineProps?.arg + ? `typeof ${defineProps.name ?? `__VLS_props`}` + : defineProps?.typeArg && withDefaults?.arg + ? `__VLS_WithDefaultsGlobal<__VLS_Props, typeof __VLS_defaults>` + : undefined; + const propTypes: string[] = []; - if (options.scriptSetupRanges?.defineProps) { - const { defineProps } = options.scriptSetupRanges; - propTypes.push(`__VLS_SpreadMerge<__VLS_PublicProps, typeof ${defineProps.name ?? `__VLS_props`}>`); + if (ctx.generatedPropsType) { + if (props) { + propTypes.push(`__VLS_SpreadMerge<__VLS_PublicProps, ${props}>`); + } + else { + propTypes.push(`__VLS_PublicProps`); + } } - else if (ctx.generatedPropsType) { - propTypes.push(`__VLS_PublicProps`); + else if (props) { + propTypes.push(props); } if (emitTypes.length) { propTypes.push(`__VLS_EmitProps`); diff --git a/test-workspace/tsc/passedFixtures/vue3/#3779/main.vue b/test-workspace/tsc/passedFixtures/vue3/#3779/main.vue index dca94c63ae..5fcef0b240 100644 --- a/test-workspace/tsc/passedFixtures/vue3/#3779/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3/#3779/main.vue @@ -6,5 +6,5 @@ defineProps<{ diff --git a/test-workspace/tsc/passedFixtures/vue3/#3779/named.vue b/test-workspace/tsc/passedFixtures/vue3/#3779/named.vue index dca94c63ae..5fcef0b240 100644 --- a/test-workspace/tsc/passedFixtures/vue3/#3779/named.vue +++ b/test-workspace/tsc/passedFixtures/vue3/#3779/named.vue @@ -6,5 +6,5 @@ defineProps<{ From 81854db6a0037ef4b4f14037cfd25e499fb25cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Mon, 21 Jul 2025 03:53:24 +0800 Subject: [PATCH 10/32] refactor: simplify --- .../lib/codegen/script/scriptSetup.ts | 32 +++++++------------ .../lib/codegen/script/template.ts | 31 +++++++----------- 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 6529ec3617..9e68b55e4f 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -4,6 +4,7 @@ import type { Code, Sfc, TextRange } from '../../types'; import { codeFeatures } from '../codeFeatures'; import { endOfLine, generateSfcBlockSection, identifierRegex, newLine } from '../utils'; import { generateCamelized } from '../utils/camelized'; +import { generateIntersectMerge } from '../utils/merge'; import { wrapWith } from '../utils/wrapWith'; import { generateComponent } from './component'; import type { ScriptCodegenContext } from './context'; @@ -448,27 +449,15 @@ function* generateComponentProps( yield `}${endOfLine}`; } - yield `type __VLS_PublicProps = `; + const propTypes: Code[] = []; if (scriptSetupRanges.defineSlots && options.vueCompilerOptions.jsxSlots) { - if (ctx.generatedPropsType) { - yield ` & `; - } - ctx.generatedPropsType = true; - yield `${ctx.localTypes.PropsChildren}<__VLS_Slots>`; + propTypes.push(`${ctx.localTypes.PropsChildren}<__VLS_Slots>`); } if (scriptSetupRanges.defineProps?.typeArg) { - if (ctx.generatedPropsType) { - yield ` & `; - } - ctx.generatedPropsType = true; - yield `__VLS_Props`; + propTypes.push(`__VLS_Props`); } if (scriptSetupRanges.defineModel.length) { - if (ctx.generatedPropsType) { - yield ` & `; - } - ctx.generatedPropsType = true; - yield `{${newLine}`; + yield `type __VLS_ModelProps = {${newLine}`; for (const defineModel of scriptSetupRanges.defineModel) { const [propName, localName] = getPropAndLocalName(scriptSetup, defineModel); @@ -499,12 +488,15 @@ function* generateComponentProps( yield `'${modifierName}'?: Partial>,${newLine}`; } } - yield `}`; + yield `}${endOfLine}`; + propTypes.push(`__VLS_ModelProps`); } - if (!ctx.generatedPropsType) { - yield `{}`; + if (propTypes.length) { + ctx.generatedPropsType = true; + yield `type __VLS_PublicProps = `; + yield* generateIntersectMerge(propTypes); + yield endOfLine; } - yield endOfLine; } function* generateModelEmit( diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 673566d04e..c8b946619e 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -20,7 +20,7 @@ export function* generateTemplate( const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), }); - yield* generateTemplateCtx(options, ctx); + yield* generateTemplateCtx(options); yield* generateTemplateElements(); yield* generateTemplateComponents(options); yield* generateTemplateDirectives(options); @@ -35,10 +35,7 @@ export function* generateTemplate( } } -function* generateTemplateCtx( - options: ScriptCodegenOptions, - ctx: ScriptCodegenContext, -): Generator { +function* generateTemplateCtx(options: ScriptCodegenOptions): Generator { const exps: Code[] = []; if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileName.endsWith(ext))) { @@ -63,29 +60,25 @@ function* generateTemplateCtx( emitTypes.push(`typeof __VLS_modelEmit`); } if (emitTypes.length) { - yield `type __VLS_EmitProps = __VLS_EmitsToProps<__VLS_NormalizeEmits<${emitTypes.join(' & ')}>>${endOfLine};`; + yield `type __VLS_EmitProps = __VLS_EmitsToProps<__VLS_NormalizeEmits<${emitTypes.join(' & ')}>>${endOfLine}`; exps.push(`{} as { $emit: ${emitTypes.join(' & ')} }`); } + const propTypes: string[] = []; const { defineProps, withDefaults } = options.scriptSetupRanges ?? {}; const props = defineProps?.arg ? `typeof ${defineProps.name ?? `__VLS_props`}` - : defineProps?.typeArg && withDefaults?.arg - ? `__VLS_WithDefaultsGlobal<__VLS_Props, typeof __VLS_defaults>` + : defineProps?.typeArg + ? withDefaults?.arg + ? `__VLS_WithDefaultsGlobal<__VLS_Props, typeof __VLS_defaults>` + : `__VLS_Props` : undefined; - - const propTypes: string[] = []; - if (ctx.generatedPropsType) { - if (props) { - propTypes.push(`__VLS_SpreadMerge<__VLS_PublicProps, ${props}>`); - } - else { - propTypes.push(`__VLS_PublicProps`); - } - } - else if (props) { + if (props) { propTypes.push(props); } + if (options.scriptSetupRanges?.defineModel.length) { + propTypes.push(`__VLS_ModelProps`); + } if (emitTypes.length) { propTypes.push(`__VLS_EmitProps`); } From 850c2c67781303bb210e78d835feac317abaf320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Mon, 21 Jul 2025 04:02:01 +0800 Subject: [PATCH 11/32] fix: intersect `__VLS_PublicProps` on demand --- .../lib/codegen/script/scriptSetup.ts | 16 ++++++++----- .../tsc/tests/__snapshots__/dts.spec.ts.snap | 24 +++++++++---------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 9e68b55e4f..6c840f5028 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -55,8 +55,16 @@ export function* generateScriptSetup( + ` __VLS_setup = (async () => {${newLine}`; yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, undefined); - const emitTypes: string[] = []; + const propTypes: Code[] = []; + if (ctx.generatedPropsType) { + propTypes.push(`__VLS_PublicProps`); + } + if (scriptSetupRanges.defineEmits || scriptSetupRanges.defineModel.length) { + propTypes.push(`__VLS_EmitProps`); + } + propTypes.push(`Partial<__VLS_InheritedAttrs>`); + const emitTypes: Code[] = []; if (scriptSetupRanges.defineEmits) { emitTypes.push(`typeof ${scriptSetupRanges.defineEmits.name ?? '__VLS_emit'}`); } @@ -65,11 +73,7 @@ export function* generateScriptSetup( } yield `return {} as {${newLine}` - + ` props: ${ctx.localTypes.PrettifyLocal}<${ - scriptSetupRanges.defineEmits || scriptSetupRanges.defineModel.length - ? `__VLS_EmitProps & ` - : `` - }__VLS_PublicProps & Partial<__VLS_InheritedAttrs>> & ${ + + ` props: ${ctx.localTypes.PrettifyLocal}<${propTypes.join(' & ')}> & ${ options.vueCompilerOptions.target >= 3.4 ? `import('${options.vueCompilerOptions.lib}').PublicProps` : options.vueCompilerOptions.target >= 3 diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index e5db6c300f..f25e36703f 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -84,14 +84,14 @@ export default _default; exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.vue.d.ts 1`] = ` "declare const __VLS_export: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ - props: __VLS_PrettifyLocal<{ - "onUpdate:title"?: (value: string) => any; - onBar?: (data: number) => any; - } & ({ + props: __VLS_PrettifyLocal<({ foo: number; } & { title?: string; - }) & Partial<{}>> & import("vue").PublicProps; + }) & { + "onUpdate:title"?: (value: string) => any; + onBar?: (data: number) => any; + } & Partial<{}>> & import("vue").PublicProps; expose(exposed: import("vue").ShallowUnwrapRef<{ baz: number; }>): void; @@ -115,14 +115,14 @@ type __VLS_PrettifyLocal = { exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: generic/custom-extension-component.cext.d.ts 1`] = ` "declare const __VLS_export: (__VLS_props: NonNullable>["props"], __VLS_ctx?: __VLS_PrettifyLocal>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable>["expose"], __VLS_setup?: Promise<{ - props: __VLS_PrettifyLocal<{ - "onUpdate:title"?: (value: string) => any; - onBar?: (data: number) => any; - } & ({ + props: __VLS_PrettifyLocal<({ foo: number; } & { title?: string; - }) & Partial<{}>> & import("vue").PublicProps; + }) & { + "onUpdate:title"?: (value: string) => any; + onBar?: (data: number) => any; + } & Partial<{}>> & import("vue").PublicProps; expose(exposed: import("vue").ShallowUnwrapRef<{ baz: number; }>): void; @@ -326,7 +326,7 @@ export default _default; `; exports[`vue-tsc-dts > Input: reference-type-model/component.vue, Output: reference-type-model/component.vue.d.ts 1`] = ` -"type __VLS_PublicProps = { +"type __VLS_ModelProps = { /** * required number modelValue */ @@ -361,7 +361,7 @@ declare const __VLS_export: import("vue").DefineComponent2<{ directives: {}; provide: {}; expose: string; - __typeProps: __VLS_PublicProps; + __typeProps: __VLS_ModelProps; __typeEmits: __VLS_ModelEmit; __typeRefs: {}; __typeEl: any; From ee3eda0000c1fce419185d183e8d510ec4b4a311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Mon, 21 Jul 2025 04:56:08 +0800 Subject: [PATCH 12/32] refactor: `Code` to `string` for `.join(' & ')` --- packages/language-core/lib/codegen/script/scriptSetup.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 6c840f5028..bada45b866 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -55,7 +55,7 @@ export function* generateScriptSetup( + ` __VLS_setup = (async () => {${newLine}`; yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, undefined); - const propTypes: Code[] = []; + const propTypes: string[] = []; if (ctx.generatedPropsType) { propTypes.push(`__VLS_PublicProps`); } @@ -64,7 +64,7 @@ export function* generateScriptSetup( } propTypes.push(`Partial<__VLS_InheritedAttrs>`); - const emitTypes: Code[] = []; + const emitTypes: string[] = []; if (scriptSetupRanges.defineEmits) { emitTypes.push(`typeof ${scriptSetupRanges.defineEmits.name ?? '__VLS_emit'}`); } From d18dd97a13be14f4160b75caa337887def51a17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Tue, 22 Jul 2025 18:20:46 +0800 Subject: [PATCH 13/32] fix: runtime props for generic component --- packages/language-core/lib/codegen/globalTypes.ts | 3 +++ .../language-core/lib/codegen/script/scriptSetup.ts | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts index 0d827fa735..928a9e5946 100644 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -144,6 +144,9 @@ export function generateGlobalTypes(options: VueCompilerOptions) { type __VLS_UseTemplateRef = Readonly>; type __VLS_ProxyRefs = import('${lib}').ShallowUnwrapRef; + function __VLS_definePublicProps(options: T): import('${lib}').${ + target >= 3.3 ? `ExtractPublicPropTypes` : `ExtractPropTypes` + }; function __VLS_getVForSourceType>(source: T): [ item: T extends number ? number : T extends string ? string diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index bada45b866..a542110745 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -59,6 +59,17 @@ export function* generateScriptSetup( if (ctx.generatedPropsType) { propTypes.push(`__VLS_PublicProps`); } + if (scriptSetupRanges.defineProps?.arg) { + yield `const __VLS_publicProps = __VLS_definePublicProps(`; + yield generateSfcBlockSection( + scriptSetup, + scriptSetupRanges.defineProps.arg.start, + scriptSetupRanges.defineProps.arg.end, + codeFeatures.navigation, + ); + yield `)${endOfLine}`; + propTypes.push(`typeof __VLS_publicProps`); + } if (scriptSetupRanges.defineEmits || scriptSetupRanges.defineModel.length) { propTypes.push(`__VLS_EmitProps`); } From 011e562726a318a9f7d458a2d005f9cb4f3e5040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Wed, 23 Jul 2025 03:44:51 +0800 Subject: [PATCH 14/32] refactor: add `Partial` to inherited attrs in advance --- .../language-core/lib/codegen/script/component.ts | 7 +++---- .../language-core/lib/codegen/script/scriptSetup.ts | 4 +++- packages/language-core/lib/codegen/template/index.ts | 11 ++++++----- packages/tsc/tests/__snapshots__/dts.spec.ts.snap | 6 +++--- test-workspace/tsc/passedFixtures/vue3/attrs/main.vue | 1 + 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index 1f07885336..84b02e8ded 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -56,7 +56,7 @@ export function* generateComponent( if (!ctx.bypassDefineComponent) { const emitOptionCodes = [...generateEmitsOption(options, scriptSetupRanges)]; yield* emitOptionCodes; - yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges, !!emitOptionCodes.length, true); + yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges, !!emitOptionCodes.length); } if ( options.vueCompilerOptions.target >= 3.5 @@ -119,13 +119,12 @@ export function* generatePropsOption( scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges, hasEmitsOption: boolean, - inheritAttrs: boolean, ): Generator { const getOptionCodes: (() => Code)[] = []; const typeOptionCodes: Code[] = []; - if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) { - let attrsType = `Partial<__VLS_InheritedAttrs>`; + if (options.templateCodegen?.inheritedAttrVars.size) { + let attrsType = `__VLS_InheritedAttrs`; if (hasEmitsOption) { attrsType = `Omit<${attrsType}, \`on\${string}\`>`; } diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index a542110745..91795b1fdb 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -73,7 +73,9 @@ export function* generateScriptSetup( if (scriptSetupRanges.defineEmits || scriptSetupRanges.defineModel.length) { propTypes.push(`__VLS_EmitProps`); } - propTypes.push(`Partial<__VLS_InheritedAttrs>`); + if (options.templateCodegen?.inheritedAttrVars.size) { + propTypes.push(`__VLS_InheritedAttrs`); + } const emitTypes: string[] = []; if (scriptSetupRanges.defineEmits) { diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index 2d09d575cd..8e08928f60 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -111,10 +111,11 @@ function* generateInheritedAttrs( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, ): Generator { - yield `type __VLS_InheritedAttrs = {}`; - for (const varName of ctx.inheritedAttrVars) { - yield ` & typeof ${varName}`; - } + yield `type __VLS_InheritedAttrs = ${ + ctx.inheritedAttrVars.size + ? `Partial<${[...ctx.inheritedAttrVars].map(name => `typeof ${name}`).join(' & ')}>` + : `{}` + }`; yield endOfLine; if (ctx.bindingAttrLocs.length) { @@ -131,7 +132,7 @@ function* generateInheritedAttrs( } yield `]${endOfLine}`; } - return `import('${options.vueCompilerOptions.lib}').ComponentPublicInstance['$attrs'] & Partial<__VLS_InheritedAttrs>`; + return `import('${options.vueCompilerOptions.lib}').ComponentPublicInstance['$attrs'] & __VLS_InheritedAttrs`; } function* generateTemplateRefs( diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index f25e36703f..5fb62b843c 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -8,7 +8,7 @@ declare const __VLS_export: (__VLS_props: NonNullable> & import("vue").PublicProps; + }> & import("vue").PublicProps; expose(exposed: import("vue").ShallowUnwrapRef<{}>): void; attrs: any; slots: { @@ -91,7 +91,7 @@ exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.v }) & { "onUpdate:title"?: (value: string) => any; onBar?: (data: number) => any; - } & Partial<{}>> & import("vue").PublicProps; + }> & import("vue").PublicProps; expose(exposed: import("vue").ShallowUnwrapRef<{ baz: number; }>): void; @@ -122,7 +122,7 @@ exports[`vue-tsc-dts > Input: generic/custom-extension-component.cext, Output: g }) & { "onUpdate:title"?: (value: string) => any; onBar?: (data: number) => any; - } & Partial<{}>> & import("vue").PublicProps; + }> & import("vue").PublicProps; expose(exposed: import("vue").ShallowUnwrapRef<{ baz: number; }>): void; diff --git a/test-workspace/tsc/passedFixtures/vue3/attrs/main.vue b/test-workspace/tsc/passedFixtures/vue3/attrs/main.vue index 63a42d4a88..60ca99480f 100644 --- a/test-workspace/tsc/passedFixtures/vue3/attrs/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3/attrs/main.vue @@ -1,3 +1,4 @@ +