diff --git a/packages/vue-language-core/schemas/vue-tsconfig.schema.json b/packages/vue-language-core/schemas/vue-tsconfig.schema.json index d7f4d33b1c..40c463d80f 100644 --- a/packages/vue-language-core/schemas/vue-tsconfig.schema.json +++ b/packages/vue-language-core/schemas/vue-tsconfig.schema.json @@ -73,6 +73,7 @@ "type": "object", "default": { "defineProps": [ "defineProps" ], + "defineSlots": [ "defineSlots" ], "defineEmits": [ "defineEmits" ], "defineExpose": [ "defineExpose" ], "withDefaults": [ "withDefaults" ] diff --git a/packages/vue-language-core/src/generators/script.ts b/packages/vue-language-core/src/generators/script.ts index bf28d7d662..a2b115d428 100644 --- a/packages/vue-language-core/src/generators/script.ts +++ b/packages/vue-language-core/src/generators/script.ts @@ -28,11 +28,11 @@ export function generate( htmlGen: ReturnType | undefined, compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions, - codeGen: Segment[] = [], + codes: Segment[] = [], mirrorBehaviorMappings: SourceMaps.Mapping<[MirrorBehaviorCapabilities, MirrorBehaviorCapabilities]>[] = [], ) { - // monkey fix for https://github.com/johnsoncodehk/volar/pull/2113 + //#region monkey fix: https://github.com/johnsoncodehk/volar/pull/2113 const sfc = { script: _sfc.script, scriptSetup: _sfc.scriptSetup, @@ -56,51 +56,48 @@ export function generate( emitsTypeArg: undefined, emitsTypeNums: 0, exposeRuntimeArg: undefined, - exposeTypeArg: undefined, importSectionEndOffset: 0, notOnTopTypeExports: [], + defineProps: undefined, propsAssignName: undefined, propsRuntimeArg: undefined, propsTypeArg: undefined, + slotsTypeArg: undefined, typeBindings: [], withDefaultsArg: undefined, }; } + //#endregion const bypassDefineComponent = lang === 'js' || lang === 'jsx'; const vueLibName = getVueLibraryName(vueCompilerOptions.target); - const usedTypes = { + const usedHelperTypes = { DefinePropsToOptions: false, mergePropDefaults: false, ConstructorOverloads: false, WithTemplateSlots: false, }; - const generateFunctionType = !!sfc.scriptSetup?.generic; if (vueCompilerOptions.jsxTemplates && vueCompilerOptions.target >= 3.3) { - codeGen.push(`/** @jsxImportSource vue */\n`); + codes.push(`/** @jsxImportSource vue */\n`); } - writeScriptSrc(); - writeScriptSetupImportsSegment(); - writeScriptContentBeforeExportDefault(); - writeScriptSetupAndTemplate(); - writeScriptSetupTypes(); - writeScriptContentAfterExportDefault(); - writeTemplateIfNoScriptSetup(); + let generatedTemplate = false; - if (!sfc.script && !sfc.scriptSetup) { - codeGen.push([ - 'export default {} as any', - undefined, - [0, 0], - {}, - ]); + generateSrc(); + generateScriptSetupImports(); + generateScriptContentBeforeExportDefault(); + generateScriptSetupAndTemplate(); + generateHelperTypes(); + generateScriptContentAfterExportDefault(); + + if (!generatedTemplate) { + generateTemplate(); } if (sfc.scriptSetup) { // for code action edits - codeGen.push([ + codes.push([ '', 'scriptSetup', sfc.scriptSetup.content.length, @@ -110,7 +107,7 @@ export function generate( // fix https://github.com/johnsoncodehk/volar/issues/1048 // fix https://github.com/johnsoncodehk/volar/issues/435 - const text = toString(codeGen); + const text = toString(codes); const start = text.length - text.trimStart().length; const end = text.trimEnd().length; const extraMappings: SourceMaps.Mapping[] = [ @@ -127,24 +124,24 @@ export function generate( ]; return { - codeGen, + codes, extraMappings, mirrorBehaviorMappings, }; - function writeScriptSetupTypes() { + function generateHelperTypes() { let usedPrettify = false; - if (usedTypes.DefinePropsToOptions) { + if (usedHelperTypes.DefinePropsToOptions) { if (compilerOptions.exactOptionalPropertyTypes) { - codeGen.push(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueLibName}').PropType } : { type: import('${vueLibName}').PropType, required: true } };\n`); + codes.push(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueLibName}').PropType } : { type: import('${vueLibName}').PropType, required: true } };\n`); } else { - codeGen.push(`type __VLS_NonUndefinedable = T extends undefined ? never : T;\n`); - codeGen.push(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueLibName}').PropType<__VLS_NonUndefinedable> } : { type: import('${vueLibName}').PropType, required: true } };\n`); + codes.push(`type __VLS_NonUndefinedable = T extends undefined ? never : T;\n`); + codes.push(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueLibName}').PropType<__VLS_NonUndefinedable> } : { type: import('${vueLibName}').PropType, required: true } };\n`); } } - if (usedTypes.mergePropDefaults) { - codeGen.push(`type __VLS_WithDefaults = { + if (usedHelperTypes.mergePropDefaults) { + codes.push(`type __VLS_WithDefaults = { // use 'keyof Pick' instead of 'keyof P' to keep props jsdoc [K in keyof Pick]: K extends keyof D ? __VLS_Prettify = __VLS_Prettify<(U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never>;\n'); + codes.push('type __VLS_UnionToIntersection = __VLS_Prettify<(U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never>;\n'); usedPrettify = true; if (scriptSetupRanges && scriptSetupRanges.emitsTypeNums !== -1) { - codeGen.push(genConstructorOverloads('__VLS_ConstructorOverloads', scriptSetupRanges.emitsTypeNums)); + codes.push(genConstructorOverloads('__VLS_ConstructorOverloads', scriptSetupRanges.emitsTypeNums)); } else { - codeGen.push(genConstructorOverloads('__VLS_ConstructorOverloads')); + codes.push(genConstructorOverloads('__VLS_ConstructorOverloads')); } } - if (usedTypes.WithTemplateSlots) { - codeGen.push(`type __VLS_WithTemplateSlots = T & { new(): { $slots: S } };\n`); + if (usedHelperTypes.WithTemplateSlots) { + codes.push(`type __VLS_WithTemplateSlots = T & { new(): { + $slots: S; + $props: { [K in keyof JSX.ElementChildrenAttribute]: S; }; + } };\n`); } if (usedPrettify) { - codeGen.push(`type __VLS_Prettify = { [K in keyof T]: T[K]; } & {};\n`); + codes.push(`type __VLS_Prettify = { [K in keyof T]: T[K]; } & {};\n`); } } - function writeScriptSrc() { + function generateSrc() { if (!sfc.script?.src) return; @@ -182,8 +182,8 @@ export function generate( if (!src.endsWith('.js') && !src.endsWith('.jsx')) src = src + '.js'; - codeGen.push(`export * from `); - codeGen.push([ + codes.push(`export * from `); + codes.push([ `'${src}'`, 'script', [sfc.script.srcOffset - 1, sfc.script.srcOffset + sfc.script.src.length + 1], @@ -212,16 +212,16 @@ export function generate( }, }, ]); - codeGen.push(`;\n`); - codeGen.push(`export { default } from '${src}';\n`); + codes.push(`;\n`); + codes.push(`export { default } from '${src}';\n`); } - function writeScriptContentBeforeExportDefault() { + function generateScriptContentBeforeExportDefault() { if (!sfc.script) return; if (!!sfc.scriptSetup && scriptRanges?.exportDefault) { // fix https://github.com/johnsoncodehk/volar/issues/1127 - codeGen.push([ + codes.push([ '', 'scriptSetup', 0, @@ -236,9 +236,9 @@ export function generate( } if (isExportRawObject && vueCompilerOptions.optionsWrapper.length && scriptRanges?.exportDefault) { addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start); - codeGen.push(vueCompilerOptions.optionsWrapper[0]); + codes.push(vueCompilerOptions.optionsWrapper[0]); addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end); - codeGen.push(vueCompilerOptions.optionsWrapper[1]); + codes.push(vueCompilerOptions.optionsWrapper[1]); addVirtualCode('script', scriptRanges.exportDefault.expression.end, sfc.script.content.length); } else { @@ -246,7 +246,7 @@ export function generate( } } } - function writeScriptContentAfterExportDefault() { + function generateScriptContentAfterExportDefault() { if (!sfc.script) return; @@ -254,27 +254,7 @@ export function generate( addVirtualCode('script', scriptRanges.exportDefault.end, sfc.script.content.length); } } - function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end?: number) { - codeGen.push([ - sfc[vueTag]!.content.substring(start, end), - vueTag, - start, - FileRangeCapabilities.full, // diagnostic also working for setup() returns unused in template checking - ]); - } - function addExtraReferenceVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { - codeGen.push([ - sfc[vueTag]!.content.substring(start, end), - vueTag, - start, - { - references: true, - definition: true, - rename: true, - }, - ]); - } - function writeScriptSetupImportsSegment() { + function generateScriptSetupImports() { if (!sfc.scriptSetup) return; @@ -282,318 +262,357 @@ export function generate( if (!scriptSetupRanges) return; - codeGen.push([ + codes.push([ sfc.scriptSetup.content.substring(0, scriptSetupRanges.importSectionEndOffset), 'scriptSetup', 0, FileRangeCapabilities.full, ]); } - function writeTemplateIfNoScriptSetup() { + function generateScriptSetupAndTemplate() { - if (!sfc.scriptSetup) { - writeTemplate(); + if (!sfc.scriptSetup || !scriptSetupRanges) { + return; } - } - function writeScriptSetupAndTemplate() { - if (sfc.scriptSetup && scriptSetupRanges) { + if (!scriptRanges?.exportDefault) { + // fix https://github.com/johnsoncodehk/volar/issues/1127 + codes.push([ + '', + 'scriptSetup', + 0, + { diagnostic: true }, + ]); + codes.push('export default '); + } - if (!scriptRanges?.exportDefault) { - // fix https://github.com/johnsoncodehk/volar/issues/1127 - codeGen.push([ - '', - 'scriptSetup', - 0, - { diagnostic: true }, - ]); - codeGen.push('export default '); + if (sfc.scriptSetup.generic) { + codes.push(`(<`); + codes.push([ + sfc.scriptSetup.generic, + sfc.scriptSetup.name, + sfc.scriptSetup.genericOffset, + FileRangeCapabilities.full, + ]); + if (!sfc.scriptSetup.generic.endsWith(',')) { + codes.push(`,`); + } + codes.push(`>`); + codes.push('(\n'); + if (scriptSetupRanges.propsRuntimeArg && scriptSetupRanges.defineProps) { + codes.push(`__VLS_props = (() => {\n`); + codes.push(`const __VLS_return = (await import('vue')).`); + addVirtualCode('scriptSetup', scriptSetupRanges.defineProps.start, scriptSetupRanges.defineProps.end); + codes.push(`;\n`); + codes.push(`return {} as typeof __VLS_return & import('vue').VNodeProps;\n`); + codes.push(`})()`); } - codeGen.push('('); - if (generateFunctionType && sfc.scriptSetup.generic) { - codeGen.push(`<`); - codeGen.push([ - sfc.scriptSetup.generic, - sfc.scriptSetup.name, - sfc.scriptSetup.genericOffset, - FileRangeCapabilities.full, - ]); - if (!sfc.scriptSetup.generic.endsWith(',')) { - codeGen.push(`,`); + else { + codes.push(`__VLS_props: import('vue').VNodeProps`); + if (scriptSetupRanges.slotsTypeArg) { + codes.push(` & { [K in keyof JSX.ElementChildrenAttribute]: `); + addVirtualCode('scriptSetup', scriptSetupRanges.slotsTypeArg.start, scriptSetupRanges.slotsTypeArg.end); + codes.push(`; }`); + } + if (scriptSetupRanges.propsTypeArg) { + codes.push(' & '); + addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); } - codeGen.push(`>`); - } - codeGen.push('('); - if (generateFunctionType && scriptSetupRanges.propsTypeArg) { - codeGen.push(`__VLS_props: import('vue').VNodeProps & `); - addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); } - codeGen.push(') => {\n'); - codeGen.push('const __VLS_setup = async () => {\n'); - if (generateFunctionType && scriptSetupRanges.propsTypeArg) { - addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset, scriptSetupRanges.propsTypeArg.start); - codeGen.push('typeof __VLS_props'); - addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.end); + codes.push(',\n'); + codes.push('__VLS_ctx = (() => {\n'); + generateSetupFunction(true); + codes.push('return {\n'); + codes.push('attrs: {} as any,\n'); + codes.push('slots: {} as typeof __VLS_setup extends () => Promise<{ slots: infer T }> ? T : never,\n'); + + //#region emit + codes.push('emit: '); + if (scriptSetupRanges.emitsTypeArg) { + codes.push('{} as '); + addVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); + codes.push(',\n'); + } + else if (scriptSetupRanges.emitsRuntimeArg) { + codes.push(`(await import('vue')).defineEmits(`); + addVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); + codes.push('),\n'); } else { - addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset); + codes.push('{} as any,\n'); } + //#endregion - if (scriptSetupRanges.propsTypeArg && scriptSetupRanges.withDefaultsArg) { - // fix https://github.com/johnsoncodehk/volar/issues/1187 - codeGen.push(`const __VLS_withDefaultsArg = (function (t: T) { return t })(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); - codeGen.push(`);\n`); - } + //#region expose + codes.push('expose(__VLS_exposed: typeof __VLS_setup extends () => Promise<{ exposed: infer T }> ? T : never) { },\n'); + //#endregion - if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.start !== scriptRanges.exportDefault.args.start) { - // use defineComponent() from user space code if it exist - codeGen.push(`const __VLS_publicComponent = `); - addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.args.start); - codeGen.push(`{\n`); - } - else { - codeGen.push(`const __VLS_publicComponent = (await import('${vueLibName}')).defineComponent({\n`); - } + codes.push('};\n'); + codes.push('})(),\n'); + codes.push(') => ({} as JSX.Element & { __ctx?: typeof __VLS_ctx, __props?: typeof __VLS_props }))'); + } + else { + codes.push('(() => {\n'); + generateSetupFunction(false); + codes.push(`return {} as typeof __VLS_setup extends () => Promise ? T : never;\n`); + codes.push(`})()`); + } - if (!bypassDefineComponent) { - if (scriptSetupRanges.propsRuntimeArg || scriptSetupRanges.propsTypeArg) { - codeGen.push(`props: (`); - if (scriptSetupRanges.propsTypeArg) { + if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.end !== scriptRanges.exportDefault.end) { + addVirtualCode('script', scriptRanges.exportDefault.expression.end, scriptRanges.exportDefault.end); + } + codes.push(`;`); + // fix https://github.com/johnsoncodehk/volar/issues/1127 + codes.push([ + '', + 'scriptSetup', + sfc.scriptSetup.content.length, + { diagnostic: true }, + ]); + codes.push(`\n`); + } + function generateSetupFunction(functional: boolean) { - usedTypes.DefinePropsToOptions = true; - codeGen.push(`{} as `); + if (!scriptSetupRanges || !sfc.scriptSetup) { + return; + } - if (scriptSetupRanges.withDefaultsArg) { - usedTypes.mergePropDefaults = true; - codeGen.push(`__VLS_WithDefaults<`); - } + codes.push('const __VLS_setup = async () => {\n'); - codeGen.push(`__VLS_TypePropsToRuntimeProps<`); - if (generateFunctionType) { - codeGen.push(`typeof __VLS_props`); - } - else { - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - } - codeGen.push(`>`); + if (sfc.scriptSetup.generic && scriptSetupRanges.propsRuntimeArg && scriptSetupRanges.defineProps) { + addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset, scriptSetupRanges.defineProps.start); + codes.push('__VLS_props'); + addVirtualCode('scriptSetup', scriptSetupRanges.defineProps.end); + } + else if (sfc.scriptSetup.generic && scriptSetupRanges.propsTypeArg) { + addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset, scriptSetupRanges.propsTypeArg.start); + codes.push('typeof __VLS_props'); + addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.end); + } + else { + addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset); + } - if (scriptSetupRanges.withDefaultsArg) { - codeGen.push(`, typeof __VLS_withDefaultsArg`); - codeGen.push(`>`); - } - } - else if (scriptSetupRanges.propsRuntimeArg) { - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); - } - codeGen.push(`),\n`); - } - if (scriptSetupRanges.emitsTypeArg) { - usedTypes.ConstructorOverloads = true; - codeGen.push(`emits: ({} as __VLS_UnionToIntersection<__VLS_ConstructorOverloads<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); - codeGen.push(`>>),\n`); - } - else if (scriptSetupRanges.emitsRuntimeArg) { - codeGen.push(`emits: (`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); - codeGen.push(`),\n`); - } - } + if (scriptSetupRanges.propsTypeArg && scriptSetupRanges.withDefaultsArg) { + // fix https://github.com/johnsoncodehk/volar/issues/1187 + codes.push(`const __VLS_withDefaultsArg = (function (t: T) { return t })(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); + codes.push(`);\n`); + } - codeGen.push(`setup() {\n`); - codeGen.push(`return {\n`); + if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.start !== scriptRanges.exportDefault.args.start) { + // use defineComponent() from user space code if it exist + codes.push(`const __VLS_publicComponent = `); + addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.args.start); + codes.push(`{\n`); + } + else { + codes.push(`const __VLS_publicComponent = (await import('${vueLibName}')).defineComponent({\n`); + } - if (bypassDefineComponent) { - // fill $props + if (!bypassDefineComponent) { + if (scriptSetupRanges.propsRuntimeArg || scriptSetupRanges.propsTypeArg) { + codes.push(`props: (`); if (scriptSetupRanges.propsTypeArg) { - // NOTE: defineProps is inaccurate for $props - codeGen.push(`$props: (await import('./__VLS_types.js')).makeOptional(defineProps<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - codeGen.push(`>()),\n`); + + usedHelperTypes.DefinePropsToOptions = true; + codes.push(`{} as `); + + if (scriptSetupRanges.withDefaultsArg) { + usedHelperTypes.mergePropDefaults = true; + codes.push(`__VLS_WithDefaults<`); + } + + codes.push(`__VLS_TypePropsToRuntimeProps<`); + if (functional) { + codes.push(`typeof __VLS_props`); + } + else { + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); + } + codes.push(`>`); + + if (scriptSetupRanges.withDefaultsArg) { + codes.push(`, typeof __VLS_withDefaultsArg`); + codes.push(`>`); + } } else if (scriptSetupRanges.propsRuntimeArg) { - // NOTE: defineProps is inaccurate for $props - codeGen.push(`$props: (await import('./__VLS_types.js')).makeOptional(defineProps(`); addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); - codeGen.push(`)),\n`); - } - // fill $emit - if (scriptSetupRanges.emitsAssignName) { - codeGen.push(`$emit: ${scriptSetupRanges.emitsAssignName},\n`); - } - else if (scriptSetupRanges.emitsTypeArg) { - codeGen.push(`$emit: defineEmits<`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); - codeGen.push(`>(),\n`); - } - else if (scriptSetupRanges.emitsRuntimeArg) { - codeGen.push(`$emit: defineEmits(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); - codeGen.push(`),\n`); } + codes.push(`),\n`); } - - if (scriptSetupRanges.exposeTypeArg) { - codeGen.push(`...({} as `); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeTypeArg.start, scriptSetupRanges.exposeTypeArg.end); - codeGen.push(`),\n`); + if (scriptSetupRanges.emitsTypeArg) { + usedHelperTypes.ConstructorOverloads = true; + codes.push(`emits: ({} as __VLS_UnionToIntersection<__VLS_ConstructorOverloads<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); + codes.push(`>>),\n`); } - else if (scriptSetupRanges.exposeRuntimeArg) { - codeGen.push(`...(`); - addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end); - codeGen.push(`),\n`); + else if (scriptSetupRanges.emitsRuntimeArg) { + codes.push(`emits: (`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); + codes.push(`),\n`); } + } - codeGen.push(`};\n`); - codeGen.push(`},\n`); - - if (scriptRanges?.exportDefault?.args) { - addVirtualCode('script', scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1); + codes.push(`setup() {\n`); + codes.push(`return {\n`); + + if (bypassDefineComponent) { + // fill $props + if (scriptSetupRanges.propsTypeArg) { + // NOTE: defineProps is inaccurate for $props + codes.push(`$props: (await import('./__VLS_types.js')).makeOptional(defineProps<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); + codes.push(`>()),\n`); + } + else if (scriptSetupRanges.propsRuntimeArg) { + // NOTE: defineProps is inaccurate for $props + codes.push(`$props: (await import('./__VLS_types.js')).makeOptional(defineProps(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); + codes.push(`)),\n`); + } + // fill $emit + if (scriptSetupRanges.emitsAssignName) { + codes.push(`$emit: ${scriptSetupRanges.emitsAssignName},\n`); + } + else if (scriptSetupRanges.emitsTypeArg) { + codes.push(`$emit: defineEmits<`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); + codes.push(`>(),\n`); + } + else if (scriptSetupRanges.emitsRuntimeArg) { + codes.push(`$emit: defineEmits(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end); + codes.push(`),\n`); } + } - codeGen.push(`});\n`); + if (scriptSetupRanges.exposeRuntimeArg) { + codes.push(`...(`); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end); + codes.push(`),\n`); + } - writeTemplate(); + codes.push(`};\n`); + codes.push(`},\n`); - if (generateFunctionType) { - codeGen.push(`return {} as Omit & Omit, '$slots' | '$emit'>`); - codeGen.push(` & {\n`); - if (scriptSetupRanges.propsTypeArg) { - codeGen.push(`props: typeof __VLS_props,\n`); - } - else { - codeGen.push(`props: InstanceType['$props'],\n`); - } - codeGen.push(`$emit: `); - if (scriptSetupRanges.emitsTypeArg) { - addVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end); - } - else { - codeGen.push(`InstanceType['$emit']`); - } - codeGen.push(`,\n`); - if (htmlGen?.hasSlot) { - codeGen.push(`children: ReturnType,\n`); - } - else { - codeGen.push(`children: {},\n`); - } - codeGen.push(`};\n`); + if (scriptRanges?.exportDefault?.args) { + addVirtualCode('script', scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1); + } + + codes.push(`});\n`); + + generateTemplate(); + + if (functional) { + codes.push('return {\n'); + codes.push('slots: __VLS_template(),\n'); + codes.push('exposed: '); + if (scriptSetupRanges.exposeRuntimeArg) { + addVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end); } else { - if (!vueCompilerOptions.skipTemplateCodegen && htmlGen?.hasSlot) { - usedTypes.WithTemplateSlots = true; - codeGen.push(`return {} as __VLS_WithTemplateSlots>;\n`); - } - else { - codeGen.push(`return {} as typeof __VLS_publicComponent;\n`); - } + codes.push(`{}`); } - codeGen.push(`};\n`); - codeGen.push(`return {} as typeof __VLS_setup extends () => Promise ? T : never;\n`); - codeGen.push(`})`); - if (!generateFunctionType) { - codeGen.push(`({} as any)`); + codes.push(',\n'); + codes.push('};\n'); + } + else { + if (!vueCompilerOptions.skipTemplateCodegen && htmlGen?.hasSlot) { + usedHelperTypes.WithTemplateSlots = true; + codes.push(`return {} as __VLS_WithTemplateSlots>;\n`); } - if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.end !== scriptRanges.exportDefault.end) { - addVirtualCode('script', scriptRanges.exportDefault.expression.end, scriptRanges.exportDefault.end); + else { + codes.push(`return {} as typeof __VLS_publicComponent;\n`); } - codeGen.push(`;`); - // fix https://github.com/johnsoncodehk/volar/issues/1127 - codeGen.push([ - '', - 'scriptSetup', - sfc.scriptSetup.content.length, - { diagnostic: true }, - ]); - - codeGen.push(`\n`); } + codes.push(`};\n`); } - function writeTemplate() { + function generateTemplate() { + + generatedTemplate = true; if (!vueCompilerOptions.skipTemplateCodegen) { - writeExportOptions(); - writeConstNameOption(); + generateExportOptions(); + generateConstNameOption(); + + if (scriptSetupRanges?.slotsTypeArg && sfc.scriptSetup) { + codes.push(`var __VLS_slots!: `); + codes.push([ + sfc.scriptSetup.content.substring(scriptSetupRanges.slotsTypeArg.start, scriptSetupRanges.slotsTypeArg.end), + sfc.scriptSetup.name, + [scriptSetupRanges.slotsTypeArg.start, scriptSetupRanges.slotsTypeArg.end], + FileRangeCapabilities.full, + ]); + codes.push(';\n'); + }; - codeGen.push(`function __VLS_template() {\n`); + codes.push(`function __VLS_template() {\n`); - const templateGened = writeTemplateContext(); + const templateGened = generateTemplateContext(); - codeGen.push(`}\n`); + codes.push(`}\n`); - writeComponentForTemplateUsage(templateGened.cssIds); + generateComponentForTemplateUsage(templateGened.cssIds); } else { - codeGen.push(`function __VLS_template() {\n`); + codes.push(`function __VLS_template() {\n`); const templateUsageVars = [...getTemplateUsageVars()]; - codeGen.push(`// @ts-ignore\n`); - codeGen.push(`[${templateUsageVars.join(', ')}]\n`); - codeGen.push(`return {};\n`); - codeGen.push(`}\n`); + codes.push(`// @ts-ignore\n`); + codes.push(`[${templateUsageVars.join(', ')}]\n`); + codes.push(`return {};\n`); + codes.push(`}\n`); } } - function writeComponentForTemplateUsage(cssIds: Set) { + function generateComponentForTemplateUsage(cssIds: Set) { if (sfc.scriptSetup && scriptSetupRanges) { - codeGen.push(`const __VLS_internalComponent = (await import('${vueLibName}')).defineComponent({\n`); - codeGen.push(`setup() {\n`); - codeGen.push(`return {\n`); + codes.push(`const __VLS_internalComponent = (await import('${vueLibName}')).defineComponent({\n`); + codes.push(`setup() {\n`); + codes.push(`return {\n`); // fill ctx from props if (bypassDefineComponent) { if (scriptSetupRanges.propsAssignName) { - codeGen.push(`...${scriptSetupRanges.propsAssignName},\n`); + codes.push(`...${scriptSetupRanges.propsAssignName},\n`); } else if (scriptSetupRanges.withDefaultsArg && scriptSetupRanges.propsTypeArg) { - codeGen.push(`...withDefaults(defineProps<`); + codes.push(`...withDefaults(defineProps<`); addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end); - codeGen.push(`>(), `); + codes.push(`>(), `); addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end); - codeGen.push(`),\n`); + codes.push(`),\n`); } else if (scriptSetupRanges.propsRuntimeArg) { - codeGen.push(`...defineProps(`); + codes.push(`...defineProps(`); addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsRuntimeArg.start, scriptSetupRanges.propsRuntimeArg.end); - codeGen.push(`),\n`); + codes.push(`),\n`); } } // bindings - const bindingsArr: { - bindings: { start: number, end: number; }[], - content: string, - vueTag: 'script' | 'scriptSetup', - }[] = []; - bindingsArr.push({ - bindings: scriptSetupRanges.bindings, - content: sfc.scriptSetup.content, - vueTag: 'scriptSetup', - }); - if (scriptRanges && sfc.script) { - bindingsArr.push({ - bindings: scriptRanges.bindings, - content: sfc.script.content, - vueTag: 'script', - }); - } const templateUsageVars = getTemplateUsageVars(); - for (const { bindings, content } of bindingsArr) { + for (const [content, bindings] of [ + [sfc.scriptSetup.content, scriptSetupRanges.bindings] as const, + scriptRanges && sfc.script + ? [sfc.script.content, scriptRanges.bindings] as const + : ['', []] as const, + ]) { for (const expose of bindings) { const varName = content.substring(expose.start, expose.end); if (!templateUsageVars.has(varName) && !cssIds.has(varName)) { continue; } - const templateStart = getLength(codeGen); - codeGen.push(varName); - const templateEnd = getLength(codeGen); - codeGen.push(`: `); + const templateStart = getLength(codes); + codes.push(varName); + const templateEnd = getLength(codes); + codes.push(`: `); - const scriptStart = getLength(codeGen); - codeGen.push(varName); - const scriptEnd = getLength(codeGen); - codeGen.push(',\n'); + const scriptStart = getLength(codes); + codes.push(varName); + const scriptEnd = getLength(codes); + codes.push(',\n'); mirrorBehaviorMappings.push({ sourceRange: [scriptStart, scriptEnd], @@ -605,23 +624,23 @@ export function generate( }); } } - codeGen.push(`};\n`); // return { - codeGen.push(`},\n`); // setup() { - codeGen.push(`});\n`); // defineComponent({ + codes.push(`};\n`); // return { + codes.push(`},\n`); // setup() { + codes.push(`});\n`); // defineComponent({ } else if (sfc.script) { - codeGen.push(`let __VLS_internalComponent!: typeof import('./${path.basename(fileName)}')['default'];\n`); + codes.push(`let __VLS_internalComponent!: typeof import('./${path.basename(fileName)}')['default'];\n`); } else { - codeGen.push(`const __VLS_internalComponent = (await import('${vueLibName}')).defineComponent({});\n`); + codes.push(`const __VLS_internalComponent = (await import('${vueLibName}')).defineComponent({});\n`); } } - function writeExportOptions() { - codeGen.push(`\n`); - codeGen.push(`const __VLS_componentsOption = `); + function generateExportOptions() { + codes.push(`\n`); + codes.push(`const __VLS_componentsOption = `); if (sfc.script && scriptRanges?.exportDefault?.componentsOption) { const componentsOption = scriptRanges.exportDefault.componentsOption; - codeGen.push([ + codes.push([ sfc.script.content.substring(componentsOption.start, componentsOption.end), 'script', componentsOption.start, @@ -632,42 +651,40 @@ export function generate( ]); } else { - codeGen.push('{}'); + codes.push('{}'); } - codeGen.push(`;\n`); + codes.push(`;\n`); } - function writeConstNameOption() { - codeGen.push(`\n`); + function generateConstNameOption() { + codes.push(`\n`); if (sfc.script && scriptRanges?.exportDefault?.nameOption) { const nameOption = scriptRanges.exportDefault.nameOption; - codeGen.push(`const __VLS_name = `); - codeGen.push(`${sfc.script.content.substring(nameOption.start, nameOption.end)} as const`); - codeGen.push(`;\n`); + codes.push(`const __VLS_name = `); + codes.push(`${sfc.script.content.substring(nameOption.start, nameOption.end)} as const`); + codes.push(`;\n`); } else if (sfc.scriptSetup) { - codeGen.push(`let __VLS_name!: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`); + codes.push(`let __VLS_name!: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`); } else { - codeGen.push(`const __VLS_name = undefined;\n`); + codes.push(`const __VLS_name = undefined;\n`); } } - function writeTemplateContext() { + function generateTemplateContext() { const useGlobalThisTypeInCtx = fileName.endsWith('.html'); - codeGen.push(`let __VLS_any: any;\n`); - - codeGen.push(`let __VLS_ctx!: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''}`); + codes.push(`let __VLS_ctx!: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''}`); if (sfc.scriptSetup) { - codeGen.push(`InstanceType {}>> & `); + codes.push(`InstanceType {}>> & `); } - codeGen.push(`InstanceType {}>> & {\n`); + codes.push(`InstanceType {}>> & {\n`); /* CSS Module */ for (const cssModule of cssModuleClasses) { - codeGen.push(`${cssModule.style.module}: Record & import('./__VLS_types.js').Prettify<{}`); + codes.push(`${cssModule.style.module}: Record & import('./__VLS_types.js').Prettify<{}`); for (const classNameRange of cssModule.classNameRanges) { - writeCssClassProperty( + generateCssClassProperty( cssModule.index, cssModule.style.content.substring(classNameRange.start + 1, classNameRange.end), classNameRange, @@ -675,23 +692,23 @@ export function generate( false, ); } - codeGen.push('>;\n'); + codes.push('>;\n'); } - codeGen.push(`};\n`); + codes.push(`};\n`); /* Components */ - codeGen.push('/* Components */\n'); - codeGen.push(`let __VLS_localComponents!: NonNullable & typeof __VLS_componentsOption & typeof __VLS_ctx;\n`); - codeGen.push(`let __VLS_otherComponents!: typeof __VLS_localComponents & import('./__VLS_types.js').GlobalComponents;\n`); - codeGen.push(`let __VLS_own!: import('./__VLS_types.js').SelfComponent { ${getSlotsPropertyName(vueCompilerOptions.target ?? 3)}: typeof __VLS_slots })>;\n`); - codeGen.push(`let __VLS_components!: typeof __VLS_otherComponents & Omit;\n`); + codes.push('/* Components */\n'); + codes.push(`let __VLS_localComponents!: NonNullable & typeof __VLS_componentsOption & typeof __VLS_ctx;\n`); + codes.push(`let __VLS_otherComponents!: typeof __VLS_localComponents & import('./__VLS_types.js').GlobalComponents;\n`); + codes.push(`let __VLS_own!: import('./__VLS_types.js').SelfComponent { ${getSlotsPropertyName(vueCompilerOptions.target ?? 3)}: typeof __VLS_slots })>;\n`); + codes.push(`let __VLS_components!: typeof __VLS_otherComponents & Omit;\n`); /* Style Scoped */ - codeGen.push('/* Style Scoped */\n'); - codeGen.push('type __VLS_StyleScopedClasses = {}'); + codes.push('/* Style Scoped */\n'); + codes.push('type __VLS_StyleScopedClasses = {}'); for (const scopedCss of cssScopedClasses) { for (const classNameRange of scopedCss.classNameRanges) { - writeCssClassProperty( + generateCssClassProperty( scopedCss.index, scopedCss.style.content.substring(classNameRange.start + 1, classNameRange.end), classNameRange, @@ -700,30 +717,38 @@ export function generate( ); } } - codeGen.push(';\n'); - codeGen.push('let __VLS_styleScopedClasses!: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[];\n'); + codes.push(';\n'); + codes.push('let __VLS_styleScopedClasses!: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[];\n'); - codeGen.push(`/* CSS variable injection */\n`); - const cssIds = writeCssVars(); - codeGen.push(`/* CSS variable injection end */\n`); + codes.push(`/* CSS variable injection */\n`); + const cssIds = generateCssVars(); + codes.push(`/* CSS variable injection end */\n`); if (htmlGen) { - for (const s of htmlGen.codeGen) { - codeGen.push(s); + for (const s of htmlGen.codes) { + codes.push(s); } } if (!htmlGen) { - codeGen.push(`const __VLS_slots = {};\n`); + codes.push(`// no template\n`); + if (scriptSetupRanges?.slotsTypeArg && sfc.scriptSetup) { + codes.push(`let __VLS_slots!: `); + addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.slotsTypeArg.start, scriptSetupRanges.slotsTypeArg.end); + codes.push(`;\n`); + } + else { + codes.push(`const __VLS_slots = {};\n`); + } } - codeGen.push(`return __VLS_slots;\n`); + codes.push(`return __VLS_slots;\n`); return { cssIds }; - function writeCssClassProperty(styleIndex: number, className: string, classRange: TextRange, propertyType: string, optional: boolean) { - codeGen.push(`\n & { `); - codeGen.push([ + function generateCssClassProperty(styleIndex: number, className: string, classRange: TextRange, propertyType: string, optional: boolean) { + codes.push(`\n & { `); + codes.push([ '', 'style_' + styleIndex, classRange.start, @@ -732,30 +757,30 @@ export function generate( referencesCodeLens: true, }, ]); - codeGen.push(`'`); - codeGen.push([ + codes.push(`'`); + codes.push([ className, 'style_' + styleIndex, [classRange.start, classRange.end], { references: true, rename: { - normalize: beforeCssRename, - apply: doCssRename, + normalize: normalizeCssRename, + apply: applyCssRename, }, }, ]); - codeGen.push(`'`); - codeGen.push([ + codes.push(`'`); + codes.push([ '', 'style_' + styleIndex, classRange.end, {}, ]); - codeGen.push(`${optional ? '?' : ''}: ${propertyType}`); - codeGen.push(` }`); + codes.push(`${optional ? '?' : ''}: ${propertyType}`); + codes.push(` }`); } - function writeCssVars() { + function generateCssVars() { const emptyLocalVars: Record = {}; const identifiers = new Set(); @@ -769,10 +794,10 @@ export function generate( ts.createSourceFile('/a.txt', code, ts.ScriptTarget.ESNext), (frag, fragOffset, onlyForErrorMapping) => { if (fragOffset === undefined) { - codeGen.push(frag); + codes.push(frag); } else { - codeGen.push([ + codes.push([ frag, cssVar.style.name, cssBind.start + fragOffset, @@ -786,7 +811,7 @@ export function generate( identifiers, vueCompilerOptions, ); - codeGen.push(';\n'); + codes.push(';\n'); } } @@ -826,12 +851,32 @@ export function generate( return usageVars; } + function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end?: number) { + codes.push([ + sfc[vueTag]!.content.substring(start, end), + vueTag, + start, + FileRangeCapabilities.full, // diagnostic also working for setup() returns unused in template checking + ]); + } + function addExtraReferenceVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) { + codes.push([ + sfc[vueTag]!.content.substring(start, end), + vueTag, + start, + { + references: true, + definition: true, + rename: true, + }, + ]); + } } -function beforeCssRename(newName: string) { +function normalizeCssRename(newName: string) { return newName.startsWith('.') ? newName.slice(1) : newName; } -function doCssRename(newName: string) { +function applyCssRename(newName: string) { return '.' + newName; } diff --git a/packages/vue-language-core/src/generators/template.ts b/packages/vue-language-core/src/generators/template.ts index 9f629426b4..3c54cdd005 100644 --- a/packages/vue-language-core/src/generators/template.ts +++ b/packages/vue-language-core/src/generators/template.ts @@ -18,6 +18,7 @@ const capabilitiesPresets = { tagReference: { references: true, definition: true, rename: { normalize: undefined, apply: noEditApply } } satisfies FileRangeCapabilities, attr: { hover: true, diagnostic: true, references: true, definition: true, rename: true } satisfies FileRangeCapabilities, attrReference: { references: true, definition: true, rename: true } satisfies FileRangeCapabilities, + slotProp: { references: true, definition: true, rename: true, diagnostic: true } satisfies FileRangeCapabilities, scopedClassName: { references: true, definition: true, rename: true, completion: true } satisfies FileRangeCapabilities, slotName: { hover: true, diagnostic: true, references: true, definition: true, completion: true } satisfies FileRangeCapabilities, slotNameExport: { hover: true, diagnostic: true, references: true, definition: true, /* referencesCodeLens: true */ } satisfies FileRangeCapabilities, @@ -53,14 +54,14 @@ export function generate( sourceTemplate: string, sourceLang: string, templateAst: CompilerDOM.RootNode, - hasScriptSetup: boolean, + hasScriptSetupSlots: boolean, cssScopedClasses: string[] = [], ) { const nativeTags = new Set(vueCompilerOptions.nativeTags); - const codeGen: Segment[] = []; - const formatCodeGen: Segment[] = []; - const cssCodeGen: Segment[] = []; + const codes: Segment[] = []; + const formatCodes: Segment[] = []; + const cssCodes: Segment[] = []; const slots = new Map, (_: typeof ${slot.varName}) => any>> &\n`); + codes.push(`Partial, (_: typeof ${slot.varName}) => any>> &\n`); } - codeGen.push(`{\n`); + codes.push(`{\n`); for (const [name, slot] of slots) { hasSlot = true; writeObjectProperty( @@ -114,19 +119,20 @@ export function generate( slot.loc, // TODO: SourceMaps.MappingKind.Expand { ...capabilitiesPresets.slotNameExport, - referencesCodeLens: hasScriptSetup, + referencesCodeLens: true, }, slot.nodeLoc, ); - codeGen.push(`?(_: typeof ${slot.varName}): any,\n`); + codes.push(`?(_: typeof ${slot.varName}): any,\n`); } - codeGen.push(`};\n`); + codes.push(`}`); + codes.push(`;\n`); } function writeStyleScopedClasses() { - codeGen.push(`if (typeof __VLS_styleScopedClasses === 'object' && !Array.isArray(__VLS_styleScopedClasses)) {\n`); + codes.push(`if (typeof __VLS_styleScopedClasses === 'object' && !Array.isArray(__VLS_styleScopedClasses)) {\n`); for (const { className, offset } of scopedClasses) { - codeGen.push(`__VLS_styleScopedClasses[`); + codes.push(`__VLS_styleScopedClasses[`); writeCodeWithQuotes( className, offset, @@ -135,15 +141,15 @@ export function generate( displayWithLink: cssScopedClassesSet.has(className), }, ); - codeGen.push(`];\n`); + codes.push(`];\n`); } - codeGen.push('}\n'); + codes.push('}\n'); } function writeComponentVars() { const data: Record = {}; - codeGen.push(`let __VLS_templateComponents!: {}\n`); + codes.push(`let __VLS_templateComponents!: {}\n`); for (const tagName in tagNames) { @@ -162,12 +168,12 @@ export function generate( ]); const varName = validTsVar.test(tagName) ? tagName : capitalize(camelize(tagName.replace(/:/g, '-'))); - codeGen.push(`& import('./__VLS_types.js').WithComponent<'${varName}', typeof __VLS_components, ${[...names].map(name => `'${name}'`).join(', ')}>\n`); + codes.push(`& import('./__VLS_types.js').WithComponent<'${varName}', typeof __VLS_components, ${[...names].map(name => `'${name}'`).join(', ')}>\n`); data[tagName] = varName; } - codeGen.push(`;\n`); + codes.push(`;\n`); for (const tagName in tagNames) { @@ -186,7 +192,7 @@ export function generate( for (const name of names) { for (const tagRange of tagRanges) { - codeGen.push('__VLS_components'); + codes.push('__VLS_components'); writePropertyAccess( name, tagRange, @@ -198,15 +204,15 @@ export function generate( }, }, ); - codeGen.push(';'); + codes.push(';'); } } - codeGen.push('\n'); + codes.push('\n'); - codeGen.push('// @ts-ignore\n'); // #2304 - codeGen.push(`[`); + codes.push('// @ts-ignore\n'); // #2304 + codes.push(`[`); for (const tagRange of tagRanges) { - codeGen.push([ + codes.push([ varName, 'template', tagRange, @@ -217,9 +223,9 @@ export function generate( }, }, ]); - codeGen.push(','); + codes.push(','); } - codeGen.push(`];\n`); + codes.push(`];\n`); } return data; @@ -323,17 +329,17 @@ export function generate( const branch = node.branches[i]; if (i === 0) - codeGen.push('if'); + codes.push('if'); else if (branch.condition) - codeGen.push('else if'); + codes.push('else if'); else - codeGen.push('else'); + codes.push('else'); let addedBlockCondition = false; if (branch.condition?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - codeGen.push(` `); - const beforeCodeLength = codeGen.length; + codes.push(` `); + const beforeCodeLength = codes.length; writeInterpolation( branch.condition.content, branch.condition.loc.start.offset, @@ -342,23 +348,23 @@ export function generate( ')', branch.condition.loc, ); - const afterCodeLength = codeGen.length; + const afterCodeLength = codes.length; appendFormattingCode( branch.condition.content, branch.condition.loc.start.offset, formatBrackets.empty, ); - blockConditions.push(muggle.toString(codeGen.slice(beforeCodeLength, afterCodeLength))); + blockConditions.push(muggle.toString(codes.slice(beforeCodeLength, afterCodeLength))); addedBlockCondition = true; } - codeGen.push(` {\n`); + codes.push(` {\n`); writeInterpolationVarsExtraCompletion(); for (const childNode of branch.children) { visitNode(childNode, parentEl); } - codeGen.push('}\n'); + codes.push('}\n'); if (addedBlockCondition) { blockConditions[blockConditions.length - 1] = `!(${blockConditions[blockConditions.length - 1]})`; @@ -374,7 +380,7 @@ export function generate( const leftExpressionText = leftExpressionRange ? node.loc.source.substring(leftExpressionRange.start - node.loc.start.offset, leftExpressionRange.end - node.loc.start.offset) : undefined; const forBlockVars: string[] = []; - codeGen.push(`for (const [`); + codes.push(`for (const [`); if (leftExpressionRange && leftExpressionText) { const collectAst = createTsAst(node.parseResult, `const [${leftExpressionText}]`); @@ -383,10 +389,10 @@ export function generate( for (const varName of forBlockVars) localVars[varName] = (localVars[varName] ?? 0) + 1; - codeGen.push([leftExpressionText, 'template', leftExpressionRange.start, capabilitiesPresets.all]); + codes.push([leftExpressionText, 'template', leftExpressionRange.start, capabilitiesPresets.all]); appendFormattingCode(leftExpressionText, leftExpressionRange.start, formatBrackets.square); } - codeGen.push(`] of (await import('./__VLS_types.js')).getVForSourceType`); + codes.push(`] of (await import('./__VLS_types.js')).getVForSourceType`); if (source.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { writeInterpolation( source.content, @@ -402,7 +408,7 @@ export function generate( formatBrackets.empty, ); - codeGen.push(`) {\n`); + codes.push(`) {\n`); writeInterpolationVarsExtraCompletion(); @@ -410,7 +416,7 @@ export function generate( visitNode(childNode, parentEl); } - codeGen.push('}\n'); + codes.push('}\n'); } for (const varName of forBlockVars) @@ -423,7 +429,7 @@ export function generate( // not needed progress } else { - codeGen.push(`// Unprocessed node type: ${node.type} json: ${JSON.stringify(node.loc)}\n`); + codes.push(`// Unprocessed node type: ${node.type} json: ${JSON.stringify(node.loc)}\n`); } }; function visitElementNode(node: CompilerDOM.ElementNode, parentEl: CompilerDOM.ElementNode | undefined) { @@ -438,7 +444,7 @@ export function generate( parentEl = node; } - codeGen.push(`{\n`); + codes.push(`{\n`); const startTagOffset = node.loc.start.offset + sourceTemplate.substring(node.loc.start.offset).indexOf(node.tag); let endTagOffset = !node.isSelfClosing && sourceLang === 'html' ? node.loc.start.offset + node.loc.source.lastIndexOf(node.tag) : undefined; @@ -447,61 +453,125 @@ export function generate( endTagOffset = undefined; } - const tagOffsets = endTagOffset !== undefined ? [startTagOffset, endTagOffset] : [startTagOffset]; - let _unWriteExps: CompilerDOM.SimpleExpressionNode[]; + let propsFailedExps: CompilerDOM.SimpleExpressionNode[] = []; - const _isIntrinsicElement = nativeTags.has(node.tag); - const _isNamespacedTag = node.tag.indexOf('.') >= 0; + const tagOffsets = endTagOffset !== undefined ? [startTagOffset, endTagOffset] : [startTagOffset]; + const isIntrinsicElement = nativeTags.has(node.tag); + const isNamespacedTag = node.tag.indexOf('.') >= 0; + const componentVar = `__VLS_${elementIndex++}`; + const componentInstanceVar = `__VLS_${elementIndex++}`; - if (vueCompilerOptions.jsxTemplates) { + if (isIntrinsicElement) { + codes.push(`const ${componentVar} = (await import('./__VLS_types.js')).asFunctionalComponent(({} as import('./__VLS_types.js').IntrinsicElements)[`); + writeCodeWithQuotes( + node.tag, + tagOffsets[0], + capabilitiesPresets.diagnosticOnly, + ); + codes.push(`]);\n`); + } + else if (isNamespacedTag) { + codes.push(`const ${componentVar} = (await import('./__VLS_types.js')).asFunctionalComponent(${node.tag}, new ${node.tag}({`); + writeProps(node, 'class', 'slots'); + codes.push(`}));\n`);; + } + else { + codes.push(`const ${componentVar} = (await import('./__VLS_types.js')).asFunctionalComponent(`); + codes.push(`__VLS_templateComponents['${componentVars[node.tag] ?? node.tag}'], `); + codes.push(`new __VLS_templateComponents['${componentVars[node.tag] ?? node.tag}']({`); + writeProps(node, 'class', 'slots'); + codes.push(`}));\n`);; + } + + if (!vueCompilerOptions.jsxTemplates) { + for (const offset of tagOffsets) { + if (isIntrinsicElement) { + codes.push(`({} as import('./__VLS_types.js').IntrinsicElements)`); + writePropertyAccess( + node.tag, + offset, + { + ...capabilitiesPresets.tagReference, + ...capabilitiesPresets.tagHover, + }, + ); + codes.push(`;\n`); + } + else if (isNamespacedTag) { + codes.push([ + node.tag, + 'template', + [offset, offset + node.tag.length], + capabilitiesPresets.all, + ]); + codes.push(`;\n`); + } + else { + if (componentVars[node.tag]) { + codes.push(`__VLS_templateComponents.`); + } + codes.push([ + componentVars[node.tag] ?? node.tag, + 'template', + [offset, offset + node.tag.length], + { + ...capabilitiesPresets.tagHover, + ...capabilitiesPresets.diagnosticOnly, + }, + ]); + codes.push(`;\n`); + } + } + } + else { - codeGen.push([ + codes.push([ '', 'template', node.loc.start.offset, capabilitiesPresets.diagnosticOnly, ]); - const tagCapabilities: FileRangeCapabilities = _isIntrinsicElement || _isNamespacedTag ? capabilitiesPresets.all : { + const tagCapabilities: FileRangeCapabilities = isIntrinsicElement || isNamespacedTag ? capabilitiesPresets.all : { ...capabilitiesPresets.diagnosticOnly, ...capabilitiesPresets.tagHover, }; - codeGen.push(`<`); + codes.push(`<`); if (componentVars[node.tag]) { - codeGen.push([ + codes.push([ '', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly, ]); - codeGen.push(`__VLS_templateComponents.`); + codes.push(`__VLS_templateComponents.`); } - codeGen.push([ + codes.push([ componentVars[node.tag] ?? node.tag, 'template', [startTagOffset, startTagOffset + node.tag.length], tagCapabilities, ]); - codeGen.push(` `); - const { unWriteExps } = writeProps(node, 'jsx', 'props'); - _unWriteExps = unWriteExps; + codes.push(` `); + const { propsFailedExps: unWriteExps } = writeProps(node, 'jsx', 'props'); + propsFailedExps = unWriteExps; if (endTagOffset === undefined) { - codeGen.push(`/>`); + codes.push(`/>`); } else { - codeGen.push(`>;\n`); + codes.push(`>;\n`); } // fix https://github.com/johnsoncodehk/volar/issues/705#issuecomment-974773353 @@ -515,100 +585,45 @@ export function generate( else { startTagEnd = node.loc.start.offset + node.loc.source.substring(0, node.loc.source.lastIndexOf('') + 1; } - codeGen.push([ + codes.push([ '', 'template', startTagEnd, capabilitiesPresets.diagnosticOnly, ]); - codeGen.push(`\n`); + codes.push(`\n`); } - else { - - if (_isIntrinsicElement) { - - for (const offset of tagOffsets) { - codeGen.push(`({} as import('./__VLS_types.js').IntrinsicElements)`); - writePropertyAccess( - node.tag, - offset, - { - ...capabilitiesPresets.tagReference, - ...capabilitiesPresets.tagHover, - }, - ); - codeGen.push(`;\n`); - } - - codeGen.push(`(await import('./__VLS_types.js')).asFunctionalComponent(({} as import('./__VLS_types.js').IntrinsicElements)[`); - writeCodeWithQuotes( - node.tag, - tagOffsets[0], - capabilitiesPresets.diagnosticOnly, - ); - codeGen.push(`])`); - } - else if (_isNamespacedTag) { - - for (const offset of tagOffsets) { - codeGen.push([ - node.tag, - 'template', - [offset, offset + node.tag.length], - capabilitiesPresets.all, - ]); - codeGen.push(`;\n`); - } - - codeGen.push(`(await import('./__VLS_types.js')).asFunctionalComponent(${node.tag})`); - } - else { - - if (endTagOffset !== undefined) { - if (componentVars[node.tag]) { - codeGen.push(`__VLS_templateComponents.`); - } - codeGen.push([ - componentVars[node.tag] ?? node.tag, - 'template', - [endTagOffset, endTagOffset + node.tag.length], - { - ...capabilitiesPresets.tagHover, - ...capabilitiesPresets.diagnosticOnly, - }, - ]); - codeGen.push(`;\n`); - } + writeInterpolationVarsExtraCompletion(); - codeGen.push(`(await import('./__VLS_types.js')).asFunctionalComponent(`); - if (componentVars[node.tag]) { - codeGen.push(`__VLS_templateComponents`); - } - writePropertyAccess( - componentVars[node.tag] ?? node.tag, - [startTagOffset, startTagOffset + node.tag.length], - { - ...capabilitiesPresets.tagHover, - ...capabilitiesPresets.diagnosticOnly, - }, - ); - codeGen.push(`)`); + codes.push(`const ${componentInstanceVar} = ${componentVar}(`); + if (vueCompilerOptions.jsxTemplates) { + codes.push(`{ `); + writeProps(node, 'class', 'slots'); + codes.push(`}`); + } + else { + codes.push(['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly]); // diagnostic start + codes.push(`{ `); + propsFailedExps = writeProps(node, 'class', 'props').propsFailedExps; + codes.push(`}`); + codes.push(['', 'template', startTagOffset + node.tag.length, capabilitiesPresets.diagnosticOnly]); // diagnostic end + } + if (parentEl) { + codes.push(', {\n'); + writeChildren(node, parentEl); + codes.push(`});\n`); + } + else { + codes.push(`);\n`); + for (const childNode of node.children) { + visitNode(childNode, undefined); } - - codeGen.push(`(`); - codeGen.push(['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly]); // diagnostic start - codeGen.push(`{ `); - const { unWriteExps } = writeProps(node, 'class', 'props'); - _unWriteExps = unWriteExps; - codeGen.push(`}`); - codeGen.push(['', 'template', startTagOffset + node.tag.length, capabilitiesPresets.diagnosticOnly]); // diagnostic end - codeGen.push(`)`); - codeGen.push(`;\n`); } + writeInterpolationVarsExtraCompletion(); //#region // fix https://github.com/johnsoncodehk/volar/issues/1775 - for (const failedExp of _unWriteExps) { + for (const failedExp of propsFailedExps) { writeInterpolation( failedExp.loc.source, failedExp.loc.start.offset, @@ -625,7 +640,7 @@ export function generate( fb, ); } - codeGen.push(';\n'); + codes.push(';\n'); } writeInlineCss(node); @@ -639,15 +654,15 @@ export function generate( const scopeVar = `__VLS_${elementIndex++}`; const condition = `(await import('./__VLS_types.js')).withScope(__VLS_ctx, ${scopeVar})`; - codeGen.push(`const ${scopeVar} = `); - codeGen.push([ + codes.push(`const ${scopeVar} = `); + codes.push([ vScope.exp.loc.source, 'template', vScope.exp.loc.start.offset, capabilitiesPresets.all, ]); - codeGen.push(';\n'); - codeGen.push(`if (${condition}) {\n`); + codes.push(';\n'); + codes.push(`if (${condition}) {\n`); blockConditions.push(condition); inScope = true; } @@ -655,21 +670,18 @@ export function generate( writeDirectives(node); writeElReferences(node); // if (cssScopedClasses.length) writeClassScoped(node); - writeEvents(node); + writeEvents(node, componentVar, componentInstanceVar); writeSlots(node, startTagOffset); - writeChildren(node, parentEl); if (inScope) { - codeGen.push('}\n'); + codes.push('}\n'); blockConditions.length = originalConditionsNum; } //#endregion - codeGen.push(`}\n`); + codes.push(`}\n`); } - function writeEvents(node: CompilerDOM.ElementNode) { - - let _varComponentInstance: string | undefined; + function writeEvents(node: CompilerDOM.ElementNode, componentVar: string, componentInstanceVar: string) { for (const prop of node.props) { if ( @@ -677,24 +689,11 @@ export function generate( && prop.name === 'on' && prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { - - const varComponentInstance = tryWriteInstance(); - const componentVar = componentVars[node.tag]; - const varInstanceProps = `__VLS_${elementIndex++}`; - const key_2 = camelize('on-' + prop.arg.loc.source); // onClickOutside - - codeGen.push(`type ${varInstanceProps} = `); - if (!varComponentInstance) { - codeGen.push(`import('./__VLS_types.js').IntrinsicElements['${node.tag}'];\n`); - } - else { - codeGen.push(`import('./__VLS_types.js').InstanceProps;\n`);; - } - codeGen.push(`const __VLS_${elementIndex++}: import('./__VLS_types.js').EventObject = {\n`); - { - if (prop.arg.loc.source.startsWith('[') && prop.arg.loc.source.endsWith(']')) { - codeGen.push(`[(`); - writeInterpolation( - prop.arg.loc.source.slice(1, -1), - prop.arg.loc.start.offset + 1, - capabilitiesPresets.all, - '', - '', - prop.arg.loc, - ); - codeGen.push(`)!]`); - } - else { - writeObjectProperty( - prop.arg.loc.source, - prop.arg.loc.start.offset, - capabilitiesPresets.event, - prop.arg.loc, - ); - } - codeGen.push(`: `); - appendExpressionNode(prop); + codes.push(`) };\n`); + codes.push(`${eventVar} = {\n`); + if (prop.arg.loc.source.startsWith('[') && prop.arg.loc.source.endsWith(']')) { + codes.push(`[(`); + writeInterpolation( + prop.arg.loc.source.slice(1, -1), + prop.arg.loc.start.offset + 1, + capabilitiesPresets.all, + '', + '', + prop.arg.loc, + ); + codes.push(`)!]`); + } + else { + writeObjectProperty( + prop.arg.loc.source, + prop.arg.loc.start.offset, + capabilitiesPresets.event, + prop.arg.loc, + ); } - codeGen.push(`};\n`); + codes.push(`: `); + appendExpressionNode(prop); + codes.push(`};\n`); writeInterpolationVarsExtraCompletion(); } else if ( @@ -760,7 +758,7 @@ export function generate( prop.exp.loc.start.offset, formatBrackets.empty, ); - codeGen.push(`;\n`); + codes.push(`;\n`); } function appendExpressionNode(prop: CompilerDOM.DirectiveNode) { @@ -822,40 +820,18 @@ export function generate( ); } else { - codeGen.push(`() => {}`); + codes.push(`() => {}`); } } } writeInterpolationVarsExtraCompletion(); - - function tryWriteInstance() { - - if (!_varComponentInstance) { - const componentVar = componentVars[node.tag]; - - if (componentVar) { - const _varComponentInstanceA = `__VLS_${elementIndex++}`; - const _varComponentInstanceB = `__VLS_${elementIndex++}`; - _varComponentInstance = `__VLS_${elementIndex++}`; - codeGen.push(`const ${_varComponentInstanceA} = new __VLS_templateComponents.${componentVar}({ `); - writeProps(node, 'class', 'slots'); - codeGen.push(`});\n`); - codeGen.push(`const ${_varComponentInstanceB} = __VLS_templateComponents.${componentVar}({ `); - writeProps(node, 'class', 'slots'); - codeGen.push(`});\n`); - codeGen.push(`let ${_varComponentInstance}!: import('./__VLS_types.js').PickNotAny;\n`); - } - } - - return _varComponentInstance; - } } function writeProps(node: CompilerDOM.ElementNode, format: 'jsx' | 'class', mode: 'props' | 'slots') { let styleAttrNum = 0; let classAttrNum = 0; - const unWriteExps: CompilerDOM.SimpleExpressionNode[] = []; + const propsFailedExps: CompilerDOM.SimpleExpressionNode[] = []; if (node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE @@ -895,7 +871,7 @@ export function generate( || (attrNameText === 'name' && node.tag === 'slot') // #2308 ) { if (prop.exp && prop.exp.constType !== CompilerDOM.ConstantTypes.CAN_STRINGIFY) { - unWriteExps.push(prop.exp); + propsFailedExps.push(prop.exp); } continue; } @@ -913,7 +889,7 @@ export function generate( // camelize name writePropStart(isStatic); - codeGen.push([ + codes.push([ '', 'template', prop.loc.start.offset, @@ -978,10 +954,10 @@ export function generate( } } else { - codeGen.push('{}'); + codes.push('{}'); } writePropValueSuffix(isStatic); - codeGen.push([ + codes.push([ '', 'template', prop.loc.end.offset, @@ -1016,7 +992,7 @@ export function generate( ); } else { - codeGen.push('undefined'); + codes.push('undefined'); } writePropValueSuffix(isStatic); writePropEnd(isStatic); @@ -1048,7 +1024,7 @@ export function generate( // camelize name writePropStart(true); - codeGen.push([ + codes.push([ '', 'template', prop.loc.start.offset, @@ -1072,10 +1048,10 @@ export function generate( writeAttrValue(prop.value); } else { - codeGen.push('true'); + codes.push('true'); } writePropValueSuffix(true); - codeGen.push([ + codes.push([ '', 'template', prop.loc.end.offset, @@ -1103,7 +1079,7 @@ export function generate( writeAttrValue(prop.value); } else { - codeGen.push('true'); + codes.push('true'); } writePropValueSuffix(true); writePropEnd(true); @@ -1116,9 +1092,9 @@ export function generate( && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { if (format === 'jsx') - codeGen.push('{...'); + codes.push('{...'); else - codeGen.push('...'); + codes.push('...'); writeInterpolation( prop.exp.content, prop.exp.loc.start.offset, @@ -1136,9 +1112,9 @@ export function generate( ); } if (format === 'jsx') - codeGen.push('} '); + codes.push('} '); else - codeGen.push(', '); + codes.push(', '); } else { // comment this line to avoid affecting comments in prop expressions @@ -1146,11 +1122,11 @@ export function generate( } } - return { unWriteExps }; + return { propsFailedExps }; function writePropName(name: string, isStatic: boolean, sourceRange: number | [number, number], data: FileRangeCapabilities, cacheOn: any) { if (format === 'jsx' && isStatic) { - codeGen.push([ + codes.push([ name, 'template', sourceRange, @@ -1168,34 +1144,34 @@ export function generate( } function writePropValuePrefix(isStatic: boolean) { if (format === 'jsx' && isStatic) { - codeGen.push('={'); + codes.push('={'); } else { - codeGen.push(': ('); + codes.push(': ('); } } function writePropValueSuffix(isStatic: boolean) { if (format === 'jsx' && isStatic) { - codeGen.push('}'); + codes.push('}'); } else { - codeGen.push(')'); + codes.push(')'); } } function writePropStart(isStatic: boolean) { if (format === 'jsx' && !isStatic) { - codeGen.push('{...{'); + codes.push('{...{'); } } function writePropEnd(isStatic: boolean) { if (format === 'jsx' && isStatic) { - codeGen.push(' '); + codes.push(' '); } else if (format === 'jsx' && !isStatic) { - codeGen.push('}} '); + codes.push('}} '); } else { - codeGen.push(', '); + codes.push(', '); } } function getCaps(caps: FileRangeCapabilities): FileRangeCapabilities { @@ -1219,7 +1195,7 @@ export function generate( } function writeAttrValue(attrNode: CompilerDOM.TextNode) { const char = attrNode.loc.source.startsWith("'") ? "'" : '"'; - codeGen.push(char); + codes.push(char); let start = attrNode.loc.start.offset; let end = attrNode.loc.end.offset; let content = attrNode.loc.source; @@ -1231,13 +1207,13 @@ export function generate( end--; content = content.slice(1, -1); } - codeGen.push([ + codes.push([ toUnicodeIfNeed(content), 'template', [start, end], getCaps(capabilitiesPresets.all), ]); - codeGen.push(char); + codes.push(char); } } function writeInlineCss(node: CompilerDOM.ElementNode) { @@ -1255,27 +1231,19 @@ export function generate( const end = prop.arg.loc.source.lastIndexOf(endCrt); const content = prop.arg.loc.source.substring(start, end); - cssCodeGen.push(`${node.tag} { `); - cssCodeGen.push([ + cssCodes.push(`${node.tag} { `); + cssCodes.push([ content, 'template', prop.arg.loc.start.offset + start, capabilitiesPresets.all, ]); - cssCodeGen.push(` }\n`); + cssCodes.push(` }\n`); } } } - function writeChildren(node: CompilerDOM.ElementNode, parentEl: CompilerDOM.ElementNode | undefined) { - - if (!parentEl) { - for (const childNode of node.children) { - visitNode(childNode, undefined); - } - return; - } + function writeChildren(node: CompilerDOM.ElementNode, parentEl: CompilerDOM.ElementNode) { - const componentVar = parentEl ? componentVars[parentEl.tag] : undefined; const slotAndChildNodes: Record = {}; for (const child of node.children) { @@ -1295,38 +1263,15 @@ export function generate( } } - if (componentVar && parentEl) { - const varComponentInstanceA = `__VLS_${elementIndex++}`; - const varComponentInstanceB = `__VLS_${elementIndex++}`; - codeGen.push(`const ${varComponentInstanceA} = new __VLS_templateComponents.${componentVar}({ `); - writeProps(parentEl, 'class', 'slots'); - codeGen.push(`});\n`); - codeGen.push(`const ${varComponentInstanceB} = __VLS_templateComponents.${componentVar}({ `); - writeProps(parentEl, 'class', 'slots'); - codeGen.push(`});\n`); - writeInterpolationVarsExtraCompletion(); - if (vueCompilerOptions.strictTemplates) { - codeGen.push([ - '', - 'template', - parentEl.loc.start.offset, - capabilitiesPresets.diagnosticOnly, - ]); - } - codeGen.push(`(__VLS_any as import('./__VLS_types.js').ExtractComponentSlots>)`); - if (vueCompilerOptions.strictTemplates) { - codeGen.push([ - '', - 'template', - parentEl.loc.end.offset, - capabilitiesPresets.diagnosticOnly, - ]); - } + if (vueCompilerOptions.strictTemplates) { + codes.push(['', 'template', parentEl.loc.start.offset, capabilitiesPresets.diagnosticOnly]); + codes.push(`slots`); + codes.push(['', 'template', parentEl.loc.end.offset, capabilitiesPresets.diagnosticOnly]); } else { - codeGen.push(`(__VLS_any as Record)`); + codes.push(`slots`); } - codeGen.push(` = {\n`); + codes.push(`: {\n`); for (const [slotName, { nodes, slotDir }] of Object.entries(slotAndChildNodes)) { @@ -1342,14 +1287,14 @@ export function generate( : undefined; if (!slotDir || !argRange) { - codeGen.push([ + codes.push([ '', 'template', Math.min(...nodes.map(node => node.loc.start.offset)), { references: true }, ]); - codeGen.push(slotName); - codeGen.push([ + codes.push(slotName); + codes.push([ '', 'template', Math.max(...nodes.map(node => node.loc.end.offset)), @@ -1368,7 +1313,7 @@ export function generate( ); } else { - codeGen.push(`[`); + codes.push(`[`); writeInterpolation( slotName, argRange[0] + 1, @@ -1377,10 +1322,10 @@ export function generate( '', (slotDir.loc as any).slot_name ?? ((slotDir.loc as any).slot_name = {}), ); - codeGen.push(`]`); + codes.push(`]`); writeInterpolationVarsExtraCompletion(); } - codeGen.push(`(`); + codes.push(`(`); const slotBlockVars: string[] = []; @@ -1389,7 +1334,7 @@ export function generate( const collectAst = createTsAst(slotDir, `(${slotDir.exp.content}) => {}`); colletVars(ts, collectAst, slotBlockVars); - codeGen.push([ + codes.push([ slotDir.exp.content, 'template', slotDir.exp.loc.start.offset, @@ -1402,9 +1347,9 @@ export function generate( ); } - codeGen.push(`): any {\n`); + codes.push(`): any {\n`); for (const blockCondition of blockConditions) { - codeGen.push(`if (!(${blockCondition})) return;\n`); + codes.push(`if (!(${blockCondition})) return;\n`); } slotBlockVars.forEach(varName => { localVars[varName] ??= 0; @@ -1416,7 +1361,7 @@ export function generate( slotBlockVars.forEach(varName => { localVars[varName]--; }); - codeGen.push(`},\n`); + codes.push(`},\n`); if (isStatic && slotDir && !slotDir.arg) { @@ -1427,18 +1372,18 @@ export function generate( else if (slotDir.loc.source.startsWith('v-slot:')) offset += 'v-slot:'.length; - codeGen.push(`'`); - codeGen.push([ + codes.push(`'`); + codes.push([ '', 'template', offset, { completion: true }, ]); - codeGen.push(`'/* empty slot name completion */\n`); + codes.push(`'/* empty slot name completion */\n`); } } - codeGen.push(`};\n`); + codes.push(`},\n`); } function writeDirectives(node: CompilerDOM.ElementNode) { for (const prop of node.props) { @@ -1451,14 +1396,14 @@ export function generate( && (prop.name !== 'scope' && prop.name !== 'data') ) { - codeGen.push([ + codes.push([ '', 'template', prop.loc.start.offset, capabilitiesPresets.diagnosticOnly, ]); - codeGen.push(`(await import('./__VLS_types.js')).directiveFunction(__VLS_ctx.`); - codeGen.push([ + codes.push(`(await import('./__VLS_types.js')).directiveFunction(__VLS_ctx.`); + codes.push([ camelize('v-' + prop.name), 'template', [prop.loc.start.offset, prop.loc.start.offset + 'v-'.length + prop.name.length], @@ -1475,7 +1420,7 @@ export function generate( }, ]); identifiers.add(camelize('v-' + prop.name)); - codeGen.push(`)`); + codes.push(`)`); if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { writeInterpolation( prop.exp.content, @@ -1491,13 +1436,13 @@ export function generate( formatBrackets.empty, ); } - codeGen.push([ + codes.push([ '', 'template', prop.loc.end.offset, capabilitiesPresets.diagnosticOnly, ]); - codeGen.push(`;\n`); + codes.push(`;\n`); writeInterpolationVarsExtraCompletion(); } } @@ -1509,7 +1454,7 @@ export function generate( && prop.name === 'ref' && prop.value ) { - codeGen.push(`// @ts-ignore\n`); + codes.push(`// @ts-ignore\n`); writeInterpolation( prop.value.content, prop.value.loc.start.offset + 1, @@ -1518,7 +1463,7 @@ export function generate( ')', prop.value.loc, ); - codeGen.push(`;\n`); + codes.push(`;\n`); writeInterpolationVarsExtraCompletion(); } } @@ -1554,14 +1499,14 @@ export function generate( && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.content === 'class' ) { - codeGen.push(`__VLS_styleScopedClasses = (`); - codeGen.push([ + codes.push(`__VLS_styleScopedClasses = (`); + codes.push([ prop.exp.content, 'template', prop.exp.loc.start.offset, capabilitiesPresets.scopedClassName, ]); - codeGen.push(`);\n`); + codes.push(`);\n`); } } } @@ -1570,19 +1515,30 @@ export function generate( if (node.tag !== 'slot') return; - const varDefaultBind = `__VLS_${elementIndex++}`; - const varBinds = `__VLS_${elementIndex++}`; const varSlot = `__VLS_${elementIndex++}`; - let hasDefaultBind = false; + const slotNameExpNode = getSlotNameExpNode(); + if (hasScriptSetupSlots) { + const slotNameExp = typeof slotNameExpNode === 'object' ? slotNameExpNode.content : slotNameExpNode; + codes.push(['', 'template', node.loc.start.offset, capabilitiesPresets.diagnosticOnly]); + codes.push(`__VLS_slots[`); + codes.push(['', 'template', node.loc.start.offset, capabilitiesPresets.diagnosticOnly]); + codes.push(slotNameExp); + codes.push(['', 'template', node.loc.end.offset, capabilitiesPresets.diagnosticOnly]); + codes.push(`]`); + codes.push(['', 'template', node.loc.end.offset, capabilitiesPresets.diagnosticOnly]); + codes.push(`({\n`); + } + else { + codes.push(`var ${varSlot} = {\n`); + } for (const prop of node.props) { if ( prop.type === CompilerDOM.NodeTypes.DIRECTIVE && !prop.arg && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { - hasDefaultBind = true; - codeGen.push(`const ${varDefaultBind} = `); + codes.push(`...`); writeInterpolation( prop.exp.content, prop.exp.loc.start.offset, @@ -1591,15 +1547,9 @@ export function generate( ')', prop.exp.loc, ); - codeGen.push(`;\n`); - writeInterpolationVarsExtraCompletion(); - break; + codes.push(`,\n`); } - } - - codeGen.push(`const ${varBinds} = {\n`); - for (const prop of node.props) { - if ( + else if ( prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION @@ -1609,7 +1559,7 @@ export function generate( prop.arg.content, [prop.arg.loc.start.offset, prop.arg.loc.end.offset], { - ...capabilitiesPresets.attrReference, + ...capabilitiesPresets.slotProp, rename: { normalize: camelize, apply: getRenameApply(prop.arg.content), @@ -1617,7 +1567,7 @@ export function generate( }, prop.arg.loc, ); - codeGen.push(`: `); + codes.push(`: `); writeInterpolation( prop.exp.content, prop.exp.loc.start.offset, @@ -1626,7 +1576,7 @@ export function generate( ')', prop.exp.loc, ); - codeGen.push(`,\n`); + codes.push(`,\n`); } else if ( prop.type === CompilerDOM.NodeTypes.ATTRIBUTE @@ -1645,36 +1595,30 @@ export function generate( }, prop.loc, ); - codeGen.push(`: (`); - codeGen.push(propValue); - codeGen.push(`),\n`); + codes.push(`: (`); + codes.push(propValue); + codes.push(`),\n`); } } - codeGen.push(`};\n`); + codes.push(hasScriptSetupSlots ? `});\n` : `};\n`); writeInterpolationVarsExtraCompletion(); - if (hasDefaultBind) { - codeGen.push(`var ${varSlot}!: typeof ${varDefaultBind} & typeof ${varBinds};\n`); - } - else { - codeGen.push(`var ${varSlot}!: typeof ${varBinds};\n`); + if (hasScriptSetupSlots) { + return; } - const slotNameExpNode = getSlotNameExpNode(); if (slotNameExpNode) { const varSlotExp = `__VLS_${elementIndex++}`; - const varSlotExp2 = `__VLS_${elementIndex++}`; - codeGen.push(`const ${varSlotExp} = `); + codes.push(`var ${varSlotExp} = `); if (typeof slotNameExpNode === 'string') { - codeGen.push(slotNameExpNode); + codes.push(slotNameExpNode); } else { writeInterpolation(slotNameExpNode.content, undefined, undefined, '(', ')', slotNameExpNode); } - codeGen.push(`;\n`); - codeGen.push(`var ${varSlotExp2}!: typeof ${varSlotExp};\n`); - slotExps.set(varSlotExp2, { + codes.push(`;\n`); + slotExps.set(varSlotExp, { varName: varSlot, }); } @@ -1703,16 +1647,14 @@ export function generate( if (prop2.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { return prop2.exp; } - else { - return `('default' as const)`; - } } } + return `('${getSlotName()}' as const)`; } } function writeObjectProperty(mapCode: string, sourceRange: number | [number, number], data: FileRangeCapabilities, cacheOn: any) { if (validTsVar.test(mapCode)) { - codeGen.push([mapCode, 'template', sourceRange, data]); + codes.push([mapCode, 'template', sourceRange, data]); return 1; } else if (mapCode.startsWith('[') && mapCode.endsWith(']')) { @@ -1733,29 +1675,29 @@ export function generate( } function writePropertyAccess(mapCode: string, sourceRange: number | [number, number], data: FileRangeCapabilities) { if (validTsVar.test(mapCode)) { - codeGen.push(`.`); - codeGen.push([mapCode, 'template', sourceRange, data]); + codes.push(`.`); + codes.push([mapCode, 'template', sourceRange, data]); } else if (mapCode.startsWith('[') && mapCode.endsWith(']')) { - codeGen.push([mapCode, 'template', sourceRange, data]); + codes.push([mapCode, 'template', sourceRange, data]); } else { - codeGen.push(`[`); + codes.push(`[`); writeCodeWithQuotes(mapCode, sourceRange, data); - codeGen.push(`]`); + codes.push(`]`); } } function writeCodeWithQuotes(mapCode: string, sourceRange: number | [number, number], data: FileRangeCapabilities) { - codeGen.push([ + codes.push([ '', 'template', typeof sourceRange === 'number' ? sourceRange : sourceRange[0], data, ]); - codeGen.push(`'`); - codeGen.push([mapCode, 'template', sourceRange, data]); - codeGen.push(`'`); - codeGen.push([ + codes.push(`'`); + codes.push([mapCode, 'template', sourceRange, data]); + codes.push(`'`); + codes.push([ '', 'template', typeof sourceRange === 'number' ? sourceRange : sourceRange[1], @@ -1773,7 +1715,7 @@ export function generate( const ast = createTsAst(cacheOn, prefix + mapCode + suffix); const vars = walkInterpolationFragment(ts, prefix + mapCode + suffix, ast, (frag, fragOffset, isJustForErrorMapping) => { if (fragOffset === undefined) { - codeGen.push(frag); + codes.push(frag); } else { fragOffset -= prefix.length; @@ -1784,12 +1726,12 @@ export function generate( frag = frag.substring(0, frag.length - overLength); } if (fragOffset < 0) { - codeGen.push(frag.substring(0, -fragOffset)); + codes.push(frag.substring(0, -fragOffset)); frag = frag.substring(-fragOffset); fragOffset = 0; } if (sourceOffset !== undefined && data !== undefined) { - codeGen.push([ + codes.push([ frag, 'template', sourceOffset + fragOffset, @@ -1799,9 +1741,9 @@ export function generate( ]); } else { - codeGen.push(frag); + codes.push(frag); } - codeGen.push(addSuffix); + codes.push(addSuffix); } }, localVars, identifiers, vueCompilerOptions); if (sourceOffset !== undefined) { @@ -1818,22 +1760,22 @@ export function generate( if (!tempVars.length) return; - codeGen.push('// @ts-ignore\n'); // #2304 - codeGen.push('['); + codes.push('// @ts-ignore\n'); // #2304 + codes.push('['); for (const _vars of tempVars) { for (const v of _vars) { - codeGen.push([v.text, 'template', v.offset, { completion: { additional: true } }]); - codeGen.push(','); + codes.push([v.text, 'template', v.offset, { completion: { additional: true } }]); + codes.push(','); } } - codeGen.push('];\n'); + codes.push('];\n'); tempVars.length = 0; } function appendFormattingCode(mapCode: string, sourceOffset: number, formatWrapper: [string, string]) { - formatCodeGen.push(formatWrapper[0]); - formatCodeGen.push([mapCode, 'template', sourceOffset, {}]); - formatCodeGen.push(formatWrapper[1]); - formatCodeGen.push(`\n`); + formatCodes.push(formatWrapper[0]); + formatCodes.push([mapCode, 'template', sourceOffset, {}]); + formatCodes.push(formatWrapper[1]); + formatCodes.push(`\n`); } }; diff --git a/packages/vue-language-core/src/parsers/scriptSetupRanges.ts b/packages/vue-language-core/src/parsers/scriptSetupRanges.ts index 3338dc73fd..c36db5fca1 100644 --- a/packages/vue-language-core/src/parsers/scriptSetupRanges.ts +++ b/packages/vue-language-core/src/parsers/scriptSetupRanges.ts @@ -14,13 +14,14 @@ export function parseScriptSetupRanges( let importSectionEndOffset = 0; let withDefaultsArg: TextRange | undefined; let propsAssignName: string | undefined; + let defineProps: TextRange | undefined; let propsRuntimeArg: TextRange | undefined; let propsTypeArg: TextRange | undefined; + let slotsTypeArg: TextRange | undefined; let emitsAssignName: string | undefined; let emitsRuntimeArg: TextRange | undefined; let emitsTypeArg: TextRange | undefined; let exposeRuntimeArg: TextRange | undefined; - let exposeTypeArg: TextRange | undefined; let emitsTypeNums = -1; const bindings = parseBindingRanges(ts, ast, false); @@ -51,15 +52,16 @@ export function parseScriptSetupRanges( bindings, typeBindings, withDefaultsArg, + defineProps, propsAssignName, propsRuntimeArg, propsTypeArg, + slotsTypeArg, emitsAssignName, emitsRuntimeArg, emitsTypeArg, emitsTypeNums, exposeRuntimeArg, - exposeTypeArg, }; function _getStartEnd(node: ts.Node) { @@ -73,9 +75,13 @@ export function parseScriptSetupRanges( const callText = node.expression.getText(ast); if ( vueCompilerOptions.macros.defineProps.includes(callText) + || vueCompilerOptions.macros.defineSlots.includes(callText) || vueCompilerOptions.macros.defineEmits.includes(callText) || vueCompilerOptions.macros.defineExpose.includes(callText) ) { + if (vueCompilerOptions.macros.defineProps.includes(callText)) { + defineProps = _getStartEnd(node); + } if (node.arguments.length) { const runtimeArg = node.arguments[0]; if (vueCompilerOptions.macros.defineProps.includes(callText)) { @@ -102,6 +108,9 @@ export function parseScriptSetupRanges( propsAssignName = parent.name.getText(ast); } } + if (vueCompilerOptions.macros.defineSlots.includes(callText)) { + slotsTypeArg = _getStartEnd(typeArg); + } else if (vueCompilerOptions.macros.defineEmits.includes(callText)) { emitsTypeArg = _getStartEnd(typeArg); if (ts.isTypeLiteralNode(typeArg)) { @@ -111,9 +120,6 @@ export function parseScriptSetupRanges( emitsAssignName = parent.name.getText(ast); } } - else if (vueCompilerOptions.macros.defineExpose.includes(callText)) { - exposeTypeArg = _getStartEnd(typeArg); - } } } else if (vueCompilerOptions.macros.withDefaults.includes(callText)) { diff --git a/packages/vue-language-core/src/plugins/file-md.ts b/packages/vue-language-core/src/plugins/file-md.ts index 365f68e135..f77beaef9d 100644 --- a/packages/vue-language-core/src/plugins/file-md.ts +++ b/packages/vue-language-core/src/plugins/file-md.ts @@ -22,13 +22,13 @@ const plugin: VueLanguagePlugin = () => { .replace(/\\\<[\s\S]+?\>\n?/g, match => ' '.repeat(match.length)); const sfcBlockReg = /\<(script|style)\b[\s\S]*?\>([\s\S]*?)\<\/\1\>/g; - const codeGen: Segment[] = []; + const codes: Segment[] = []; for (const match of content.matchAll(sfcBlockReg)) { if (match.index !== undefined) { const matchText = match[0]; - codeGen.push([matchText, undefined, match.index]); - codeGen.push('\n\n'); + codes.push([matchText, undefined, match.index]); + codes.push('\n\n'); content = content.substring(0, match.index) + ' '.repeat(matchText.length) + content.substring(match.index + matchText.length); } } @@ -39,12 +39,12 @@ const plugin: VueLanguagePlugin = () => { // [foo](http://foo.com) .replace(/\[[\s\S]*?\]\([\s\S]*?\)/g, match => ' '.repeat(match.length)); - codeGen.push(''); + codes.push(''); - const file2VueSourceMap = new SourceMap(buildMappings(codeGen)); - const sfc = parse(toString(codeGen)); + const file2VueSourceMap = new SourceMap(buildMappings(codes)); + const sfc = parse(toString(codes)); if (sfc.descriptor.template) { transformRange(sfc.descriptor.template); diff --git a/packages/vue-language-core/src/plugins/vue-tsx.ts b/packages/vue-language-core/src/plugins/vue-tsx.ts index 1ffbee5323..5c4d466604 100644 --- a/packages/vue-language-core/src/plugins/vue-tsx.ts +++ b/packages/vue-language-core/src/plugins/vue-tsx.ts @@ -1,4 +1,4 @@ -import { computed } from '@vue/reactivity'; +import { computed, shallowRef as ref } from '@vue/reactivity'; import { generate as genScript } from '../generators/script'; import * as templateGen from '../generators/template'; import { parseScriptRanges } from '../parsers/scriptRanges'; @@ -50,7 +50,7 @@ const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOption }; const tsx = _tsx.tsxGen.value; if (tsx) { - embeddedFile.content = [...tsx.codeGen]; + embeddedFile.content = [...tsx.codes]; embeddedFile.extraMappings = [...tsx.extraMappings]; embeddedFile.mirrorBehaviorMappings = [...tsx.mirrorBehaviorMappings]; } @@ -68,7 +68,7 @@ const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOption }; if (_tsx.htmlGen.value) { - embeddedFile.content = [..._tsx.htmlGen.value.formatCodeGen]; + embeddedFile.content = [..._tsx.htmlGen.value.formatCodes]; } for (const cssVar of _tsx.cssVars.value) { @@ -90,7 +90,7 @@ const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOption embeddedFile.parentFileName = fileName + '.template.' + sfc.template?.lang; if (_tsx.htmlGen.value) { - embeddedFile.content = [..._tsx.htmlGen.value.cssCodeGen]; + embeddedFile.content = [..._tsx.htmlGen.value.cssCodes]; } } }, @@ -138,9 +138,7 @@ const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOption })); const htmlGen = computed(() => { - const templateAst = _sfc.templateAst; - - if (!templateAst) + if (!_sfc.templateAst) return; return templateGen.generate( @@ -148,25 +146,29 @@ const plugin: VueLanguagePlugin = ({ modules, vueCompilerOptions, compilerOption vueCompilerOptions, _sfc.template?.content ?? '', _sfc.template?.lang ?? 'html', - templateAst, - !!_sfc.scriptSetup, + _sfc.templateAst, + hasScriptSetupSlots.value, Object.values(cssScopedClasses.value).map(style => style.classNames).flat(), ); }); - const tsxGen = computed(() => genScript( - ts, - fileName, - _sfc, - lang.value, - scriptRanges.value, - scriptSetupRanges.value, - cssVars.value, - cssModuleClasses.value, - cssScopedClasses.value, - htmlGen.value, - compilerOptions, - vueCompilerOptions, - )); + const hasScriptSetupSlots = ref(false); // remove when https://github.com/vuejs/core/pull/5912 merged + const tsxGen = computed(() => { + hasScriptSetupSlots.value = !!scriptSetupRanges.value?.slotsTypeArg; + return genScript( + ts, + fileName, + _sfc, + lang.value, + scriptRanges.value, + scriptSetupRanges.value, + cssVars.value, + cssModuleClasses.value, + cssScopedClasses.value, + htmlGen.value, + compilerOptions, + vueCompilerOptions, + ); + }); return { lang, diff --git a/packages/vue-language-core/src/types.ts b/packages/vue-language-core/src/types.ts index 76640c2b6c..de58d4c295 100644 --- a/packages/vue-language-core/src/types.ts +++ b/packages/vue-language-core/src/types.ts @@ -28,6 +28,7 @@ export interface VueCompilerOptions { optionsWrapper: [string, string] | []; macros: { defineProps: string[], + defineSlots: string[], defineEmits: string[], defineExpose: string[], withDefaults: string[], diff --git a/packages/vue-language-core/src/utils/localTypes.ts b/packages/vue-language-core/src/utils/localTypes.ts index 784c593ef9..cc6f1d9387 100644 --- a/packages/vue-language-core/src/utils/localTypes.ts +++ b/packages/vue-language-core/src/utils/localTypes.ts @@ -1,5 +1,5 @@ import { VueCompilerOptions } from '../types'; -import { getSlotsPropertyName, getVueLibraryName } from './shared'; +import { getVueLibraryName } from './shared'; export const typesFileName = '__VLS_types.ts'; @@ -8,14 +8,8 @@ export function getTypesCode( vueCompilerOptions: VueCompilerOptions, ) { const libName = getVueLibraryName(vueVersion); - const slots = getSlotsPropertyName(vueVersion); return ` -// @ts-nocheck import type { - FunctionalComponent, - EmitsOptions, - DefineComponent, - SetupContext, ObjectDirective, FunctionDirective, } from '${libName}'; @@ -26,7 +20,6 @@ export type Element = JSX.Element; type IsAny = boolean extends (T extends never ? true : false) ? true : false; export type PickNotAny = IsAny extends true ? B : A; -type AnyArray = T[] | readonly T[]; type ForableSource = [ T extends { [Symbol.iterator](): Iterator } ? T1 : T[keyof T], // item typeof Symbol.iterator extends keyof T ? number : T extends T ? keyof T : never, // key @@ -61,78 +54,51 @@ export declare function directiveFunction(dir: T): export declare function withScope(ctx: T, scope: K): ctx is T & K; export declare function makeOptional(t: T): { [K in keyof T]?: T[K] }; -// TODO: make it stricter between class component type and functional component type -export type ExtractComponentSlots = - IsAny extends true ? Record - : T extends { ${slots}?: infer S } ? S - : T extends { children?: infer S } ? S - : T extends { [K in keyof PickNotAny]?: infer S } ? S - : Record; +export type SelfComponent = string extends N ? {} : N extends string ? { [P in N]: C } : {}; +export type WithComponent = + N1 extends keyof Components ? { [K in N0]: Components[N1] } : + N2 extends keyof Components ? { [K in N0]: Components[N2] } : + N3 extends keyof Components ? { [K in N0]: Components[N3] } : + ${vueCompilerOptions.strictTemplates ? '{}' : '{ [K in N0]: any }'}; export type FillingEventArg_ParametersLength any> = IsAny> extends true ? -1 : Parameters['length']; export type FillingEventArg = E extends (...args: any) => any ? FillingEventArg_ParametersLength extends 0 ? ($event?: undefined) => ReturnType : E : E; - -export type ReturnVoid = T extends (...payload: infer P) => any ? (...payload: P) => void : (...args: any) => void; -export type EmitEvent2 = +export type EmitEvent = F extends { - (event: E, ...payload: infer P): infer R + (event: E, ...payload: infer P): any } ? (...payload: P) => void : F extends { - (event: E, ...payload: infer P): infer R + (event: E, ...payload: infer P): any (...args: any): any } ? (...payload: P) => void : F extends { - (event: E, ...payload: infer P): infer R + (event: E, ...payload: infer P): any (...args: any): any (...args: any): any } ? (...payload: P) => void : F extends { - (event: E, ...payload: infer P): infer R + (event: E, ...payload: infer P): any (...args: any): any (...args: any): any (...args: any): any } ? (...payload: P) => void : unknown | '[Type Warning] Volar could not infer $emit event more than 4 overloads without DefineComponent. see https://github.com/johnsoncodehk/volar/issues/60'; -export type EmitEvent = - T extends DefineComponent ? EmitEvent_3 - : T extends FunctionalComponent ? EmitEvent_3 - : T extends FunctionalComponent ? EmitEvent2['emit'], E> - : unknown; -export type EmitEvent_3 = - EmitsOptions extends E2 ? unknown - : E2 extends AnyArray ? (E extends K ? (...args: any) => void : unknown) // emits: ['event-1', 'event-2'] - : E extends keyof E2 ? ReturnVoid // emits: { 'event-1': () => true, 'event-2': () => true } - : unknown -export type FirstFunction = - NonNullable extends (Function | AnyArray) ? F0 : - NonNullable extends (Function | AnyArray) ? F1 : - NonNullable extends (Function | AnyArray) ? F2 : - NonNullable extends (Function | AnyArray) ? F3 : - NonNullable extends (Function | AnyArray) ? F4 : - unknown; -export type SelfComponent = string extends N ? {} : N extends string ? { [P in N]: C } : {}; -export type WithComponent = - N1 extends keyof Components ? { [K in N0]: Components[N1] } : - N2 extends keyof Components ? { [K in N0]: Components[N2] } : - N3 extends keyof Components ? { [K in N0]: Components[N3] } : - ${vueCompilerOptions.strictTemplates ? '{}' : '{ [K in N0]: any }'}; -export declare function asFunctionalComponent(t: T): - T extends new (...args: any) => { $props: infer Props } ? (_: ${vueCompilerOptions.strictTemplates ? '' : 'Record &'} Props) => any - : T extends (props: infer Props, ...args: any) => any ? T - : T extends (...args: any) => { props: infer Props } ? (_: ${vueCompilerOptions.strictTemplates ? '' : 'Record &'} Props) => any - : T extends new (...args: any) => any ? (_: ${vueCompilerOptions.strictTemplates ? '' : 'Record &'} {}) => any - : T extends (...args: any) => any ? (_: ${vueCompilerOptions.strictTemplates ? '' : 'Record &'} {}) => any - : (_: T) => any; // IntrinsicElement -export type InstanceProps = I extends { $props: infer Props } ? Props & Record : C & Record; -export type EventObject = { - [K in K1]: FillingEventArg< - FirstFunction< - EmitEvent, - E1, - I extends { $emit: infer Emit } ? EmitEvent2 : unknown - > +export declare function asFunctionalComponent(t: T, instance?: K): + T extends (...args: any) => any ? T + : K extends { $props?: infer Props, $slots?: infer Slots, $emit?: infer Emit } + ? (props: Props, ctx?: { attrs?: any, expose?: any, slots?: Slots, emit?: Emit }) => JSX.Element & { __ctx?: typeof ctx, __props?: typeof props } + : (_: T) => { __ctx?: { attrs?: undefined, expose?: undefined, slots?: undefined, emit?: undefined }, __props?: T }; // IntrinsicElement +export declare function pickEvent(emit: Emit, emitKey: K, event: E): FillingEventArg< + PickNotAny< + asFunctionOrAny, + asFunctionOrAny> > -}; +>; +export declare function pickFunctionalComponentCtx(comp: T, compInstance: K): PickNotAny< + K extends { __ctx?: infer Ctx } ? Ctx : any, + T extends (props: any, ctx: infer Ctx) => any ? Ctx : any +>; +type asFunctionOrAny = ((...args: any) => any) extends F ? F : any; `.trim(); } diff --git a/packages/vue-language-core/src/utils/ts.ts b/packages/vue-language-core/src/utils/ts.ts index 865fbb8610..c43203c82a 100644 --- a/packages/vue-language-core/src/utils/ts.ts +++ b/packages/vue-language-core/src/utils/ts.ts @@ -217,6 +217,7 @@ export function resolveVueCompilerOptions(vueOptions: Partial + + diff --git a/packages/vue-test-workspace/vue-tsc/#2472/GenericComp.vue b/packages/vue-test-workspace/vue-tsc/#2472/GenericComp.vue new file mode 100644 index 0000000000..d9659093bb --- /dev/null +++ b/packages/vue-test-workspace/vue-tsc/#2472/GenericComp.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/vue-test-workspace/vue-tsc/#2472/env.d.ts b/packages/vue-test-workspace/vue-tsc/#2472/env.d.ts new file mode 100644 index 0000000000..382808aebc --- /dev/null +++ b/packages/vue-test-workspace/vue-tsc/#2472/env.d.ts @@ -0,0 +1,5 @@ +declare namespace JSX { + interface ElementChildrenAttribute { + $children: {}; + } +} diff --git a/packages/vue-test-workspace/vue-tsc/#2472/main.vue b/packages/vue-test-workspace/vue-tsc/#2472/main.vue new file mode 100644 index 0000000000..7b1d3cc925 --- /dev/null +++ b/packages/vue-test-workspace/vue-tsc/#2472/main.vue @@ -0,0 +1,11 @@ + diff --git a/packages/vue-test-workspace/vue-tsc/components/main.vue b/packages/vue-test-workspace/vue-tsc/components/main.vue index b70ed1d135..ba9e418360 100644 --- a/packages/vue-test-workspace/vue-tsc/components/main.vue +++ b/packages/vue-test-workspace/vue-tsc/components/main.vue @@ -5,6 +5,7 @@ import ScriptSetup from './script-setup.vue'; import ScriptSetupExpose from './script-setup-expose.vue'; import ScriptSetupTypeOnly from './script-setup-type-only.vue'; import ScriptSetupDefaultProps from './script-setup-default-props.vue'; +import ScriptSetupGeneric from './script-setup-generic.vue'; // https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits const ScriptSetupExact = defineComponent({ @@ -60,9 +61,23 @@ const ScriptSetupDefaultPropsExact = defineComponent({ return {}; }, }); +// vue 3.3 generic +declare const ScriptSetupGenericExact: ( + _props: import('vue').VNodeProps & { [K in keyof JSX.ElementChildrenAttribute]: { default(data: T): any } } & { foo: T }, + _ctx?: { + attrs: any, + slots: { default(data: T): any }, + emit: { (e: 'bar', data: T): void }, + expose(_exposed: { baz: T }): void, + } +) => JSX.Element & { + __props?: typeof _props; + __ctx?: typeof _ctx; +}; exactType(ScriptSetup, ScriptSetupExact); exactType(ScriptSetupExpose, ScriptSetupExposeExact); exactType(ScriptSetupTypeOnly, ScriptSetupTypeOnlyExact); exactType(ScriptSetupDefaultProps, ScriptSetupDefaultPropsExact); +exactType(ScriptSetupGeneric, ScriptSetupGenericExact); diff --git a/packages/vue-test-workspace/vue-tsc/components/script-setup-generic.vue b/packages/vue-test-workspace/vue-tsc/components/script-setup-generic.vue new file mode 100644 index 0000000000..72ec367782 --- /dev/null +++ b/packages/vue-test-workspace/vue-tsc/components/script-setup-generic.vue @@ -0,0 +1,10 @@ + + + diff --git a/packages/vue-test-workspace/vue-tsc/tsconfig.json b/packages/vue-test-workspace/vue-tsc/tsconfig.json index df885e3bd0..35f34374b9 100644 --- a/packages/vue-test-workspace/vue-tsc/tsconfig.json +++ b/packages/vue-test-workspace/vue-tsc/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "jsx": "preserve", "noPropertyAccessFromIndexSignature": true, }, "vueCompilerOptions": { @@ -8,6 +9,7 @@ "plugins": ["../../vue-language-plugin-pug"] }, "include": [ + "**/*.ts", "**/*.vue", "**/*.html", ]