diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts index 5851bb1899..71cc20fb19 100644 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -50,13 +50,13 @@ export function generateGlobalTypes(options: VueCompilerOptions) { type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; type __VLS_SpreadMerge = Omit & B; 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] } : + N1 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N1] } : + N2 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N2] } : + N3 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N3] } : Self extends object ? { [K in N0]: Self } : - N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } : - N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } : - N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } : + N1 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N1] } : + N2 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N2] } : + N3 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N3] } : {}; type __VLS_FunctionalComponentCtx = __VLS_PickNotAny<'__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: infer Ctx } ? NonNullable : never : any @@ -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, @@ -129,10 +133,16 @@ export function generateGlobalTypes(options: VueCompilerOptions) { NormalizedEmits = __VLS_NormalizeEmits extends infer E ? string extends keyof E ? {} : E : never, > = __VLS_SpreadMerge; type __VLS_ResolveDirectives = { - [K in Exclude & string as \`v\${Capitalize}\`]: T[K]; + [K in keyof T & 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; function __VLS_getVForSourceType>(source: T): [ item: T extends number ? number @@ -154,7 +164,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/localTypes.ts b/packages/language-core/lib/codegen/localTypes.ts index c41cd55cde..7be0e26c85 100644 --- a/packages/language-core/lib/codegen/localTypes.ts +++ b/packages/language-core/lib/codegen/localTypes.ts @@ -5,20 +5,11 @@ import { endOfLine } from './utils'; export function getLocalTypesGenerator(vueCompilerOptions: VueCompilerOptions) { const used = new Set(); - const OmitKeepDiscriminatedUnion = defineHelper( - `__VLS_OmitKeepDiscriminatedUnion`, + const WithDefaultsLocal = defineHelper( + `__VLS_WithDefaultsLocal`, () => ` -type __VLS_OmitKeepDiscriminatedUnion = T extends any - ? Pick> - : never; -`.trimStart(), - ); - const WithDefaults = defineHelper( - `__VLS_WithDefaults`, - () => - ` -type __VLS_WithDefaults = { +type __VLS_WithDefaultsLocal = { [K in keyof Pick]: K extends keyof D ? ${PrettifyLocal.name} : P[K] @@ -36,7 +27,6 @@ type __VLS_WithDefaults = { type __VLS_WithSlots = T & { new(): { ${getSlotsPropertyName(vueCompilerOptions.target)}: S; - ${vueCompilerOptions.jsxSlots ? `$props: ${PropsChildren.name};` : ''} } }; `.trimStart(), @@ -78,8 +68,7 @@ type __VLS_TypePropsToOption = { ); const helpers = { [PrettifyLocal.name]: PrettifyLocal, - [OmitKeepDiscriminatedUnion.name]: OmitKeepDiscriminatedUnion, - [WithDefaults.name]: WithDefaults, + [WithDefaultsLocal.name]: WithDefaultsLocal, [WithSlots.name]: WithSlots, [PropsChildren.name]: PropsChildren, [TypePropsToOption.name]: TypePropsToOption, @@ -89,17 +78,11 @@ type __VLS_TypePropsToOption = { return { generate, - getUsedNames() { - return used; - }, get PrettifyLocal() { return PrettifyLocal.name; }, - get OmitKeepDiscriminatedUnion() { - return OmitKeepDiscriminatedUnion.name; - }, - get WithDefaults() { - return WithDefaults.name; + get WithDefaultsLocal() { + return WithDefaultsLocal.name; }, get WithSlots() { return WithSlots.name; @@ -115,20 +98,11 @@ type __VLS_TypePropsToOption = { }, }; - function* generate(names: string[]) { - const generated = new Set(); - while (names.length) { - used.clear(); - for (const name of names) { - if (generated.has(name)) { - continue; - } - const helper = helpers[name as keyof typeof helpers]; - yield helper.generate(); - generated.add(name); - } - names = [...used].filter(name => !generated.has(name)); + function* generate() { + for (const name of used) { + yield helpers[name].generate(); } + used.clear(); } function defineHelper(name: string, generate: () => string) { diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index 4b1de2a15b..4a6457a589 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -29,9 +29,18 @@ export function* generateComponent( yield `(await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; } - const returns: Code[] = []; + const returns: string[] = []; if (ctx.bypassDefineComponent) { - returns.push(...generateComponentSetupReturns(scriptSetupRanges)); + // fill $props + if (scriptSetupRanges.defineProps) { + const name = scriptSetupRanges.defineProps.name ?? `__VLS_props`; + // NOTE: defineProps is inaccurate for $props + returns.push(name, `{} as { $props: Partial }`); + } + // fill $emit + if (scriptSetupRanges.defineEmits) { + returns.push(`{} as { $emit: typeof ${scriptSetupRanges.defineEmits.name ?? `__VLS_emit`} }`); + } } if (scriptSetupRanges.defineExpose) { returns.push(`__VLS_exposed`); @@ -45,7 +54,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 @@ -68,21 +77,7 @@ export function* generateComponent( yield `})`; } -export function* generateComponentSetupReturns(scriptSetupRanges: ScriptSetupRanges): Generator { - // fill $props - if (scriptSetupRanges.defineProps) { - const name = scriptSetupRanges.defineProps.name ?? `__VLS_props`; - // NOTE: defineProps is inaccurate for $props - yield name; - yield `{} as { $props: Partial }`; - } - // fill $emit - if (scriptSetupRanges.defineEmits) { - yield `{} as { $emit: typeof ${scriptSetupRanges.defineEmits.name ?? `__VLS_emit`} }`; - } -} - -export function* generateEmitsOption( +function* generateEmitsOption( options: ScriptCodegenOptions, scriptSetupRanges: ScriptSetupRanges, ): Generator { @@ -116,18 +111,17 @@ export function* generateEmitsOption( } } -export function* generatePropsOption( +function* generatePropsOption( options: ScriptCodegenOptions, ctx: ScriptCodegenContext, scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges, hasEmitsOption: boolean, - inheritAttrs: boolean, ): Generator { const getOptionCodes: (() => Code)[] = []; const typeOptionCodes: Code[] = []; - if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) { + if (options.templateCodegen?.inheritedAttrVars.size) { let attrsType = `__VLS_InheritedAttrs`; if (hasEmitsOption) { attrsType = `Omit<${attrsType}, \`on\${string}\`>`; @@ -145,7 +139,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 ); }); @@ -166,7 +160,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/componentSelf.ts b/packages/language-core/lib/codegen/script/componentSelf.ts deleted file mode 100644 index 1f8b6b8377..0000000000 --- a/packages/language-core/lib/codegen/script/componentSelf.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { camelize, capitalize } from '@vue/shared'; -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'; - -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}`; - if (ctx.bypassDefineComponent) { - for (const code of generateComponentSetupReturns(options.scriptSetupRanges)) { - yield `...${code},${newLine}`; - } - } - // bindings - const templateUsageVars = new Set([ - ...options.sfc.template?.ast?.components.flatMap(c => [camelize(c), capitalize(camelize(c))]) ?? [], - ...options.templateCodegen?.accessExternalVariables.keys() ?? [], - ...templateCodegenCtx.accessExternalVariables.keys(), - ]); - for (const varName of ctx.bindingNames) { - 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 `}),${newLine}`; - 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 69cf33c303..fac9504143 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -2,12 +2,12 @@ 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 { generateComponentSelf } from './componentSelf'; -import { type ScriptCodegenContext } from './context'; +import { endOfLine, generatePartiallyEnding, generateSfcBlockSection, newLine } from '../utils'; +import { wrapWith } from '../utils/wrapWith'; +import type { ScriptCodegenContext } from './context'; import { generateScriptSetup, generateScriptSetupImports } from './scriptSetup'; import { generateSrc } from './src'; import { generateTemplate } from './template'; @@ -32,74 +32,63 @@ export function* generateScript( options: ScriptCodegenOptions, ctx: ScriptCodegenContext, ): Generator { - yield* generateGlobalTypesPath(options); + yield* generateGlobalTypesReference(options); - if (options.sfc.script?.src) { - yield* generateSrc(options.sfc.script.src); - } if (options.sfc.scriptSetup && options.scriptSetupRanges) { yield* generateScriptSetupImports(options.sfc.scriptSetup, options.scriptSetupRanges); } if (options.sfc.script && options.scriptRanges) { const { exportDefault, classBlockEnd } = options.scriptRanges; - const isExportRawObject = exportDefault - && options.sfc.script.content[exportDefault.expression.start] === '{'; if (options.sfc.scriptSetup && options.scriptSetupRanges) { if (exportDefault) { - yield generateSfcBlockSection(options.sfc.script, 0, exportDefault.expression.start, codeFeatures.all); + yield generateSfcBlockSection(options.sfc.script, 0, exportDefault.start, codeFeatures.all); yield* generateScriptSetup(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges); - yield generateSfcBlockSection( - options.sfc.script, - exportDefault.expression.end, - options.sfc.script.content.length, - codeFeatures.all, - ); } else { yield generateSfcBlockSection(options.sfc.script, 0, options.sfc.script.content.length, codeFeatures.all); - yield* generateScriptSectionPartiallyEnding( - options.sfc.script.name, - options.sfc.script.content.length, - '#3632/both.vue', - ); yield* generateScriptSetup(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges); } } - else if (exportDefault && isExportRawObject && options.vueCompilerOptions.optionsWrapper.length) { - ctx.inlayHints.push({ - blockName: options.sfc.script.name, - offset: exportDefault.expression.start, - setting: 'vue.inlayHints.optionsWrapper', - label: options.vueCompilerOptions.optionsWrapper.length - ? options.vueCompilerOptions.optionsWrapper[0] - : '[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: options.vueCompilerOptions.optionsWrapper.length >= 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 ( + options.sfc.script.content[exportDefault.expression.start] === '{' + && 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, 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; + } + yield endOfLine; } else if (classBlockEnd !== undefined) { if (options.vueCompilerOptions.skipTemplateCodegen) { @@ -108,8 +97,7 @@ export function* generateScript( else { yield generateSfcBlockSection(options.sfc.script, 0, classBlockEnd, codeFeatures.all); yield `__VLS_template = () => {${newLine}`; - const templateCodegenCtx = yield* generateTemplate(options, ctx); - yield* generateComponentSelf(options, ctx, templateCodegenCtx); + yield* generateTemplate(options, ctx); yield `}${endOfLine}`; yield generateSfcBlockSection( options.sfc.script, @@ -121,39 +109,26 @@ export function* generateScript( } else { yield generateSfcBlockSection(options.sfc.script, 0, options.sfc.script.content.length, codeFeatures.all); - yield* generateScriptSectionPartiallyEnding( - options.sfc.script.name, - options.sfc.script.content.length, - '#3632/script.vue', - ); + yield* generateConstExport(options, options.sfc.script); + yield `(await import('${options.vueCompilerOptions.lib}')).defineComponent({})${endOfLine}`; } } else if (options.sfc.scriptSetup && options.scriptSetupRanges) { yield* generateScriptSetup(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges); } - if (options.sfc.scriptSetup) { - yield* generateScriptSectionPartiallyEnding( - options.sfc.scriptSetup.name, - options.sfc.scriptSetup.content.length, - '#4569/main.vue', - ';', - ); - } - if (!ctx.generatedTemplate) { - const templateCodegenCtx = yield* generateTemplate(options, ctx); - yield* generateComponentSelf(options, ctx, templateCodegenCtx); + yield* generateTemplate(options, ctx); } - yield* ctx.localTypes.generate([...ctx.localTypes.getUsedNames()]); - - if (options.sfc.scriptSetup) { - yield ['', 'scriptSetup', options.sfc.scriptSetup.content.length, codeFeatures.verification]; + if (!options.scriptRanges?.classBlockEnd) { + yield* generateExportDefault(options); } + + yield* ctx.localTypes.generate(); } -function* generateGlobalTypesPath( +function* generateGlobalTypesReference( options: ScriptCodegenOptions, ): Generator { const globalTypesPath = options.vueCompilerOptions.globalTypesPath(options.fileName); @@ -177,13 +152,58 @@ function* generateGlobalTypesPath( } } -export function* generateScriptSectionPartiallyEnding( - source: string, - end: number, - mark: string, - delimiter = 'debugger', +export function* generateConstExport( + options: ScriptCodegenOptions, + block: SfcBlock, ): Generator { - yield delimiter; - yield ['', source, end, codeFeatures.verification]; - yield `/* PartiallyEnd: ${mark} */${newLine}`; + if (options.sfc.script) { + yield* generatePartiallyEnding( + options.sfc.script.name, + options.scriptRanges?.exportDefault + ? options.scriptRanges.exportDefault.start + : options.sfc.script.content.length, + '#3632/script.vue', + ); + } + yield `const `; + yield* wrapWith( + 0, + block.content.length, + block.name, + codeFeatures.verification, + `__VLS_export`, + ); + yield ` = `; +} + +function* generateExportDefault(options: ScriptCodegenOptions): Generator { + if (options.sfc.script?.src) { + yield* generateSrc(options.sfc.script.src); + return; + } + + 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; } diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 58397c4c2d..8bf9f895d6 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -2,13 +2,12 @@ import { camelize } from '@vue/shared'; import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges'; import type { Code, Sfc, TextRange } from '../../types'; import { codeFeatures } from '../codeFeatures'; -import { endOfLine, generateSfcBlockSection, newLine } from '../utils'; +import { endOfLine, generatePartiallyEnding, generateSfcBlockSection, identifierRegex, newLine } from '../utils'; import { generateCamelized } from '../utils/camelized'; import { wrapWith } from '../utils/wrapWith'; -import { generateComponent, generateEmitsOption } from './component'; -import { generateComponentSelf } from './componentSelf'; +import { generateComponent } from './component'; import type { ScriptCodegenContext } from './context'; -import { generateScriptSectionPartiallyEnding, type ScriptCodegenOptions } from './index'; +import { generateConstExport, type ScriptCodegenOptions } from './index'; import { generateTemplate } from './template'; export function* generateScriptSetupImports( @@ -33,11 +32,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(options, scriptSetup); yield `(`; if (typeof scriptSetup.generic === 'object') { yield `<`; @@ -59,8 +54,33 @@ export function* generateScriptSetup( + ` __VLS_setup = (async () => {${newLine}`; yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, undefined); - const emitTypes: string[] = []; + const propTypes: string[] = []; + if (ctx.generatedPropsType) { + propTypes.push(`__VLS_PublicProps`); + } + if (scriptSetupRanges.defineProps?.arg) { + yield `const __VLS_propsOption = `; + yield generateSfcBlockSection( + scriptSetup, + scriptSetupRanges.defineProps.arg.start, + scriptSetupRanges.defineProps.arg.end, + codeFeatures.navigation, + ); + yield endOfLine; + propTypes.push( + `import('${options.vueCompilerOptions.lib}').${ + options.vueCompilerOptions.target >= 3.3 ? `ExtractPublicPropTypes` : `ExtractPropTypes` + }`, + ); + } + if (scriptSetupRanges.defineEmits || scriptSetupRanges.defineModel.length) { + propTypes.push(`__VLS_EmitProps`); + } + if (options.templateCodegen?.inheritedAttrVars.size) { + propTypes.push(`__VLS_InheritedAttrs`); + } + const emitTypes: string[] = []; if (scriptSetupRanges.defineEmits) { emitTypes.push(`typeof ${scriptSetupRanges.defineEmits.name ?? '__VLS_emit'}`); } @@ -69,28 +89,36 @@ export function* generateScriptSetup( } yield `return {} as {${newLine}` - + ` props: ${ctx.localTypes.PrettifyLocal}<__VLS_OwnProps & __VLS_PublicProps & __VLS_InheritedAttrs> & __VLS_BuiltInPublicProps,${newLine}` - + ` expose(exposed: import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef<${ - scriptSetupRanges.defineExpose ? 'typeof __VLS_exposed' : '{}' - }>): void,${newLine}` - + ` attrs: any,${newLine}` - + ` slots: __VLS_Slots,${newLine}` - + ` emit: ${emitTypes.length ? emitTypes.join(' & ') : `{}`},${newLine}` + + ` props: ${ctx.localTypes.PrettifyLocal}<${propTypes.join(` & `)}> & ${ + 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` + }${endOfLine}` + + ` expose: (exposed: ${ + scriptSetupRanges.defineExpose + ? `import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef` + : `{}` + }) => void${endOfLine}` + + ` attrs: any${endOfLine}` + + ` slots: __VLS_Slots${endOfLine}` + + ` emit: ${emitTypes.length ? emitTypes.join(` & `) : `{}`}${endOfLine}` + `}${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(options, scriptSetup); yield `await (async () => {${newLine}`; yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, 'return'); - yield `})()`; + yield `})()${endOfLine}`; } } @@ -291,40 +319,32 @@ function* generateSetupFunction( } yield generateSfcBlockSection(scriptSetup, nextStart, scriptSetup.content.length, codeFeatures.all); - yield* generateScriptSectionPartiallyEnding(scriptSetup.name, scriptSetup.content.length, '#3632/scriptSetup.vue'); + yield* generatePartiallyEnding(scriptSetup.name, scriptSetup.content.length, '#3632/scriptSetup.vue'); yield* generateMacros(options, ctx); - 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 generateSfcBlockSection( - scriptSetup, - scriptSetupRanges.withDefaults.arg.start, - scriptSetupRanges.withDefaults.arg.end, - codeFeatures.navigation, - ); - yield `)${endOfLine}`; - } + const hasSlots = !!( + scriptSetupRanges.defineSlots + || options.templateCodegen?.slots.length + || options.templateCodegen?.dynamicSlots.length + ); - yield* generateComponentProps(options, ctx, scriptSetup, scriptSetupRanges); - yield* generateModelEmit(scriptSetup, scriptSetupRanges); - const templateCodegenCtx = yield* generateTemplate(options, ctx); - yield* generateComponentSelf(options, ctx, templateCodegenCtx); + yield* generateModels(scriptSetup, scriptSetupRanges); + yield* generatePublicProps(options, ctx, scriptSetup, scriptSetupRanges, hasSlots); + yield* generateTemplate(options, ctx); if (syntax) { - if ( - scriptSetupRanges.defineSlots - || options.templateCodegen?.slots.length - || options.templateCodegen?.dynamicSlots.length - ) { - yield `const __VLS_component = `; + const prefix = syntax === 'return' + ? [`return `] + : generateConstExport(options, scriptSetup); + if (hasSlots) { + 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; } @@ -406,176 +426,163 @@ function* generateDefineWithType( ]; } } + else if (!identifierRegex.test(name)) { + yield [[`const ${defaultName} = `], statement.start, callExp.start]; + yield [ + [ + endOfLine, + generateSfcBlockSection(scriptSetup, statement.start, callExp.start, codeFeatures.all), + defaultName, + ], + statement.end, + statement.end, + ]; + } } -function* generateComponentProps( +function* generatePublicProps( options: ScriptCodegenOptions, ctx: ScriptCodegenContext, scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges, + hasSlots: boolean, ): 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>`; + if (scriptSetupRanges.defineProps?.typeArg && scriptSetupRanges.withDefaults?.arg) { + yield `const __VLS_defaults = `; + yield generateSfcBlockSection( + scriptSetup, + scriptSetupRanges.withDefaults.arg.start, + scriptSetupRanges.withDefaults.arg.end, + codeFeatures.navigation, + ); yield endOfLine; } - if (scriptSetupRanges.defineModel.length) { - yield `const __VLS_defaults = {${newLine}`; - for (const defineModel of scriptSetupRanges.defineModel) { - if (!defineModel.defaultValue) { - continue; - } - const [propName] = getPropAndLocalName(scriptSetup, defineModel); - - yield `'${propName}': `; - yield getRangeText(scriptSetup, defineModel.defaultValue); - yield `,${newLine}`; - } - yield `}${endOfLine}`; - } - - yield `type __VLS_PublicProps = `; - if (scriptSetupRanges.defineSlots && options.vueCompilerOptions.jsxSlots) { - if (ctx.generatedPropsType) { - yield ` & `; - } - ctx.generatedPropsType = true; - yield `${ctx.localTypes.PropsChildren}<__VLS_Slots>`; + const propTypes: string[] = []; + if (options.vueCompilerOptions.jsxSlots && hasSlots) { + 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}`; - for (const defineModel of scriptSetupRanges.defineModel) { - const [propName, localName] = getPropAndLocalName(scriptSetup, defineModel); - - if (defineModel.comments) { - yield scriptSetup.content.slice(defineModel.comments.start, defineModel.comments.end); - yield newLine; - } - - if (defineModel.name) { - yield* generateCamelized( - getRangeText(scriptSetup, defineModel.name), - scriptSetup.name, - defineModel.name.start, - codeFeatures.navigation, - ); - } - else { - yield propName; - } - - yield defineModel.required ? `: ` : `?: `; - yield* generateDefineModelType(scriptSetup, propName, localName, defineModel); - yield `,${newLine}`; - - if (defineModel.modifierType) { - const modifierName = `${propName === 'modelValue' ? 'model' : propName}Modifiers`; - const modifierType = getRangeText(scriptSetup, defineModel.modifierType); - yield `'${modifierName}'?: Partial>,${newLine}`; - } - } - yield `}`; + propTypes.push(`__VLS_ModelProps`); } - if (!ctx.generatedPropsType) { - yield `{}`; + if (propTypes.length) { + ctx.generatedPropsType = true; + yield `type __VLS_PublicProps = ${propTypes.join(` & `)}${endOfLine}`; } - yield endOfLine; } -function* generateModelEmit( +function* generateModels( scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges, ): Generator { - if (scriptSetupRanges.defineModel.length) { - yield `type __VLS_ModelEmit = {${newLine}`; - for (const defineModel of scriptSetupRanges.defineModel) { - const [propName, localName] = getPropAndLocalName(scriptSetup, defineModel); - yield `'update:${propName}': [value: `; - yield* generateDefineModelType(scriptSetup, propName, localName, defineModel); - if (!defineModel.required && !defineModel.defaultValue) { - yield ` | undefined`; - } - yield `]${endOfLine}`; + if (!scriptSetupRanges.defineModel.length) { + return; + } + + const defaultCodes: string[] = []; + const propCodes: Generator[] = []; + const emitCodes: Generator[] = []; + + for (const defineModel of scriptSetupRanges.defineModel) { + const propName = defineModel.name + ? camelize(getRangeText(scriptSetup, defineModel.name).slice(1, -1)) + : 'modelValue'; + + let modelType: string; + if (defineModel.type) { + // Infer from defineModel + modelType = getRangeText(scriptSetup, defineModel.type); + } + else if (defineModel.runtimeType && defineModel.localName) { + // Infer from actual prop declaration code + modelType = `typeof ${getRangeText(scriptSetup, defineModel.localName)}['value']`; + } + else if (defineModel.defaultValue && propName) { + // Infer from defineModel({ default: T }) + modelType = `typeof __VLS_defaultModels['${propName}']`; } + else { + modelType = `any`; + } + + if (defineModel.defaultValue) { + defaultCodes.push( + `'${propName}': ${getRangeText(scriptSetup, defineModel.defaultValue)},${newLine}`, + ); + } + + propCodes.push(generateModelProp(scriptSetup, defineModel, propName, modelType)); + emitCodes.push(generateModelEmit(defineModel, propName, modelType)); + } + + if (defaultCodes.length) { + yield `const __VLS_defaultModels = {${newLine}`; + yield* defaultCodes; yield `}${endOfLine}`; - yield `const __VLS_modelEmit = defineEmits<__VLS_ModelEmit>()${endOfLine}`; } + + yield `type __VLS_ModelProps = {${newLine}`; + for (const codes of propCodes) { + yield* codes; + } + yield `}${endOfLine}`; + + yield `type __VLS_ModelEmit = {${newLine}`; + for (const codes of emitCodes) { + yield* codes; + } + yield `}${endOfLine}`; + yield `const __VLS_modelEmit = defineEmits<__VLS_ModelEmit>()${endOfLine}`; } -function* generateDefineModelType( +function* generateModelProp( scriptSetup: NonNullable, - propName: string | undefined, - localName: string | undefined, defineModel: ScriptSetupRanges['defineModel'][number], -) { - if (defineModel.type) { - // Infer from defineModel - yield getRangeText(scriptSetup, defineModel.type); - } - else if (defineModel.runtimeType && localName) { - // Infer from actual prop declaration code - yield `typeof ${localName}['value']`; + propName: string, + modelType: string, +): Generator { + if (defineModel.comments) { + yield scriptSetup.content.slice(defineModel.comments.start, defineModel.comments.end); + yield newLine; } - else if (defineModel.defaultValue && propName) { - // Infer from defineModel({default: T}) - yield `typeof __VLS_defaults['${propName}']`; + + if (defineModel.name) { + yield* generateCamelized( + getRangeText(scriptSetup, defineModel.name), + scriptSetup.name, + defineModel.name.start, + codeFeatures.navigation, + ); } else { - yield `any`; + yield propName; + } + + yield defineModel.required ? `: ` : `?: `; + yield modelType; + yield endOfLine; + + if (defineModel.modifierType) { + const modifierName = `${propName === 'modelValue' ? 'model' : propName}Modifiers`; + const modifierType = getRangeText(scriptSetup, defineModel.modifierType); + yield `'${modifierName}'?: Partial>${endOfLine}`; } } -function getPropAndLocalName( - scriptSetup: NonNullable, +function* generateModelEmit( defineModel: ScriptSetupRanges['defineModel'][number], -) { - const localName = defineModel.localName - ? getRangeText(scriptSetup, defineModel.localName) - : undefined; - const propName = defineModel.name - ? camelize(getRangeText(scriptSetup, defineModel.name).slice(1, -1)) - : 'modelValue'; - return [propName, localName] as const; + propName: string, + modelType: string, +): Generator { + yield `'update:${propName}': [value: `; + yield modelType; + if (!defineModel.required && !defineModel.defaultValue) { + yield ` | undefined`; + } + yield `]${endOfLine}`; } function getRangeText( diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 9963e7a38e..f3a5ba8a0f 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -1,3 +1,5 @@ +import { camelize, capitalize } from '@vue/shared'; +import * as path from 'path-browserify'; import type { Code } from '../../types'; import { codeFeatures } from '../codeFeatures'; import { generateStyleModules } from '../style/modules'; @@ -13,32 +15,89 @@ import type { ScriptCodegenOptions } from './index'; export function* generateTemplate( options: ScriptCodegenOptions, ctx: ScriptCodegenContext, -): Generator { +): Generator { ctx.generatedTemplate = true; - const templateCodegenCtx = createTemplateCodegenContext({ - scriptSetupBindingNames: new Set(), - }); - yield* generateTemplateCtx(options); + yield* generateSelf(options); + yield* generateTemplateCtx(options, ctx); yield* generateTemplateElements(); yield* generateTemplateComponents(options); yield* generateTemplateDirectives(options); - yield* generateTemplateBody(options, templateCodegenCtx); - return templateCodegenCtx; + yield* generateTemplateBody(options, ctx); } -function* generateTemplateCtx(options: ScriptCodegenOptions): Generator { - const exps = []; +function* generateSelf(options: ScriptCodegenOptions): Generator { + 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}`; + } + else if (options.sfc.script?.src || options.scriptRanges?.classBlockEnd) { + yield `let __VLS_self!: typeof import('./${path.basename(options.fileName)}').default${endOfLine}`; + } +} - exps.push(`{} as InstanceType<__VLS_PickNotAny {}>>`); +function* generateTemplateCtx( + options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, +): Generator { + const exps: Code[] = []; if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileName.endsWith(ext))) { exps.push(`globalThis`); } + if (options.sfc.script?.src || options.scriptRanges?.exportDefault || options.scriptRanges?.classBlockEnd) { + exps.push(`{} as InstanceType<__VLS_PickNotAny {}>>`); + } + else { + exps.push(`{} as import('${options.vueCompilerOptions.lib}').ComponentPublicInstance`); + } if (options.sfc.styles.some(style => style.module)) { exps.push(`{} as __VLS_StyleModules`); } + const emitTypes: string[] = []; + if (options.scriptSetupRanges?.defineEmits) { + const { defineEmits } = options.scriptSetupRanges; + emitTypes.push(`typeof ${defineEmits.name ?? `__VLS_emit`}`); + } + if (options.scriptSetupRanges?.defineModel.length) { + 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(` & `)} }`); + } + + 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>` + : `__VLS_Props` + : undefined; + if (props) { + propTypes.push(props); + } + if (options.scriptSetupRanges?.defineModel.length) { + propTypes.push(`__VLS_ModelProps`); + } + 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`); + } + + if (options.scriptSetupRanges && ctx.bindingNames.size) { + exps.push(`{} as __VLS_Bindings`); + } + yield `const __VLS_ctx = `; yield* generateSpreadMerge(exps); yield endOfLine; @@ -68,7 +127,7 @@ function* generateTemplateComponents(options: ScriptCodegenOptions): Generator { +function* generateTemplateDirectives(options: ScriptCodegenOptions): Generator { const types: string[] = [`typeof __VLS_ctx`]; if (options.sfc.script && options.scriptRanges?.exportDefault?.directivesOption) { @@ -90,12 +149,17 @@ export function* generateTemplateDirectives(options: ScriptCodegenOptions): Gene function* generateTemplateBody( options: ScriptCodegenOptions, - templateCodegenCtx: TemplateCodegenContext, + ctx: ScriptCodegenContext, ): Generator { + const templateCodegenCtx = createTemplateCodegenContext({ + scriptSetupBindingNames: new Set(), + }); + yield* generateStyleScopedClasses(options, templateCodegenCtx); yield* generateStyleScopedClassReferences(templateCodegenCtx, true); yield* generateStyleModules(options); yield* generateCssVars(options, templateCodegenCtx); + yield* generateBindings(options, ctx, templateCodegenCtx); if (options.templateCodegen) { yield* options.templateCodegen.codes; @@ -110,11 +174,10 @@ function* generateTemplateBody( } } -function* generateCssVars(options: ScriptCodegenOptions, ctx: TemplateCodegenContext): Generator { - if (!options.sfc.styles.length) { - return; - } - yield `// CSS variable injection ${newLine}`; +function* generateCssVars( + options: ScriptCodegenOptions, + ctx: TemplateCodegenContext, +): Generator { for (const style of options.sfc.styles) { for (const binding of style.bindings) { yield* generateInterpolation( @@ -124,9 +187,41 @@ function* generateCssVars(options: ScriptCodegenOptions, ctx: TemplateCodegenCon codeFeatures.all, binding.text, binding.offset, + `(`, + `)`, ); yield endOfLine; } } - yield `// CSS variable injection end ${newLine}`; +} + +function* generateBindings( + options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, + templateCodegenCtx: TemplateCodegenContext, +): Generator { + if (!options.sfc.scriptSetup || !ctx.bindingNames.size) { + return; + } + + const usageVars = new Set([ + ...options.sfc.template?.ast?.components.flatMap(c => [camelize(c), capitalize(camelize(c))]) ?? [], + ...options.templateCodegen?.accessExternalVariables.keys() ?? [], + ...templateCodegenCtx.accessExternalVariables.keys(), + ]); + + yield `type __VLS_Bindings = __VLS_ProxyRefs<{${newLine}`; + for (const varName of ctx.bindingNames) { + if (!usageVars.has(varName)) { + continue; + } + + const token = Symbol(varName.length); + yield ['', undefined, 0, { __linkedToken: token }]; + yield `${varName}: typeof `; + yield ['', undefined, 0, { __linkedToken: token }]; + yield varName; + yield endOfLine; + } + yield `}>${endOfLine}`; } diff --git a/packages/language-core/lib/codegen/template/context.ts b/packages/language-core/lib/codegen/template/context.ts index c5c7897e10..4f9f2958d0 100644 --- a/packages/language-core/lib/codegen/template/context.ts +++ b/packages/language-core/lib/codegen/template/context.ts @@ -264,7 +264,7 @@ export function createTemplateCodegenContext( }, *generateAutoImportCompletion(): Generator { const all = [...accessExternalVariables.entries()]; - if (!all.some(([_, offsets]) => offsets.size)) { + if (!all.some(([, offsets]) => offsets.size)) { return; } yield `// @ts-ignore${newLine}`; // #2304 diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index 3348c945c2..3ecf690809 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/codegen/template/elementProps.ts b/packages/language-core/lib/codegen/template/elementProps.ts index 4aa75ecbe8..e303be1870 100644 --- a/packages/language-core/lib/codegen/template/elementProps.ts +++ b/packages/language-core/lib/codegen/template/elementProps.ts @@ -372,9 +372,7 @@ function getShouldCamelize( && !options.vueCompilerOptions.htmlAttributes.some(pattern => isMatch(propName, pattern)); } -function getPropsCodeFeatures( - strictPropsCheck: boolean, -): VueCodeInformation { +function getPropsCodeFeatures(strictPropsCheck: boolean): VueCodeInformation { return { ...codeFeatures.withoutHighlightAndCompletion, ...strictPropsCheck diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index f623349b67..fb80082f46 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -22,8 +22,8 @@ export interface TemplateCodegenOptions { destructuredPropNames: Set; templateRefNames: Set; hasDefineSlots?: boolean; - slotsAssignName?: string; propsAssignName?: string; + slotsAssignName?: string; inheritAttrs: boolean; selfComponentName?: string; } @@ -60,24 +60,26 @@ export function* generateTemplate( yield* generateStyleScopedClassReferences(ctx); yield* ctx.generateHoistVariables(); - const speicalTypes = [ + const dollarTypes = [ [slotsPropertyName, yield* generateSlots(options, ctx)], ['$attrs', yield* generateInheritedAttrs(options, ctx)], ['$refs', yield* generateTemplateRefs(options, ctx)], ['$el', yield* generateRootEl(ctx)], - ]; + ].filter(([name]) => ctx.dollarVars.has(name)); - yield `var __VLS_dollars!: {${newLine}`; - for (const [name, type] of speicalTypes) { - yield `${name}: ${type}${endOfLine}`; + if (dollarTypes.length) { + yield `var __VLS_dollars!: {${newLine}`; + for (const [name, type] of dollarTypes) { + yield `${name}: ${type}${endOfLine}`; + } + yield `} & { [K in keyof import('${options.vueCompilerOptions.lib}').ComponentPublicInstance]: unknown }${endOfLine}`; } - yield `} & { [K in keyof import('${options.vueCompilerOptions.lib}').ComponentPublicInstance]: unknown }${endOfLine}`; } function* generateSlots( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, -): Generator { +): Generator { if (!options.hasDefineSlots) { yield `type __VLS_Slots = {}`; for (const { expVar, propsVar } of ctx.dynamicSlots) { @@ -112,7 +114,7 @@ function* generateSlots( function* generateInheritedAttrs( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, -): Generator { +): Generator { yield `type __VLS_InheritedAttrs = ${ ctx.inheritedAttrVars.size ? `Partial<${[...ctx.inheritedAttrVars].map(name => `typeof ${name}`).join(` & `)}>` @@ -140,7 +142,7 @@ function* generateInheritedAttrs( function* generateTemplateRefs( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, -): Generator { +): Generator { yield `type __VLS_TemplateRefs = {}`; for (const [name, refs] of ctx.templateRefs) { yield `${newLine}& `; @@ -172,7 +174,7 @@ function* generateTemplateRefs( function* generateRootEl( ctx: TemplateCodegenContext, -): Generator { +): Generator { yield `type __VLS_RootEl = `; if (ctx.singleRootElTypes.length && !ctx.singleRootNodes.has(null)) { for (const type of ctx.singleRootElTypes) { diff --git a/packages/language-core/lib/codegen/utils/index.ts b/packages/language-core/lib/codegen/utils/index.ts index 6f7ab63253..61e9f5c85b 100644 --- a/packages/language-core/lib/codegen/utils/index.ts +++ b/packages/language-core/lib/codegen/utils/index.ts @@ -1,6 +1,7 @@ import type * as CompilerDOM from '@vue/compiler-dom'; import type * as ts from 'typescript'; import type { Code, SfcBlock, VueCodeInformation } from '../../types'; +import { codeFeatures } from '../codeFeatures'; export const newLine = `\n`; export const endOfLine = `;${newLine}`; @@ -47,3 +48,14 @@ export function generateSfcBlockSection( features, ]; } + +export function* generatePartiallyEnding( + source: string, + end: number, + mark: string, + delimiter = 'debugger', +): Generator { + yield delimiter; + yield [``, source, end, codeFeatures.verification]; + yield `/* PartiallyEnd: ${mark} */${newLine}`; +} 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/scriptRanges.ts b/packages/language-core/lib/parsers/scriptRanges.ts index 1aca002dc0..e2ae3d5cf8 100644 --- a/packages/language-core/lib/parsers/scriptRanges.ts +++ b/packages/language-core/lib/parsers/scriptRanges.ts @@ -1,7 +1,7 @@ import type * as ts from 'typescript'; import type { TextRange } from '../types'; import { getNodeText, getStartEnd } from '../utils/shared'; -import { parseBindingRanges } from './scriptSetupRanges'; +import { getClosestMultiLineCommentRange, parseBindingRanges } from './utils'; export interface ScriptRanges extends ReturnType {} @@ -72,6 +72,10 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc nameOption: nameOptionNode ? _getStartEnd(nameOptionNode) : undefined, inheritAttrsOption, }; + const comment = getClosestMultiLineCommentRange(ts, raw, [], ast); + if (comment) { + exportDefault.start = comment.start; + } } } diff --git a/packages/language-core/lib/parsers/scriptSetupRanges.ts b/packages/language-core/lib/parsers/scriptSetupRanges.ts index 886de3ff79..343e1bf72b 100644 --- a/packages/language-core/lib/parsers/scriptSetupRanges.ts +++ b/packages/language-core/lib/parsers/scriptSetupRanges.ts @@ -1,9 +1,10 @@ import type * as ts from 'typescript'; import type { TextRange, VueCompilerOptions } from '../types'; -import { collectBindingIdentifiers, collectBindingRanges } from '../utils/collectBindings'; +import { collectBindingIdentifiers } from '../utils/collectBindings'; import { getNodeText, getStartEnd } from '../utils/shared'; +import { getClosestMultiLineCommentRange, parseBindingRanges } from './utils'; -const tsCheckReg = /^\/\/\s*@ts-(?:no)?check($|\s)/; +const tsCheckReg = /^\/\/\s*@ts-(?:no)?check(?:$|\s)/; type CallExpressionRange = { callExp: TextRange; @@ -349,7 +350,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), }; } @@ -363,90 +366,6 @@ export function parseScriptSetupRanges( } } -export function parseBindingRanges(ts: typeof import('typescript'), ast: ts.SourceFile) { - const bindings: { - range: TextRange; - moduleName?: string; - isDefaultImport?: boolean; - isNamespace?: boolean; - }[] = []; - - ts.forEachChild(ast, node => { - if (ts.isVariableStatement(node)) { - for (const decl of node.declarationList.declarations) { - const ranges = collectBindingRanges(ts, decl.name, ast); - bindings.push(...ranges.map(range => ({ range }))); - } - } - else if (ts.isFunctionDeclaration(node)) { - if (node.name && ts.isIdentifier(node.name)) { - bindings.push({ - range: _getStartEnd(node.name), - }); - } - } - else if (ts.isClassDeclaration(node)) { - if (node.name) { - bindings.push({ - range: _getStartEnd(node.name), - }); - } - } - else if (ts.isEnumDeclaration(node)) { - bindings.push({ - range: _getStartEnd(node.name), - }); - } - - if (ts.isImportDeclaration(node)) { - const moduleName = _getNodeText(node.moduleSpecifier).slice(1, -1); - - if (node.importClause && !node.importClause.isTypeOnly) { - const { name, namedBindings } = node.importClause; - - if (name) { - bindings.push({ - range: _getStartEnd(name), - moduleName, - isDefaultImport: true, - }); - } - if (namedBindings) { - if (ts.isNamedImports(namedBindings)) { - for (const element of namedBindings.elements) { - if (element.isTypeOnly) { - continue; - } - bindings.push({ - range: _getStartEnd(element.name), - moduleName, - isDefaultImport: element.propertyName?.text === 'default', - }); - } - } - else { - bindings.push({ - range: _getStartEnd(namedBindings.name), - moduleName, - isNamespace: true, - }); - } - } - } - } - }); - - return bindings; - - function _getStartEnd(node: ts.Node) { - return getStartEnd(ts, node, ast); - } - - function _getNodeText(node: ts.Node) { - return getNodeText(ts, node, ast); - } -} - function getStatementRange( ts: typeof import('typescript'), parents: ts.Node[], @@ -470,27 +389,3 @@ function getStatementRange( } return statementRange; } - -function getClosestMultiLineCommentRange( - ts: typeof import('typescript'), - node: ts.Node, - parents: ts.Node[], - ast: ts.SourceFile, -) { - for (let i = parents.length - 1; i >= 0; i--) { - if (ts.isStatement(node)) { - break; - } - node = parents[i]; - } - const comment = ts.getLeadingCommentRanges(ast.text, node.pos) - ?.reverse() - .find(range => range.kind === 3 satisfies ts.SyntaxKind.MultiLineCommentTrivia); - - if (comment) { - return { - start: comment.pos, - end: comment.end, - }; - } -} diff --git a/packages/language-core/lib/parsers/utils.ts b/packages/language-core/lib/parsers/utils.ts new file mode 100644 index 0000000000..f69c1f7709 --- /dev/null +++ b/packages/language-core/lib/parsers/utils.ts @@ -0,0 +1,112 @@ +import type * as ts from 'typescript'; +import type { TextRange } from '../types'; +import { collectBindingRanges } from '../utils/collectBindings'; +import { getNodeText, getStartEnd } from '../utils/shared'; + +export function parseBindingRanges(ts: typeof import('typescript'), ast: ts.SourceFile) { + const bindings: { + range: TextRange; + moduleName?: string; + isDefaultImport?: boolean; + isNamespace?: boolean; + }[] = []; + + ts.forEachChild(ast, node => { + if (ts.isVariableStatement(node)) { + for (const decl of node.declarationList.declarations) { + const ranges = collectBindingRanges(ts, decl.name, ast); + bindings.push(...ranges.map(range => ({ range }))); + } + } + else if (ts.isFunctionDeclaration(node)) { + if (node.name && ts.isIdentifier(node.name)) { + bindings.push({ + range: _getStartEnd(node.name), + }); + } + } + else if (ts.isClassDeclaration(node)) { + if (node.name) { + bindings.push({ + range: _getStartEnd(node.name), + }); + } + } + else if (ts.isEnumDeclaration(node)) { + bindings.push({ + range: _getStartEnd(node.name), + }); + } + + if (ts.isImportDeclaration(node)) { + const moduleName = _getNodeText(node.moduleSpecifier).slice(1, -1); + + if (node.importClause && !node.importClause.isTypeOnly) { + const { name, namedBindings } = node.importClause; + + if (name) { + bindings.push({ + range: _getStartEnd(name), + moduleName, + isDefaultImport: true, + }); + } + if (namedBindings) { + if (ts.isNamedImports(namedBindings)) { + for (const element of namedBindings.elements) { + if (element.isTypeOnly) { + continue; + } + bindings.push({ + range: _getStartEnd(element.name), + moduleName, + isDefaultImport: element.propertyName?.text === 'default', + }); + } + } + else { + bindings.push({ + range: _getStartEnd(namedBindings.name), + moduleName, + isNamespace: true, + }); + } + } + } + } + }); + + return bindings; + + function _getStartEnd(node: ts.Node) { + return getStartEnd(ts, node, ast); + } + + function _getNodeText(node: ts.Node) { + return getNodeText(ts, node, ast); + } +} + +export function getClosestMultiLineCommentRange( + ts: typeof import('typescript'), + node: ts.Node, + parents: ts.Node[], + ast: ts.SourceFile, +) { + for (let i = parents.length - 1; i >= 0; i--) { + if (ts.isStatement(node)) { + break; + } + node = parents[i]; + } + const comment = ts.getLeadingCommentRanges(ast.text, node.pos) + ?.reverse() + .find(range => range.kind === 3 satisfies ts.SyntaxKind.MultiLineCommentTrivia); + + if (comment) { + return { + start: comment.pos, + end: comment.end, + }; + } +} diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index 731f06ce97..1a92f5e681 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; @@ -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(() => { @@ -189,8 +197,8 @@ function createTsx( destructuredPropNames: getSetupDestructuredPropNames(), templateRefNames: getSetupTemplateRefNames(), hasDefineSlots: setupHasDefineSlots(), - slotsAssignName: getSetupSlotsAssignName(), propsAssignName: getSetupPropsAssignName(), + slotsAssignName: getSetupSlotsAssignName(), inheritAttrs: getSetupInheritAttrs(), selfComponentName: getComponentSelfName(), }; @@ -216,7 +224,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 666ef55420..3061be7155 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 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(): {}; data(): {}; props: {}; @@ -22,12 +22,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(): {}; data(): {}; props: {}; @@ -48,23 +49,24 @@ 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<{ - 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"> & ({ +"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<({ foo: number; } & { title?: string; - }) & {}> & import("vue").PublicProps; - expose(exposed: import("vue").ShallowUnwrapRef<{ + }) & { + "onUpdate:title"?: (value: string) => any; + onBar?: (data: number) => any; + }> & import("vue").PublicProps; + expose: (exposed: import("vue").ShallowUnwrapRef<{ baz: number; - }>): void; + }>) => void; attrs: any; slots: { default?: (props: { @@ -75,6 +77,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]; @@ -83,18 +86,18 @@ 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<{ - 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"> & ({ +"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<({ foo: number; } & { title?: string; - }) & {}> & import("vue").PublicProps; - expose(exposed: import("vue").ShallowUnwrapRef<{ + }) & { + "onUpdate:title"?: (value: string) => any; + onBar?: (data: number) => any; + }> & import("vue").PublicProps; + expose: (exposed: import("vue").ShallowUnwrapRef<{ baz: number; - }>): void; + }>) => void; attrs: any; slots: { default?: (props: { @@ -105,6 +108,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]; @@ -113,7 +117,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(): {}; data(): {}; props: {}; @@ -134,6 +138,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; " `; @@ -215,7 +220,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(): {}; data(): {}; props: {}; @@ -236,6 +241,7 @@ declare const _default: import("vue").DefineComponent2<{ __typeEl: any; __defaults: unknown; }>; +declare const _default: typeof __VLS_export; export default _default; " `; @@ -260,7 +266,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 @@ -286,12 +292,13 @@ 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; " `; 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 */ @@ -311,7 +318,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(): {}; data(): {}; props: {}; @@ -326,19 +333,20 @@ declare const _default: import("vue").DefineComponent2<{ directives: {}; provide: {}; expose: string; - __typeProps: __VLS_PublicProps; + __typeProps: __VLS_ModelProps; __typeEmits: __VLS_ModelEmit; __typeRefs: {}; __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(): {}; data(): {}; props: {}; @@ -362,6 +370,7 @@ declare const _default: import("vue").DefineComponent2<{ baz: () => string[]; }; }>; +declare const _default: typeof __VLS_export; export default _default; " `; @@ -370,7 +379,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(): {}; data(): {}; props: {}; @@ -391,12 +400,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: { @@ -440,12 +450,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(): {}; data(): {}; props: { @@ -506,6 +517,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; " `; @@ -639,7 +651,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(): {}; data(): {}; props: {}; @@ -660,7 +672,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 (): { @@ -685,7 +698,7 @@ type __VLS_Slots = { }) => VNode[]; 'no-bind': () => VNode[]; }; -declare const __VLS_component: import("vue").DefineComponent2<{ +declare const __VLS_base: import("vue").DefineComponent2<{ setup(): {}; data(): {}; props: {}; @@ -706,7 +719,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 (): { @@ -734,7 +748,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(): {}; data(): {}; props: {}; @@ -755,7 +769,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..198591bac1 100644 --- a/packages/tsc/tests/typecheck.spec.ts +++ b/packages/tsc/tests/typecheck.spec.ts @@ -12,11 +12,12 @@ 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/#4569/main.vue(1,41): error TS4025: Exported variable '__VLS_export' has or is using private name 'Props'.", "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 ..., {}>'.", - "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/packages/typescript-plugin/lib/requests/getComponentEvents.ts b/packages/typescript-plugin/lib/requests/getComponentEvents.ts index ceea3992f7..70d13fcf0f 100644 --- a/packages/typescript-plugin/lib/requests/getComponentEvents.ts +++ b/packages/typescript-plugin/lib/requests/getComponentEvents.ts @@ -22,7 +22,7 @@ export function getComponentEvents( return []; } - const componentType = getComponentType(ts, languageService, root, components, fileName, tag); + const componentType = getComponentType(ts, languageService, root, components, tag); if (!componentType) { return []; } diff --git a/packages/typescript-plugin/lib/requests/getComponentProps.ts b/packages/typescript-plugin/lib/requests/getComponentProps.ts index e30130a259..6b30932f03 100644 --- a/packages/typescript-plugin/lib/requests/getComponentProps.ts +++ b/packages/typescript-plugin/lib/requests/getComponentProps.ts @@ -30,7 +30,7 @@ export function getComponentProps( return []; } - const componentType = getComponentType(ts, languageService, root, components, fileName, tag); + const componentType = getComponentType(ts, languageService, root, 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 { diff --git a/test-workspace/tsc/passedFixtures/vue3/components/main.vue b/test-workspace/tsc/passedFixtures/vue3/components/main.vue index 2dc0ba4dea..9da3e4898b 100644 --- a/test-workspace/tsc/passedFixtures/vue3/components/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3/components/main.vue @@ -57,7 +57,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, 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(), {