From 29b13cf42b35e7d3eab6cb23c0ce1d83805dba1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E5=90=B9=E8=89=B2=E5=BE=A1=E5=AE=88?= <85992002+KazariEX@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:13:02 +0800 Subject: [PATCH 01/93] test: add scoped classes renaming case (#4727) --- .../lib/codegen/template/element.ts | 3 + .../language-server/tests/renaming.spec.ts | 87 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index 7cc1b213f2..be157ff333 100644 --- a/packages/language-core/lib/codegen/template/element.ts +++ b/packages/language-core/lib/codegen/template/element.ts @@ -713,6 +713,9 @@ function* generateReferencesForScopedCssClasses( if (ts.isIdentifier(name)) { walkIdentifier(name); } + else if (ts.isStringLiteral(name)) { + literals.push(name); + } else if (ts.isComputedPropertyName(name)) { const { expression } = name; if (ts.isStringLiteralLike(expression)) { diff --git a/packages/language-server/tests/renaming.spec.ts b/packages/language-server/tests/renaming.spec.ts index eeb0e85dac..eec6001974 100644 --- a/packages/language-server/tests/renaming.spec.ts +++ b/packages/language-server/tests/renaming.spec.ts @@ -790,6 +790,93 @@ describe('Renaming', async () => { `); }); + it('Scoped Classes', async () => { + expect( + await requestRename('fixture.vue', 'vue', ` + + + `, 'bar') + ).toMatchInlineSnapshot(` + { + "changes": { + "file://\${testWorkspacePath}/fixture.vue": [ + { + "newText": "bar", + "range": { + "end": { + "character": 23, + "line": 4, + }, + "start": { + "character": 20, + "line": 4, + }, + }, + }, + { + "newText": "bar", + "range": { + "end": { + "character": 32, + "line": 3, + }, + "start": { + "character": 29, + "line": 3, + }, + }, + }, + { + "newText": "bar", + "range": { + "end": { + "character": 23, + "line": 3, + }, + "start": { + "character": 20, + "line": 3, + }, + }, + }, + { + "newText": "bar", + "range": { + "end": { + "character": 22, + "line": 2, + }, + "start": { + "character": 19, + "line": 2, + }, + }, + }, + { + "newText": "bar", + "range": { + "end": { + "character": 8, + "line": 7, + }, + "start": { + "character": 5, + "line": 7, + }, + }, + }, + ], + }, + } + `); + }); + const openedDocuments: TextDocument[] = []; afterEach(async () => { From b2707f07108fe0a7a46878d5d6733ce5c6098047 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 26 Aug 2024 15:19:25 +0800 Subject: [PATCH 02/93] fix(language-core): fallthrough attributes should be optional --- .../language-core/lib/codegen/script/template.ts | 2 +- packages/tsc/tests/__snapshots__/dts.spec.ts.snap | 14 +++++++------- .../vue3_strictTemplate/inheritAttrs/main.vue | 2 +- .../inheritAttrs_requiredProps/basic.vue | 7 +++++++ .../inheritAttrs_requiredProps/child.vue | 5 +++++ .../inheritAttrs_requiredProps/main.vue | 7 +++++++ 6 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/basic.vue create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/child.vue create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/main.vue diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 50113a84fb..582b9a1345 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -158,7 +158,7 @@ function* generateTemplateContext( yield `return {${newLine}`; yield `slots: ${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'},${newLine}`; yield `refs: __VLS_refs as __VLS_PickRefsExpose,${newLine}`; - yield `attrs: __VLS_inheritedAttrs,${newLine}`; + yield `attrs: {} as Partial,${newLine}`; yield `}${endOfLine}`; } diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index fa28f7d399..4a8769e32e 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -170,17 +170,17 @@ type __VLS_Prettify = { `; exports[`vue-tsc-dts > Input: generic/main.vue, Output: generic/main.vue.d.ts 1`] = ` -"declare const _default: import("vue").DefineComponent<__VLS_TypePropsToOption<__VLS_OmitIndexSignature<{ +"declare const _default: import("vue").DefineComponent<__VLS_TypePropsToOption<__VLS_OmitIndexSignature any; onBar?: (data: number) => any; title?: string; foo: number; -} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps>>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly any; onBar?: (data: number) => any; title?: string; foo: number; -} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps>>>>, {}, {}>; +} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps>>>>>, {}, {}>; export default _default; type __VLS_NonUndefinedable = T extends undefined ? never : T; type __VLS_TypePropsToOption = { @@ -671,7 +671,7 @@ exports[`vue-tsc-dts > Input: template-slots/component.vue, Output: template-slo }): any; }; refs: __VLS_PickRefsExpose<{}>; - attrs: {}; + attrs: Partial<{}>; }; type __VLS_Slots = ReturnType['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; @@ -714,7 +714,7 @@ declare const __VLS_template: () => { 'no-bind': () => VNode[]; }; refs: __VLS_PickRefsExpose<{}>; - attrs: {}; + attrs: Partial<{}>; }; type __VLS_Slots = ReturnType['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; @@ -740,7 +740,7 @@ exports[`vue-tsc-dts > Input: template-slots/component-destructuring.vue, Output }) => any[]; }; refs: __VLS_PickRefsExpose<{}>; - attrs: {}; + attrs: Partial<{}>; }; type __VLS_Slots = ReturnType['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; @@ -770,7 +770,7 @@ exports[`vue-tsc-dts > Input: template-slots/component-no-script.vue, Output: te }): any; }; refs: __VLS_PickRefsExpose<{}>; - attrs: {}; + attrs: Partial<{}>; }; type __VLS_Slots = ReturnType['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/main.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/main.vue index b9a545507a..976b5e849c 100644 --- a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/main.vue @@ -1,4 +1,4 @@ - + + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/child.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/child.vue new file mode 100644 index 0000000000..74e63d8179 --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/child.vue @@ -0,0 +1,5 @@ + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/main.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/main.vue new file mode 100644 index 0000000000..e177ec22aa --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/main.vue @@ -0,0 +1,7 @@ + + + From 30706912af7d340423cd37cac8666bcc10e33297 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 26 Aug 2024 18:39:37 +0800 Subject: [PATCH 03/93] fix(language-core): don't fallthrough attributes if defineEmits exists --- .../lib/codegen/script/component.ts | 102 ++++++++++++------ .../lib/codegen/script/scriptSetup.ts | 51 ++++----- .../inheritAttrs_duplicateNameEvent/basic.vue | 12 +++ .../inheritAttrs_duplicateNameEvent/child.vue | 5 + .../inheritAttrs_duplicateNameEvent/main.vue | 8 ++ .../inheritAttrs_duplicateNameProp/basic.vue | 11 ++ .../child.vue | 0 .../inheritAttrs_duplicateNameProp/main.vue | 9 ++ .../basic.vue | 0 .../inheritAttrs_requiredProp/child.vue | 5 + .../main.vue | 0 11 files changed, 146 insertions(+), 57 deletions(-) create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/basic.vue create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/child.vue create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/main.vue create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/basic.vue rename test-workspace/tsc/passedFixtures/vue3_strictTemplate/{inheritAttrs_requiredProps => inheritAttrs_duplicateNameProp}/child.vue (100%) create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/main.vue rename test-workspace/tsc/passedFixtures/vue3_strictTemplate/{inheritAttrs_requiredProps => inheritAttrs_requiredProp}/basic.vue (100%) create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/child.vue rename test-workspace/tsc/passedFixtures/vue3_strictTemplate/{inheritAttrs_requiredProps => inheritAttrs_requiredProp}/main.vue (100%) diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index 4d5bbe4215..f1618c3f59 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -71,34 +71,34 @@ export function* generateScriptSetupOptions( scriptSetupRanges: ScriptSetupRanges, inheritAttrs: boolean ): Generator { - yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges, inheritAttrs); - yield* generateEmitsOption(options, scriptSetup, scriptSetupRanges); -} - -export function* generatePropsOption( - options: ScriptCodegenOptions, - ctx: ScriptCodegenContext, - scriptSetup: NonNullable, - scriptSetupRanges: ScriptSetupRanges, - inheritAttrs: boolean -) { + const emitOptionCodes = [...generateEmitsOption(options, scriptSetup, scriptSetupRanges)]; + for (const code of emitOptionCodes) { + yield code; + } if (options.vueCompilerOptions.target >= 3.5) { const types = []; - if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) { - types.push(`ReturnType['attrs']`); + if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size && !emitOptionCodes.length) { + types.push(`{} as ReturnType['attrs']`); } if (ctx.generatedPropsType) { types.push(`{} as __VLS_PublicProps`); } - if (types.length) { - yield `__typeProps: ${types.join(' & ')},${newLine}`; + if (types.length === 1) { + yield `__typeProps: ${types[0]},${newLine}`; + } + else if (types.length >= 2) { + yield `__typeProps: {${newLine}`; + for (const type of types) { + yield `...${type},${newLine}`; + } + yield `},${newLine}`; } } if (options.vueCompilerOptions.target < 3.5 || !ctx.generatedPropsType || scriptSetupRanges.props.withDefaults) { const codegens: (() => Generator)[] = []; - if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) { + if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size && !emitOptionCodes.length) { codegens.push(function* () { yield `{} as ${ctx.helperTypes.TypePropsToOption.name}<__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}['attrs']>, {}>>`; }); @@ -148,25 +148,63 @@ export function* generateEmitsOption( scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges ): Generator { - if (!scriptSetupRanges.emits.define && !scriptSetupRanges.defineProp.some(p => p.isModel)) { - return; + const codes: { + optionExp?: Code[], + typeOptionType?: Code[], + }[] = []; + if (scriptSetupRanges.defineProp.some(p => p.isModel)) { + codes.push({ + optionExp: [`{} as __VLS_NormalizeEmits<__VLS_ModelEmitsType>`], + typeOptionType: [`__VLS_ModelEmitsType`], + }); } - - if (options.vueCompilerOptions.target < 3.5 || scriptSetupRanges.emits.define?.arg || scriptSetupRanges.emits.define?.hasUnionTypeArg) { - yield `emits: ({} as __VLS_NormalizeEmits<__VLS_ModelEmitsType`; - if (scriptSetupRanges?.emits.define) { - yield ` & typeof `; - yield scriptSetupRanges.emits.name ?? '__VLS_emit'; + if (scriptSetupRanges.emits.define) { + const { typeArg, hasUnionTypeArg } = scriptSetupRanges.emits.define; + codes.push({ + optionExp: [`{} as __VLS_NormalizeEmits`], + typeOptionType: typeArg && !hasUnionTypeArg ? [scriptSetup.content.slice(typeArg.start, typeArg.end)] : undefined, + }); + } + if (options.vueCompilerOptions.target >= 3.5 && codes.every(code => code.typeOptionType)) { + if (codes.length === 1) { + yield `__typeEmits: {} as `; + for (const code of codes[0].typeOptionType!) { + yield code; + } + yield `,${newLine}`; + } + else if (codes.length >= 2) { + yield `__typeEmits: {} as `; + for (const code of codes[0].typeOptionType!) { + yield code; + } + for (let i = 1; i < codes.length; i++) { + yield ` & `; + for (const code of codes[i].typeOptionType!) { + yield code; + } + } + yield `,${newLine}`; } - yield `>),${newLine}`; } - else { - yield `__typeEmits: {} as __VLS_ModelEmitsType`; - const typeArg = scriptSetupRanges.emits.define?.typeArg; - if (typeArg) { - yield ` & `; - yield scriptSetup.content.slice(typeArg.start, typeArg.end); + else if (codes.every(code => code.optionExp)) { + if (codes.length === 1) { + yield `emits: `; + for (const code of codes[0].optionExp!) { + yield code; + } + yield `,${newLine}`; + } + else if (codes.length >= 2) { + yield `emits: {${newLine}`; + for (const code of codes) { + yield `...`; + for (const c of code.optionExp!) { + yield c; + } + yield `,${newLine}`; + } + yield `},${newLine}`; } - yield `,${newLine}`; } } diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 869a9b60ed..05bab1100f 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -303,14 +303,16 @@ function* generateComponentProps( scriptSetupRanges: ScriptSetupRanges, definePropMirrors: Map ): Generator { - yield `const __VLS_fnComponent = ` - + `(await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; + yield `const __VLS_fnComponent = (await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; + if (scriptSetupRanges.props.define?.arg) { - yield ` props: `; + yield `props: `; yield generateSfcBlockSection(scriptSetup, scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end, codeFeatures.navigation); yield `,${newLine}`; } + yield* generateEmitsOption(options, scriptSetup, scriptSetupRanges); + yield `})${endOfLine}`; yield `type __VLS_BuiltInPublicProps = ${options.vueCompilerOptions.target >= 3.4 @@ -418,32 +420,31 @@ function* generateModelEmits( scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges ): Generator { - yield `type __VLS_ModelEmitsType = `; - if (scriptSetupRanges.defineProp.filter(p => p.isModel).length) { - if (options.vueCompilerOptions.target < 3.5) { - yield `typeof __VLS_modelEmitsType${endOfLine}`; - yield `const __VLS_modelEmitsType = (await import('${options.vueCompilerOptions.lib}')).defineEmits<`; - } - yield `{${newLine}`; - for (const defineProp of scriptSetupRanges.defineProp) { - if (!defineProp.isModel) { - continue; + const defineModels = scriptSetupRanges.defineProp.filter(p => p.isModel); + if (defineModels.length) { + const generateDefineModels = function* () { + for (const defineModel of defineModels) { + const [propName, localName] = getPropAndLocalName(scriptSetup, defineModel); + yield `'update:${propName}': [${propName}:`; + yield* generateDefinePropType(scriptSetup, propName, localName, defineModel); + yield `]${endOfLine}`; } - - const [propName, localName] = getPropAndLocalName(scriptSetup, defineProp); - - yield `'update:${propName}': [${propName}:`; - yield* generateDefinePropType(scriptSetup, propName, localName, defineProp); - yield `]${endOfLine}`; + }; + if (options.vueCompilerOptions.target >= 3.5) { + yield `type __VLS_ModelEmitsType = {${newLine}`; + yield* generateDefineModels(); + yield `}${endOfLine}`; } - yield `}`; - if (options.vueCompilerOptions.target < 3.5) { - yield `>()`; + else { + yield `const __VLS_modelEmitsType = (await import('${options.vueCompilerOptions.lib}')).defineEmits<{${newLine}`; + yield* generateDefineModels(); + yield `}>()${endOfLine}`; + yield `type __VLS_ModelEmitsType = typeof __VLS_modelEmitsType${endOfLine}`; } - } else { - yield `{}`; } - yield endOfLine; + else { + yield `type __VLS_ModelEmitsType = {}${endOfLine}`; + } } function* generateStyleModules( diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/basic.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/basic.vue new file mode 100644 index 0000000000..e7574c27d2 --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/basic.vue @@ -0,0 +1,12 @@ + + + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/child.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/child.vue new file mode 100644 index 0000000000..57e78c68eb --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/child.vue @@ -0,0 +1,5 @@ + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/main.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/main.vue new file mode 100644 index 0000000000..247181ad3c --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/main.vue @@ -0,0 +1,8 @@ + + + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/basic.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/basic.vue new file mode 100644 index 0000000000..f24d7f587a --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/basic.vue @@ -0,0 +1,11 @@ + + + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/child.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/child.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/child.vue rename to test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/child.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/main.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/main.vue new file mode 100644 index 0000000000..23c4cd71d8 --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/main.vue @@ -0,0 +1,9 @@ + + + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/basic.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/basic.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/basic.vue rename to test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/basic.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/child.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/child.vue new file mode 100644 index 0000000000..74e63d8179 --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/child.vue @@ -0,0 +1,5 @@ + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/main.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/main.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProps/main.vue rename to test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/main.vue From 5a9bb123daa1481d196fc812de77ef1bfa1baad5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 27 Aug 2024 00:26:03 +0800 Subject: [PATCH 04/93] refactor(language-core): rewrite generatePropsOption --- .../lib/codegen/script/component.ts | 201 ++++++++---------- .../lib/codegen/script/internalComponent.ts | 17 +- .../lib/codegen/script/scriptSetup.ts | 12 +- .../tsc/tests/__snapshots__/dts.spec.ts.snap | 2 +- 4 files changed, 106 insertions(+), 126 deletions(-) diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index f1618c3f59..ca7cf0b44d 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -1,4 +1,3 @@ -import type { ScriptRanges } from '../../parsers/scriptRanges'; import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges'; import type { Code, Sfc } from '../../types'; import { endOfLine, generateSfcBlockSection, newLine } from '../common'; @@ -31,10 +30,15 @@ export function* generateComponent( yield `}${endOfLine}`; yield `},${newLine}`; if (!ctx.bypassDefineComponent) { - yield* generateScriptSetupOptions(options, ctx, scriptSetup, scriptSetupRanges, true); + const emitOptionCodes = [...generateEmitsOption(options, scriptSetup, scriptSetupRanges)]; + for (const code of emitOptionCodes) { + yield code; + } + yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges, !!emitOptionCodes.length, true); } - if (options.sfc.script && options.scriptRanges) { - yield* generateScriptOptions(options.sfc.script, options.scriptRanges); + if (options.sfc.script && options.scriptRanges?.exportDefault?.args) { + const { args } = options.scriptRanges.exportDefault; + yield generateSfcBlockSection(options.sfc.script, args.start + 1, args.end - 1, codeFeatures.all); } if (options.vueCompilerOptions.target >= 3.5 && scriptSetupRanges.templateRefs.length) { yield `__typeRefs: {} as __VLS_Refs,${newLine}`; @@ -55,134 +59,42 @@ export function* generateComponentSetupReturns(scriptSetupRanges: ScriptSetupRan } } -export function* generateScriptOptions( - script: NonNullable, - scriptRanges: ScriptRanges -): Generator { - if (scriptRanges.exportDefault?.args) { - yield generateSfcBlockSection(script, scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1, codeFeatures.all); - } -} - -export function* generateScriptSetupOptions( - options: ScriptCodegenOptions, - ctx: ScriptCodegenContext, - scriptSetup: NonNullable, - scriptSetupRanges: ScriptSetupRanges, - inheritAttrs: boolean -): Generator { - const emitOptionCodes = [...generateEmitsOption(options, scriptSetup, scriptSetupRanges)]; - for (const code of emitOptionCodes) { - yield code; - } - - if (options.vueCompilerOptions.target >= 3.5) { - const types = []; - if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size && !emitOptionCodes.length) { - types.push(`{} as ReturnType['attrs']`); - } - if (ctx.generatedPropsType) { - types.push(`{} as __VLS_PublicProps`); - } - if (types.length === 1) { - yield `__typeProps: ${types[0]},${newLine}`; - } - else if (types.length >= 2) { - yield `__typeProps: {${newLine}`; - for (const type of types) { - yield `...${type},${newLine}`; - } - yield `},${newLine}`; - } - } - if (options.vueCompilerOptions.target < 3.5 || !ctx.generatedPropsType || scriptSetupRanges.props.withDefaults) { - const codegens: (() => Generator)[] = []; - - if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size && !emitOptionCodes.length) { - codegens.push(function* () { - yield `{} as ${ctx.helperTypes.TypePropsToOption.name}<__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}['attrs']>, {}>>`; - }); - } - - if (ctx.generatedPropsType) { - codegens.push(function* () { - yield `{} as `; - if (scriptSetupRanges.props.withDefaults?.arg) { - yield `${ctx.helperTypes.WithDefaults.name}<`; - } - yield `${ctx.helperTypes.TypePropsToOption.name}<`; - yield `__VLS_PublicProps>`; - if (scriptSetupRanges.props.withDefaults?.arg) { - yield `, typeof __VLS_withDefaultsArg>`; - } - }); - } - if (scriptSetupRanges.props.define?.arg) { - const { arg } = scriptSetupRanges.props.define; - codegens.push(function* () { - yield generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation); - }); - } - - if (codegens.length === 1) { - yield `props: `; - for (const generate of codegens) { - yield* generate(); - } - yield `,${newLine}`; - } - else if (codegens.length >= 2) { - yield `props: {${newLine}`; - for (const generate of codegens) { - yield `...`; - yield* generate(); - yield `,${newLine}`; - } - yield `},${newLine}`; - } - } -} - export function* generateEmitsOption( options: ScriptCodegenOptions, scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges ): Generator { const codes: { - optionExp?: Code[], - typeOptionType?: Code[], + optionExp?: Code, + typeOptionType?: Code, }[] = []; if (scriptSetupRanges.defineProp.some(p => p.isModel)) { codes.push({ - optionExp: [`{} as __VLS_NormalizeEmits<__VLS_ModelEmitsType>`], - typeOptionType: [`__VLS_ModelEmitsType`], + optionExp: `{} as __VLS_NormalizeEmits<__VLS_ModelEmitsType>`, + typeOptionType: `__VLS_ModelEmitsType`, }); } if (scriptSetupRanges.emits.define) { const { typeArg, hasUnionTypeArg } = scriptSetupRanges.emits.define; codes.push({ - optionExp: [`{} as __VLS_NormalizeEmits`], - typeOptionType: typeArg && !hasUnionTypeArg ? [scriptSetup.content.slice(typeArg.start, typeArg.end)] : undefined, + optionExp: `{} as __VLS_NormalizeEmits`, + typeOptionType: typeArg && !hasUnionTypeArg + ? scriptSetup.content.slice(typeArg.start, typeArg.end) + : undefined, }); } if (options.vueCompilerOptions.target >= 3.5 && codes.every(code => code.typeOptionType)) { if (codes.length === 1) { yield `__typeEmits: {} as `; - for (const code of codes[0].typeOptionType!) { - yield code; - } + yield codes[0].typeOptionType!; yield `,${newLine}`; } else if (codes.length >= 2) { yield `__typeEmits: {} as `; - for (const code of codes[0].typeOptionType!) { - yield code; - } + yield codes[0].typeOptionType!; for (let i = 1; i < codes.length; i++) { yield ` & `; - for (const code of codes[i].typeOptionType!) { - yield code; - } + yield codes[i].typeOptionType!; } yield `,${newLine}`; } @@ -190,18 +102,81 @@ export function* generateEmitsOption( else if (codes.every(code => code.optionExp)) { if (codes.length === 1) { yield `emits: `; - for (const code of codes[0].optionExp!) { - yield code; - } + yield codes[0].optionExp!; yield `,${newLine}`; } else if (codes.length >= 2) { yield `emits: {${newLine}`; for (const code of codes) { yield `...`; - for (const c of code.optionExp!) { - yield c; - } + yield code.optionExp!; + yield `,${newLine}`; + } + yield `},${newLine}`; + } + } +} + +export function* generatePropsOption( + options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, + scriptSetup: NonNullable, + scriptSetupRanges: ScriptSetupRanges, + hasEmitsOption: boolean, + inheritAttrs: boolean +): Generator { + const optionExpCodes: Code[] = []; + const typeOptionExpCodes: Code[] = []; + + if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size && !hasEmitsOption) { + optionExpCodes.push(`{} as ${ctx.helperTypes.TypePropsToOption.name}<__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}['attrs']>, {}>>`); + typeOptionExpCodes.push(`{} as ReturnType['attrs']`); + } + if (ctx.generatedPropsType) { + optionExpCodes.push([ + `{} as `, + scriptSetupRanges.props.withDefaults?.arg ? `${ctx.helperTypes.WithDefaults.name}<` : '', + `${ctx.helperTypes.TypePropsToOption.name}<__VLS_PublicProps>`, + scriptSetupRanges.props.withDefaults?.arg ? `, typeof __VLS_withDefaultsArg>` : '', + ].join('')); + typeOptionExpCodes.push(`{} as __VLS_PublicProps`); + } + + if (scriptSetupRanges.props.define?.arg) { + const { arg } = scriptSetupRanges.props.define; + optionExpCodes.push(generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation)); + } + + const useTypeOption = options.vueCompilerOptions.target >= 3.5 && typeOptionExpCodes.length; + const useOption = (!useTypeOption || scriptSetupRanges.props.withDefaults) && optionExpCodes.length; + + if (useTypeOption) { + if (typeOptionExpCodes.length === 1) { + yield `__typeProps: `; + yield typeOptionExpCodes[0]; + yield `,${newLine}`; + } + else if (typeOptionExpCodes.length >= 2) { + yield `__typeProps: {${newLine}`; + for (const code of typeOptionExpCodes) { + yield `...`; + yield code; + yield `,${newLine}`; + } + yield `},${newLine}`; + } + } + if (useOption) { + if (optionExpCodes.length === 1) { + yield `props: `; + yield optionExpCodes[0]; + yield `,${newLine}`; + } + else if (optionExpCodes.length >= 2) { + yield `props: {${newLine}`; + for (const code of optionExpCodes) { + yield `...`; + yield code; yield `,${newLine}`; } yield `},${newLine}`; diff --git a/packages/language-core/lib/codegen/script/internalComponent.ts b/packages/language-core/lib/codegen/script/internalComponent.ts index af8859709c..4cf91246f7 100644 --- a/packages/language-core/lib/codegen/script/internalComponent.ts +++ b/packages/language-core/lib/codegen/script/internalComponent.ts @@ -1,9 +1,9 @@ import type { Code } from '../../types'; -import { endOfLine, newLine } from '../common'; +import { endOfLine, generateSfcBlockSection, newLine } from '../common'; import type { TemplateCodegenContext } from '../template/context'; -import { generateComponentSetupReturns, generateScriptOptions, generateScriptSetupOptions } from './component'; +import { generateComponentSetupReturns, generateEmitsOption, generatePropsOption } from './component'; import type { ScriptCodegenContext } from './context'; -import type { ScriptCodegenOptions } from './index'; +import { codeFeatures, type ScriptCodegenOptions } from './index'; import { getTemplateUsageVars } from './template'; export function* generateInternalComponent( @@ -52,10 +52,15 @@ export function* generateInternalComponent( yield `__typeRefs: {} as __VLS_Refs,${newLine}`; } if (options.sfc.scriptSetup && options.scriptSetupRanges && !ctx.bypassDefineComponent) { - yield* generateScriptSetupOptions(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges, false); + const emitOptionCodes = [...generateEmitsOption(options, options.sfc.scriptSetup, options.scriptSetupRanges)]; + for (const code of emitOptionCodes) { + yield code; + } + yield* generatePropsOption(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges, !!emitOptionCodes.length, false); } - if (options.sfc.script && options.scriptRanges) { - yield* generateScriptOptions(options.sfc.script, options.scriptRanges); + if (options.sfc.script && options.scriptRanges?.exportDefault?.args) { + const { args } = options.scriptRanges.exportDefault; + yield generateSfcBlockSection(options.sfc.script, args.start + 1, args.end - 1, codeFeatures.all); } yield `})${endOfLine}`; // defineComponent { } diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 05bab1100f..6c6c014597 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -57,10 +57,13 @@ export function* generateScriptSetup( + ` __VLS_setup = (async () => {${newLine}`; yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, undefined, definePropMirrors); - const emitTypes = ['__VLS_ModelEmitsType']; + const emitTypes: string[] = []; if (scriptSetupRanges.emits.define) { - emitTypes.unshift(`typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'}`); + emitTypes.push(`typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'}`); + } + if (scriptSetupRanges.defineProp.some(p => p.isModel)) { + emitTypes.push(`__VLS_ModelEmitsType`); } yield ` return {} as {${newLine}` @@ -68,7 +71,7 @@ export function* generateScriptSetup( + ` expose(exposed: import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,${newLine}` + ` attrs: any,${newLine}` + ` slots: __VLS_Slots,${newLine}` - + ` emit: ${emitTypes.join(' & ')},${newLine}` + + ` emit: ${emitTypes.length ? emitTypes.join(' & ') : `{}`},${newLine}` + ` }${endOfLine}`; yield ` })(),${newLine}`; // __VLS_setup = (async () => { yield `) => ({} as import('${options.vueCompilerOptions.lib}').VNode & { __ctx?: Awaited }))`; @@ -442,9 +445,6 @@ function* generateModelEmits( yield `type __VLS_ModelEmitsType = typeof __VLS_modelEmitsType${endOfLine}`; } } - else { - yield `type __VLS_ModelEmitsType = {}${endOfLine}`; - } } function* generateStyleModules( diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index 4a8769e32e..6a19645cc1 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -78,7 +78,7 @@ exports[`vue-tsc-dts > Input: events/component-generic.vue, Output: events/compo expose(exposed: import("vue").ShallowUnwrapRef<{}>): void; attrs: any; slots: {}; - emit: ((evt: "foo", value: string) => void) & {}; + emit: (evt: "foo", value: string) => void; }>) => import("vue").VNode & { From 21205fb9d7c14650c67c2a581cf18f9e7b24bfc4 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 27 Aug 2024 18:02:51 +0800 Subject: [PATCH 05/93] fix(language-core): fallthrough attributes break component type when root tag type is unknown (#4729) --- .../lib/codegen/script/component.ts | 18 +++++++++++++----- .../vue3/inheritAttrs_unknownTag/basic.vue | 7 +++++++ .../vue3/inheritAttrs_unknownTag/main.vue | 7 +++++++ .../inheritAttrs_unknownTag/basic.vue | 7 +++++++ .../inheritAttrs_unknownTag/main.vue | 7 +++++++ 5 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/basic.vue create mode 100644 test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/main.vue create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/basic.vue create mode 100644 test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/main.vue diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index ca7cf0b44d..9029d2f1d1 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -128,10 +128,6 @@ export function* generatePropsOption( const optionExpCodes: Code[] = []; const typeOptionExpCodes: Code[] = []; - if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size && !hasEmitsOption) { - optionExpCodes.push(`{} as ${ctx.helperTypes.TypePropsToOption.name}<__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}['attrs']>, {}>>`); - typeOptionExpCodes.push(`{} as ReturnType['attrs']`); - } if (ctx.generatedPropsType) { optionExpCodes.push([ `{} as `, @@ -141,11 +137,23 @@ export function* generatePropsOption( ].join('')); typeOptionExpCodes.push(`{} as __VLS_PublicProps`); } - if (scriptSetupRanges.props.define?.arg) { const { arg } = scriptSetupRanges.props.define; optionExpCodes.push(generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation)); } + if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size && !hasEmitsOption) { + const attrsType = `ReturnType['attrs']`; + const propsType = `__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}<${attrsType}>, {}>`; + const optionType = `${ctx.helperTypes.TypePropsToOption.name}<${propsType}>`; + if (optionExpCodes.length) { + optionExpCodes.unshift(`{} as ${optionType}`); + } + else { + // workaround for https://github.com/vuejs/core/pull/7419 + optionExpCodes.unshift(`{} as keyof ${propsType} extends never ? never: ${optionType}`); + } + typeOptionExpCodes.unshift(`{} as ${attrsType}`); + } const useTypeOption = options.vueCompilerOptions.target >= 3.5 && typeOptionExpCodes.length; const useOption = (!useTypeOption || scriptSetupRanges.props.withDefaults) && optionExpCodes.length; diff --git a/test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/basic.vue b/test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/basic.vue new file mode 100644 index 0000000000..88fa3d63aa --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/basic.vue @@ -0,0 +1,7 @@ + + + diff --git a/test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/main.vue b/test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/main.vue new file mode 100644 index 0000000000..e177ec22aa --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/main.vue @@ -0,0 +1,7 @@ + + + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/basic.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/basic.vue new file mode 100644 index 0000000000..88fa3d63aa --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/basic.vue @@ -0,0 +1,7 @@ + + + diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/main.vue b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/main.vue new file mode 100644 index 0000000000..e177ec22aa --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/main.vue @@ -0,0 +1,7 @@ + + + From d94bd3c9d707fc26ebca5f7da79546b005f17f14 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 27 Aug 2024 18:52:59 +0800 Subject: [PATCH 06/93] fix(language-core): improve fallthrough attributes and defineEmit compatibility --- packages/language-core/lib/codegen/script/component.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index 9029d2f1d1..fa49ad1791 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -141,8 +141,11 @@ export function* generatePropsOption( const { arg } = scriptSetupRanges.props.define; optionExpCodes.push(generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation)); } - if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size && !hasEmitsOption) { - const attrsType = `ReturnType['attrs']`; + if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) { + let attrsType = `ReturnType['attrs']`; + if (hasEmitsOption) { + attrsType = `Omit<${attrsType}, \`on\${string}\`>`; + } const propsType = `__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}<${attrsType}>, {}>`; const optionType = `${ctx.helperTypes.TypePropsToOption.name}<${propsType}>`; if (optionExpCodes.length) { From ebb84900b890ddd6b793a8fe78c19d87dc2d7f38 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 27 Aug 2024 19:13:00 +0800 Subject: [PATCH 07/93] feat(language-core): add fallthroughAttributes compiler option --- .../lib/codegen/template/element.ts | 14 +++++++---- packages/language-core/lib/types.ts | 1 + packages/language-core/lib/utils/ts.ts | 1 + .../schemas/vue-tsconfig.schema.json | 6 +++++ .../tsc/tests/__snapshots__/dts.spec.ts.snap | 24 +------------------ .../fallthroughAttributes/tsconfig.json | 7 ++++++ .../unknownTag}/basic.vue | 0 .../unknownTag}/main.vue | 0 .../#4699/HelloWorld.vue | 0 .../#4699/main.vue | 0 .../base}/basic.vue | 0 .../base}/child.vue | 0 .../define-options-inherit-attrs-false.vue | 0 .../base}/inherit-attrs-false-v-bind.vue | 0 .../base}/inherit-attrs-false.vue | 0 .../base}/main.vue | 0 .../duplicateNameEvent}/basic.vue | 0 .../duplicateNameEvent}/child.vue | 0 .../duplicateNameEvent}/main.vue | 0 .../duplicateNameProp}/basic.vue | 0 .../duplicateNameProp}/child.vue | 0 .../duplicateNameProp}/main.vue | 0 .../requiredProp}/basic.vue | 0 .../requiredProp}/child.vue | 0 .../requiredProp}/main.vue | 0 .../tsconfig.json | 8 +++++++ .../unknownTag}/basic.vue | 0 .../unknownTag}/main.vue | 0 .../vue2_strictTemplate/tsconfig.json | 1 - test-workspace/tsc/tsconfig.json | 2 ++ 30 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 test-workspace/tsc/passedFixtures/fallthroughAttributes/tsconfig.json rename test-workspace/tsc/passedFixtures/{vue3/inheritAttrs_unknownTag => fallthroughAttributes/unknownTag}/basic.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3/inheritAttrs_unknownTag => fallthroughAttributes/unknownTag}/main.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate => fallthroughAttributes_strictTemplate}/#4699/HelloWorld.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate => fallthroughAttributes_strictTemplate}/#4699/main.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs => fallthroughAttributes_strictTemplate/base}/basic.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs => fallthroughAttributes_strictTemplate/base}/child.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs => fallthroughAttributes_strictTemplate/base}/define-options-inherit-attrs-false.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs => fallthroughAttributes_strictTemplate/base}/inherit-attrs-false-v-bind.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs => fallthroughAttributes_strictTemplate/base}/inherit-attrs-false.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs => fallthroughAttributes_strictTemplate/base}/main.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_duplicateNameEvent => fallthroughAttributes_strictTemplate/duplicateNameEvent}/basic.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_duplicateNameEvent => fallthroughAttributes_strictTemplate/duplicateNameEvent}/child.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_duplicateNameEvent => fallthroughAttributes_strictTemplate/duplicateNameEvent}/main.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_duplicateNameProp => fallthroughAttributes_strictTemplate/duplicateNameProp}/basic.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_duplicateNameProp => fallthroughAttributes_strictTemplate/duplicateNameProp}/child.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_duplicateNameProp => fallthroughAttributes_strictTemplate/duplicateNameProp}/main.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_requiredProp => fallthroughAttributes_strictTemplate/requiredProp}/basic.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_requiredProp => fallthroughAttributes_strictTemplate/requiredProp}/child.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_requiredProp => fallthroughAttributes_strictTemplate/requiredProp}/main.vue (100%) create mode 100644 test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/tsconfig.json rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_unknownTag => fallthroughAttributes_strictTemplate/unknownTag}/basic.vue (100%) rename test-workspace/tsc/passedFixtures/{vue3_strictTemplate/inheritAttrs_unknownTag => fallthroughAttributes_strictTemplate/unknownTag}/main.vue (100%) diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index be157ff333..42026ad78b 100644 --- a/packages/language-core/lib/codegen/template/element.ts +++ b/packages/language-core/lib/codegen/template/element.ts @@ -288,8 +288,11 @@ export function* generateComponent( } if ( - node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs') - || node === ctx.singleRootNode + options.vueCompilerOptions.fallthroughAttributes + && ( + node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs') + || node === ctx.singleRootNode + ) ) { const varAttrs = ctx.getInternalVariable(); ctx.inheritedAttrVars.add(varAttrs); @@ -386,8 +389,11 @@ export function* generateElement( } if ( - node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs') - || node === ctx.singleRootNode + options.vueCompilerOptions.fallthroughAttributes + && ( + node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs') + || node === ctx.singleRootNode + ) ) { ctx.inheritedAttrVars.add(`__VLS_intrinsicElements.${node.tag}`); } diff --git a/packages/language-core/lib/types.ts b/packages/language-core/lib/types.ts index 17158d277e..11860c48de 100644 --- a/packages/language-core/lib/types.ts +++ b/packages/language-core/lib/types.ts @@ -30,6 +30,7 @@ export interface VueCompilerOptions { jsxSlots: boolean; strictTemplates: boolean; skipTemplateCodegen: boolean; + fallthroughAttributes: boolean; dataAttributes: string[]; htmlAttributes: string[]; optionsWrapper: [string, string] | []; diff --git a/packages/language-core/lib/utils/ts.ts b/packages/language-core/lib/utils/ts.ts index 18d21ad14c..b17874e8ff 100644 --- a/packages/language-core/lib/utils/ts.ts +++ b/packages/language-core/lib/utils/ts.ts @@ -216,6 +216,7 @@ export function resolveVueCompilerOptions(vueOptions: Partial = { `; exports[`vue-tsc-dts > Input: generic/main.vue, Output: generic/main.vue.d.ts 1`] = ` -"declare const _default: import("vue").DefineComponent<__VLS_TypePropsToOption<__VLS_OmitIndexSignature any; - onBar?: (data: number) => any; - title?: string; - foo: number; -} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps>>>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly any; - onBar?: (data: number) => any; - title?: string; - foo: number; -} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps>>>>>, {}, {}>; +"declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; export default _default; -type __VLS_NonUndefinedable = T extends undefined ? never : T; -type __VLS_TypePropsToOption = { - [K in keyof T]-?: {} extends Pick ? { - type: import('vue').PropType<__VLS_NonUndefinedable>; - } : { - type: import('vue').PropType; - required: true; - }; -}; -type __VLS_OmitIndexSignature = { - [K in keyof T as {} extends Record ? never : K]: T[K]; -}; " `; diff --git a/test-workspace/tsc/passedFixtures/fallthroughAttributes/tsconfig.json b/test-workspace/tsc/passedFixtures/fallthroughAttributes/tsconfig.json new file mode 100644 index 0000000000..d5a2c81e67 --- /dev/null +++ b/test-workspace/tsc/passedFixtures/fallthroughAttributes/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.base.json", + "vueCompilerOptions": { + "fallthroughAttributes": true, + }, + "include": [ "**/*" ], +} diff --git a/test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/basic.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes/unknownTag/basic.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/basic.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes/unknownTag/basic.vue diff --git a/test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/main.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes/unknownTag/main.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3/inheritAttrs_unknownTag/main.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes/unknownTag/main.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/#4699/HelloWorld.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/#4699/HelloWorld.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/#4699/HelloWorld.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/#4699/HelloWorld.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/#4699/main.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/#4699/main.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/#4699/main.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/#4699/main.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/basic.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/basic.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/basic.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/basic.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/child.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/child.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/child.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/child.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/define-options-inherit-attrs-false.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/define-options-inherit-attrs-false.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/define-options-inherit-attrs-false.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/define-options-inherit-attrs-false.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/inherit-attrs-false-v-bind.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/inherit-attrs-false-v-bind.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/inherit-attrs-false-v-bind.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/inherit-attrs-false-v-bind.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/inherit-attrs-false.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/inherit-attrs-false.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/inherit-attrs-false.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/inherit-attrs-false.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/main.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/main.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs/main.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/base/main.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/basic.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameEvent/basic.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/basic.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameEvent/basic.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/child.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameEvent/child.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/child.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameEvent/child.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/main.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameEvent/main.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameEvent/main.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameEvent/main.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/basic.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameProp/basic.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/basic.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameProp/basic.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/child.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameProp/child.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/child.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameProp/child.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/main.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameProp/main.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_duplicateNameProp/main.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/duplicateNameProp/main.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/basic.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/requiredProp/basic.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/basic.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/requiredProp/basic.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/child.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/requiredProp/child.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/child.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/requiredProp/child.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/main.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/requiredProp/main.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_requiredProp/main.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/requiredProp/main.vue diff --git a/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/tsconfig.json b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/tsconfig.json new file mode 100644 index 0000000000..e93dd518af --- /dev/null +++ b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.base.json", + "vueCompilerOptions": { + "fallthroughAttributes": true, + "strictTemplates": true, + }, + "include": [ "**/*" ], +} diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/basic.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/unknownTag/basic.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/basic.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/unknownTag/basic.vue diff --git a/test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/main.vue b/test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/unknownTag/main.vue similarity index 100% rename from test-workspace/tsc/passedFixtures/vue3_strictTemplate/inheritAttrs_unknownTag/main.vue rename to test-workspace/tsc/passedFixtures/fallthroughAttributes_strictTemplate/unknownTag/main.vue diff --git a/test-workspace/tsc/passedFixtures/vue2_strictTemplate/tsconfig.json b/test-workspace/tsc/passedFixtures/vue2_strictTemplate/tsconfig.json index 2011fc02db..141c51abdd 100644 --- a/test-workspace/tsc/passedFixtures/vue2_strictTemplate/tsconfig.json +++ b/test-workspace/tsc/passedFixtures/vue2_strictTemplate/tsconfig.json @@ -12,6 +12,5 @@ "../vue3_strictTemplate/#3140", "../vue3_strictTemplate/#3718", "../vue3_strictTemplate/intrinsicProps", - "../vue3_strictTemplate/inheritAttrs", ] } diff --git a/test-workspace/tsc/tsconfig.json b/test-workspace/tsc/tsconfig.json index 714f41f63f..ecc8bdbcc3 100644 --- a/test-workspace/tsc/tsconfig.json +++ b/test-workspace/tsc/tsconfig.json @@ -14,6 +14,8 @@ { "path": "./passedFixtures/#3819" }, { "path": "./passedFixtures/#4503" }, { "path": "./passedFixtures/core#9923" }, + { "path": "./passedFixtures/fallthroughAttributes" }, + { "path": "./passedFixtures/fallthroughAttributes_strictTemplate" }, { "path": "./passedFixtures/noPropertyAccessFromIndexSignature" }, // { "path": "./passedFixtures/petite-vue" }, { "path": "./passedFixtures/pug" }, From 180af0bed813b1e90ddab437f86ce5919373a713 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 27 Aug 2024 23:29:28 +0800 Subject: [PATCH 08/93] fix(language-core): fix __VLS_template returns when skipTemplateCodegen enabled --- packages/language-core/lib/codegen/script/template.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 582b9a1345..3a895a603c 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -39,7 +39,7 @@ export function* generateTemplate( const templateUsageVars = [...getTemplateUsageVars(options, ctx)]; yield `// @ts-ignore${newLine}`; yield `[${templateUsageVars.join(', ')}]${newLine}`; - yield `return [{}, {}] as const${endOfLine}`; + yield `return { slots: {}, refs: {}, attrs: {} }${endOfLine}`; yield `}${newLine}`; } } From 5936c8281888227e66bdb86c809222c944cc2c5b Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 28 Aug 2024 00:13:27 +0800 Subject: [PATCH 09/93] refactor(language-core): reduce virtual code generated by component tags (#4714) --- .../lib/codegen/script/globalTypes.ts | 9 +- .../language-core/lib/codegen/script/index.ts | 1 + .../lib/codegen/script/template.ts | 2 +- .../lib/codegen/template/context.ts | 7 +- .../lib/codegen/template/element.ts | 139 +++++++----------- .../lib/codegen/template/elementChildren.ts | 1 + .../lib/codegen/template/elementProps.ts | 32 +++- .../lib/codegen/template/index.ts | 15 +- .../lib/codegen/template/templateChild.ts | 2 +- packages/language-core/lib/plugins/vue-tsx.ts | 5 + packages/language-core/lib/types.ts | 3 + packages/language-server/lib/initialize.ts | 1 + packages/language-server/node.ts | 1 + .../language-server/tests/completions.spec.ts | 6 + 14 files changed, 123 insertions(+), 101 deletions(-) diff --git a/packages/language-core/lib/codegen/script/globalTypes.ts b/packages/language-core/lib/codegen/script/globalTypes.ts index 70125ca668..9638adef0f 100644 --- a/packages/language-core/lib/codegen/script/globalTypes.ts +++ b/packages/language-core/lib/codegen/script/globalTypes.ts @@ -67,7 +67,10 @@ declare global { function __VLS_nonNullable(t: T): T extends null | undefined ? never : T; type __VLS_SelfComponent = string extends N ? {} : N extends string ? { [P in N]: C } : {}; - type __VLS_WithComponent = + type __VLS_WithComponent = + N1 extends keyof Ctx ? N1 extends N0 ? Pick : { [K in N0]: Ctx[N1] } : + N2 extends keyof Ctx ? N2 extends N0 ? Pick : { [K in N0]: Ctx[N2] } : + N3 extends keyof Ctx ? N3 extends N0 ? Pick : { [K in N0]: Ctx[N3] } : N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : @@ -88,10 +91,10 @@ declare global { : (_: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record'} } }; function __VLS_elementAsFunction(tag: T, endTag?: T): (_: T${vueCompilerOptions.strictTemplates ? '' : ' & Record'}) => void; function __VLS_functionalComponentArgsRest any>(t: T): Parameters['length'] extends 2 ? [any] : []; - function __VLS_pickFunctionalComponentCtx(comp: T, compInstance: K): __VLS_PickNotAny< + function __VLS_pickFunctionalComponentCtx(comp: T, compInstance: K): NonNullable<__VLS_PickNotAny< '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: infer Ctx } ? Ctx : never : any , T extends (props: any, ctx: infer Ctx) => any ? Ctx : any - >; + >>; type __VLS_FunctionalComponentProps = '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: { props?: infer P } } ? NonNullable

: never : T extends (props: infer P, ...args: any) => any ? P : diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index ec48ad0a81..92da84d736 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -45,6 +45,7 @@ export interface ScriptCodegenOptions { scriptSetupRanges: ScriptSetupRanges | undefined; templateCodegen: TemplateCodegenContext & { codes: Code[]; } | undefined; globalTypes: boolean; + edited: boolean; getGeneratedLength: () => number; linkedCodeMappings: Mapping[]; } diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 3a895a603c..c7c8a9cbcf 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -23,7 +23,7 @@ export function* generateTemplate( else { yield `const __VLS_template = (() => {${newLine}`; } - const templateCodegenCtx = createTemplateCodegenContext(new Set()); + const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), edited: options.edited }); yield `const __VLS_template_return = () => {${newLine}`; yield* generateCtx(options, isClassComponent); yield* generateTemplateContext(options, templateCodegenCtx); diff --git a/packages/language-core/lib/codegen/template/context.ts b/packages/language-core/lib/codegen/template/context.ts index 480643d1a8..85c1548aa1 100644 --- a/packages/language-core/lib/codegen/template/context.ts +++ b/packages/language-core/lib/codegen/template/context.ts @@ -57,7 +57,7 @@ const _codeFeatures = { export type TemplateCodegenContext = ReturnType; -export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCodegenOptions['scriptSetupBindingNames']) { +export function createTemplateCodegenContext(options: Pick) { let ignoredError = false; let expectErrorToken: { errors: number; @@ -190,6 +190,9 @@ export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCo } }, generateAutoImportCompletion: function* (): Generator { + if (!options.edited) { + return; + } const all = [...accessExternalVariables.entries()]; if (!all.some(([_, offsets]) => offsets.size)) { return; @@ -198,7 +201,7 @@ export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCo yield `[`; for (const [varName, offsets] of all) { for (const offset of offsets) { - if (scriptSetupBindingNames.has(varName)) { + if (options.scriptSetupBindingNames.has(varName)) { // #3409 yield [ varName, diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index 42026ad78b..2a3edac1da 100644 --- a/packages/language-core/lib/codegen/template/element.ts +++ b/packages/language-core/lib/codegen/template/element.ts @@ -23,8 +23,7 @@ export function* generateComponent( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, node: CompilerDOM.ElementNode, - currentComponent: CompilerDOM.ElementNode | undefined, - componentCtxVar: string | undefined + currentComponent: CompilerDOM.ElementNode | undefined ): Generator { const startTagOffset = node.loc.start.offset + options.template.content.substring(node.loc.start.offset).indexOf(node.tag); const endTagOffset = !node.isSelfClosing && options.template.lang === 'html' ? node.loc.start.offset + node.loc.source.lastIndexOf(node.tag) : undefined; @@ -137,40 +136,26 @@ export function* generateComponent( yield `)${endOfLine}`; } else if (!isComponentTag) { - yield `// @ts-ignore${newLine}`; - yield `const ${var_originalComponent} = ({} as `; - for (const componentName of possibleOriginalNames) { - yield `'${componentName}' extends keyof typeof __VLS_ctx ? { '${getCanonicalComponentName(node.tag)}': typeof __VLS_ctx`; - yield* generatePropertyAccess(options, ctx, componentName); - yield ` }: `; - } - yield `typeof __VLS_resolvedLocalAndGlobalComponents)${newLine}`; - yield* generatePropertyAccess( - options, - ctx, - getCanonicalComponentName(node.tag), + yield `const ${var_originalComponent} = __VLS_resolvedLocalAndGlobalComponents.`; + yield* generateCanonicalComponentName( + node.tag, startTagOffset, - ctx.codeFeatures.verification + { + // with hover support + ...ctx.codeFeatures.withoutHighlightAndCompletionAndNavigation, + ...ctx.codeFeatures.verification, + } ); - yield endOfLine; + yield `${endOfLine}`; - // hover support - for (const offset of tagOffsets) { - yield `({} as { ${getCanonicalComponentName(node.tag)}: typeof ${var_originalComponent} }).`; - yield* generateCanonicalComponentName( - node.tag, - offset, - ctx.codeFeatures.withoutHighlightAndCompletionAndNavigation - ); - yield endOfLine; - } const camelizedTag = camelize(node.tag); if (variableNameRegex.test(camelizedTag)) { // renaming / find references support + yield `/** @type { [`; for (const tagOffset of tagOffsets) { for (const shouldCapitalize of (node.tag[0] === node.tag[0].toUpperCase() ? [false] : [true, false])) { const expectName = shouldCapitalize ? capitalize(camelizedTag) : camelizedTag; - yield `__VLS_components.`; + yield `typeof __VLS_components.`; yield* generateCamelized( shouldCapitalize ? capitalize(node.tag) : node.tag, tagOffset, @@ -181,17 +166,16 @@ export function* generateComponent( }, } ); - yield `;`; + yield `, `; } } - yield `${newLine}`; + yield `] } */${newLine}`; // auto import support - yield `// @ts-ignore${newLine}`; // #2304 - yield `[`; - for (const tagOffset of tagOffsets) { + if (options.edited) { + yield `// @ts-ignore${newLine}`; // #2304 yield* generateCamelized( capitalize(node.tag), - tagOffset, + startTagOffset, { completion: { isAdditional: true, @@ -199,9 +183,8 @@ export function* generateComponent( }, } ); - yield `,`; + yield `${endOfLine}`; } - yield `]${endOfLine}`; } } else { @@ -213,38 +196,17 @@ export function* generateComponent( yield* generateElementProps(options, ctx, node, props, false); yield `}))${endOfLine}`; - if (options.vueCompilerOptions.strictTemplates) { - // with strictTemplates, generate once for props type-checking + instance type - yield `const ${var_componentInstance} = ${var_functionalComponent}(`; - yield* wrapWith( - startTagOffset, - startTagOffset + node.tag.length, - ctx.codeFeatures.verification, - `{`, - ...generateElementProps(options, ctx, node, props, true, propsFailedExps), - `}` - ); - yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`; - } - else { - // without strictTemplates, this only for instance type - yield `const ${var_componentInstance} = ${var_functionalComponent}({`; - yield* generateElementProps(options, ctx, node, props, false); - yield `}, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`; - // and this for props type-checking - yield `({} as (props: __VLS_FunctionalComponentProps & Record) => void)(`; - yield* wrapWith( - startTagOffset, - startTagOffset + node.tag.length, - ctx.codeFeatures.verification, - `{`, - ...generateElementProps(options, ctx, node, props, true, propsFailedExps), - `}` - ); - yield `)${endOfLine}`; - } + yield `const ${var_componentInstance} = ${var_functionalComponent}(`; + yield* wrapWith( + startTagOffset, + startTagOffset + node.tag.length, + ctx.codeFeatures.verification, + `{`, + ...generateElementProps(options, ctx, node, props, true, propsFailedExps), + `}` + ); + yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`; - componentCtxVar = var_defineComponentCtx; currentComponent = node; for (const failedExp of propsFailedExps) { @@ -262,28 +224,14 @@ export function* generateComponent( } const refName = yield* generateVScope(options, ctx, node, props); + if (refName) { + ctx.usedComponentCtxVars.add(var_defineComponentCtx); + } - ctx.usedComponentCtxVars.add(componentCtxVar); const usedComponentEventsVar = yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents); - - if (var_defineComponentCtx && ctx.usedComponentCtxVars.has(var_defineComponentCtx)) { - yield `const ${componentCtxVar} = __VLS_nonNullable(__VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance}))${endOfLine}`; - if (refName) { - yield `// @ts-ignore${newLine}`; - if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL - && node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION - && node.codegenNode.props.properties.find(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for') - ) { - yield `(${refName} ??= []).push(${var_defineComponentCtx})`; - } else { - yield `${refName} = ${var_defineComponentCtx}`; - } - - yield endOfLine; - } - } if (usedComponentEventsVar) { - yield `let ${var_componentEmit}!: typeof ${componentCtxVar}.emit${endOfLine}`; + ctx.usedComponentCtxVars.add(var_defineComponentCtx); + yield `let ${var_componentEmit}!: typeof ${var_defineComponentCtx}.emit${endOfLine}`; yield `let ${var_componentEvents}!: __VLS_NormalizeEmits${endOfLine}`; } @@ -301,10 +249,27 @@ export function* generateComponent( const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode; if (slotDir) { - yield* generateComponentSlot(options, ctx, node, slotDir, currentComponent, componentCtxVar); + yield* generateComponentSlot(options, ctx, node, slotDir, currentComponent, var_defineComponentCtx); } else { - yield* generateElementChildren(options, ctx, node, currentComponent, componentCtxVar); + yield* generateElementChildren(options, ctx, node, currentComponent, var_defineComponentCtx); + } + + if (ctx.usedComponentCtxVars.has(var_defineComponentCtx)) { + yield `const ${var_defineComponentCtx} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})${endOfLine}`; + if (refName) { + yield `// @ts-ignore${newLine}`; + if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL + && node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION + && node.codegenNode.props.properties.find(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for') + ) { + yield `(${refName} ??= []).push(${var_defineComponentCtx})`; + } else { + yield `${refName} = ${var_defineComponentCtx}`; + } + + yield endOfLine; + } } } diff --git a/packages/language-core/lib/codegen/template/elementChildren.ts b/packages/language-core/lib/codegen/template/elementChildren.ts index 67e38651c1..a184a2c798 100644 --- a/packages/language-core/lib/codegen/template/elementChildren.ts +++ b/packages/language-core/lib/codegen/template/elementChildren.ts @@ -28,6 +28,7 @@ export function* generateElementChildren( && node.tagType !== CompilerDOM.ElementTypes.ELEMENT && node.tagType !== CompilerDOM.ElementTypes.TEMPLATE ) { + ctx.usedComponentCtxVars.add(componentCtxVar); yield `__VLS_nonNullable(${componentCtxVar}.slots).`; yield* wrapWith( node.children[0].loc.start.offset, diff --git a/packages/language-core/lib/codegen/template/elementProps.ts b/packages/language-core/lib/codegen/template/elementProps.ts index 780e9d9618..5d4960192a 100644 --- a/packages/language-core/lib/codegen/template/elementProps.ts +++ b/packages/language-core/lib/codegen/template/elementProps.ts @@ -109,6 +109,7 @@ export function* generateElementProps( if (shouldSpread) { yield `...{ `; } + const codeInfo = ctx.codeFeatures.withoutHighlightAndCompletion; const codes = wrapWith( prop.loc.start.offset, prop.loc.end.offset, @@ -121,8 +122,20 @@ export function* generateElementProps( propName, prop.arg.loc.start.offset, { - ...ctx.codeFeatures.withoutHighlightAndCompletion, - navigation: ctx.codeFeatures.withoutHighlightAndCompletion.navigation + ...codeInfo, + verification: options.vueCompilerOptions.strictTemplates + ? codeInfo.verification + : { + shouldReport(_source, code) { + if (String(code) === '2353' || String(code) === '2561') { + return false; + } + return typeof codeInfo.verification === 'object' + ? codeInfo.verification.shouldReport?.(_source, code) ?? true + : true; + }, + }, + navigation: codeInfo.navigation ? { resolveRenameNewName: camelize, resolveRenameEditText: shouldCamelize ? hyphenateAttr : undefined, @@ -183,6 +196,7 @@ export function* generateElementProps( if (shouldSpread) { yield `...{ `; } + const codeInfo = ctx.codeFeatures.withoutHighlightAndCompletion; const codes = conditionWrapWith( enableCodeFeatures, prop.loc.start.offset, @@ -195,7 +209,19 @@ export function* generateElementProps( prop.loc.start.offset, shouldCamelize ? { - ...ctx.codeFeatures.withoutHighlightAndCompletion, + ...codeInfo, + verification: options.vueCompilerOptions.strictTemplates + ? codeInfo.verification + : { + shouldReport(_source, code) { + if (String(code) === '2353' || String(code) === '2561') { + return false; + } + return typeof codeInfo.verification === 'object' + ? codeInfo.verification.shouldReport?.(_source, code) ?? true + : true; + }, + }, navigation: ctx.codeFeatures.withoutHighlightAndCompletion.navigation ? { resolveRenameNewName: camelize, diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index a338437e97..b6aa1f43ce 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -15,6 +15,7 @@ export interface TemplateCodegenOptions { template: NonNullable; scriptSetupBindingNames: Set; scriptSetupImportComponentNames: Set; + edited: boolean; templateRefNames: Map; hasDefineSlots?: boolean; slotsAssignName?: string; @@ -23,7 +24,7 @@ export interface TemplateCodegenOptions { } export function* generateTemplate(options: TemplateCodegenOptions): Generator { - const ctx = createTemplateCodegenContext(options.scriptSetupBindingNames); + const ctx = createTemplateCodegenContext(options); if (options.slotsAssignName) { ctx.addLocalVariable(options.slotsAssignName); @@ -105,15 +106,21 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator { - yield `let __VLS_resolvedLocalAndGlobalComponents!: {}`; + yield `let __VLS_resolvedLocalAndGlobalComponents!: Required<{}`; if (options.template.ast) { + const components = new Set(); for (const node of forEachElementNode(options.template.ast)) { if ( node.tagType === CompilerDOM.ElementTypes.COMPONENT && node.tag.toLowerCase() !== 'component' && !node.tag.includes('.') // namespace tag ) { - yield ` & __VLS_WithComponent<'${getCanonicalComponentName(node.tag)}', typeof __VLS_localComponents, `; + if (components.has(node.tag)) { + continue; + } + components.add(node.tag); + yield newLine; + yield ` & __VLS_WithComponent<'${getCanonicalComponentName(node.tag)}', typeof __VLS_ctx, typeof __VLS_localComponents, `; yield getPossibleOriginalComponentNames(node.tag, false) .map(name => `"${name}"`) .join(', '); @@ -121,7 +128,7 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator${endOfLine}`; } } diff --git a/packages/language-core/lib/codegen/template/templateChild.ts b/packages/language-core/lib/codegen/template/templateChild.ts index 999c0d4709..ad3608242f 100644 --- a/packages/language-core/lib/codegen/template/templateChild.ts +++ b/packages/language-core/lib/codegen/template/templateChild.ts @@ -80,7 +80,7 @@ export function* generateTemplateChild( yield* generateElement(options, ctx, node, currentComponent, componentCtxVar); } else { - yield* generateComponent(options, ctx, node, currentComponent, componentCtxVar); + yield* generateComponent(options, ctx, node, currentComponent); } } } diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index a84dfef4b2..c5e1308e9c 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -9,6 +9,8 @@ import type { Code, Sfc, VueLanguagePlugin } from '../types'; export const tsCodegen = new WeakMap>(); +const fileEditTimes = new Map(); + const plugin: VueLanguagePlugin = ctx => { return { @@ -92,6 +94,7 @@ function createTsx( compilerOptions: ctx.compilerOptions, vueCompilerOptions: ctx.vueCompilerOptions, template: _sfc.template, + edited: ctx.vueCompilerOptions.__test || (fileEditTimes.get(fileName) ?? 0) >= 2, scriptSetupBindingNames: scriptSetupBindingNames(), scriptSetupImportComponentNames: scriptSetupImportComponentNames(), templateRefNames: new Map(), @@ -157,9 +160,11 @@ function createTsx( templateCodegen: _template, compilerOptions: ctx.compilerOptions, vueCompilerOptions: ctx.vueCompilerOptions, + edited: ctx.vueCompilerOptions.__test || (fileEditTimes.get(fileName) ?? 0) >= 2, getGeneratedLength: () => generatedLength, linkedCodeMappings, }); + fileEditTimes.set(fileName, (fileEditTimes.get(fileName) ?? 0) + 1); let current = codegen.next(); diff --git a/packages/language-core/lib/types.ts b/packages/language-core/lib/types.ts index 11860c48de..57dfd05898 100644 --- a/packages/language-core/lib/types.ts +++ b/packages/language-core/lib/types.ts @@ -53,6 +53,9 @@ export interface VueCompilerOptions { experimentalDefinePropProposal: 'kevinEdition' | 'johnsonEdition' | false; experimentalResolveStyleCssClasses: 'scoped' | 'always' | 'never'; experimentalModelPropName: Record | Record[]>>; + + // internal + __test?: boolean; } export const validVersions = [2, 2.1] as const; diff --git a/packages/language-server/lib/initialize.ts b/packages/language-server/lib/initialize.ts index 450353bfcb..bfb91fb916 100644 --- a/packages/language-server/lib/initialize.ts +++ b/packages/language-server/lib/initialize.ts @@ -37,6 +37,7 @@ export function initialize( compilerOptions = ts.getDefaultCompilerOptions(); vueCompilerOptions = resolveVueCompilerOptions({}); } + vueCompilerOptions.__test = params.initializationOptions.typescript.disableAutoImportCache; updateFileWatcher(vueCompilerOptions); return { languagePlugins: [createVueLanguagePlugin2( diff --git a/packages/language-server/node.ts b/packages/language-server/node.ts index 541467169d..65b4c4d787 100644 --- a/packages/language-server/node.ts +++ b/packages/language-server/node.ts @@ -26,6 +26,7 @@ connection.onInitialize(params => { vueOptions: resolveVueCompilerOptions({}), options: ts.getDefaultCompilerOptions(), }; + commandLine.vueOptions.__test = params.initializationOptions.typescript.disableAutoImportCache; return { languagePlugins: [createVueLanguagePlugin2( ts, diff --git a/packages/language-server/tests/completions.spec.ts b/packages/language-server/tests/completions.spec.ts index 96c9624688..044f28966f 100644 --- a/packages/language-server/tests/completions.spec.ts +++ b/packages/language-server/tests/completions.spec.ts @@ -338,6 +338,12 @@ describe('Completions', async () => { }, }, ], + "commitCharacters": [ + ".", + ",", + ";", + "(", + ], "detail": "Add import from "./ComponentForAutoImport.vue" (property) default: DefineComponent<{}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly>, {}, {}>", "documentation": { From 13924d1d2fb8a6f11c267dd4612c366502c1cbc5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 28 Aug 2024 02:55:26 +0800 Subject: [PATCH 10/93] refactor(language-core): do not wrap template virtual code with function (#4731) --- .../lib/codegen/script/component.ts | 2 +- .../lib/codegen/script/globalTypes.ts | 6 +- .../language-core/lib/codegen/script/index.ts | 27 ++-- .../lib/codegen/script/internalComponent.ts | 3 +- .../lib/codegen/script/scriptSetup.ts | 4 +- .../lib/codegen/script/template.ts | 131 ++++++++---------- .../lib/codegen/template/index.ts | 2 +- .../tsc/tests/__snapshots__/dts.spec.ts.snap | 92 +++++++----- 8 files changed, 130 insertions(+), 137 deletions(-) diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index fa49ad1791..457ba0c133 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -142,7 +142,7 @@ export function* generatePropsOption( optionExpCodes.push(generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation)); } if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) { - let attrsType = `ReturnType['attrs']`; + let attrsType = `typeof __VLS_templateResult['attrs']`; if (hasEmitsOption) { attrsType = `Omit<${attrsType}, \`on\${string}\`>`; } diff --git a/packages/language-core/lib/codegen/script/globalTypes.ts b/packages/language-core/lib/codegen/script/globalTypes.ts index 9638adef0f..541a1512d3 100644 --- a/packages/language-core/lib/codegen/script/globalTypes.ts +++ b/packages/language-core/lib/codegen/script/globalTypes.ts @@ -66,11 +66,7 @@ declare global { function __VLS_makeOptional(t: T): { [K in keyof T]?: T[K] }; function __VLS_nonNullable(t: T): T extends null | undefined ? never : T; - type __VLS_SelfComponent = string extends N ? {} : N extends string ? { [P in N]: C } : {}; - type __VLS_WithComponent = - N1 extends keyof Ctx ? N1 extends N0 ? Pick : { [K in N0]: Ctx[N1] } : - N2 extends keyof Ctx ? N2 extends N0 ? Pick : { [K in N0]: Ctx[N2] } : - N3 extends keyof Ctx ? N3 extends N0 ? Pick : { [K in N0]: Ctx[N3] } : + type __VLS_WithComponent = N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index 92da84d736..322eaebd01 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -101,9 +101,16 @@ export function* generateScript(options: ScriptCodegenOptions): Generator {`; + yield* generateTemplate(options, ctx, true); + yield `},${newLine}`; + yield generateSfcBlockSection(options.sfc.script, classBlockEnd, options.sfc.script.content.length, codeFeatures.all); + } } else { yield generateSfcBlockSection(options.sfc.script, 0, options.sfc.script.content.length, codeFeatures.all); @@ -118,12 +125,7 @@ export function* generateScript(options: ScriptCodegenOptions): Generator { if (options.sfc.scriptSetup && options.scriptSetupRanges) { - yield `let __VLS_defineComponent!: typeof import('${options.vueCompilerOptions.lib}').defineComponent${endOfLine}`; - yield `const __VLS_internalComponent = __VLS_defineComponent({${newLine}`; + yield `const __VLS_internalComponent = (await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`; yield `setup() {${newLine}`; yield `return {${newLine}`; if (ctx.bypassDefineComponent) { diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 6c6c014597..0e99fa781c 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -280,8 +280,8 @@ function* generateSetupFunction( yield* generateModelEmits(options, scriptSetup, scriptSetupRanges); yield* generateStyleModules(options, ctx); yield* generateTemplate(options, ctx, false); - yield `type __VLS_Refs = ReturnType['refs']${endOfLine}`; - yield `type __VLS_Slots = ReturnType['slots']${endOfLine}`; + yield `type __VLS_Refs = typeof __VLS_templateResult['refs']${endOfLine}`; + yield `type __VLS_Slots = typeof __VLS_templateResult['slots']${endOfLine}`; if (syntax) { if (!options.vueCompilerOptions.skipTemplateCodegen && (options.templateCodegen?.hasSlot || scriptSetupRanges?.slots.define)) { diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index c7c8a9cbcf..7507e2dffd 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -4,114 +4,99 @@ import { getSlotsPropertyName, hyphenateTag } from '../../utils/shared'; import { endOfLine, newLine } from '../common'; import { TemplateCodegenContext, createTemplateCodegenContext } from '../template/context'; import { forEachInterpolationSegment } from '../template/interpolation'; +import { generateStyleScopedClasses } from '../template/styleScopedClasses'; import type { ScriptCodegenContext } from './context'; import { codeFeatures, type ScriptCodegenOptions } from './index'; import { generateInternalComponent } from './internalComponent'; -import { generateStyleScopedClasses } from '../template/styleScopedClasses'; - -export function* generateTemplate( - options: ScriptCodegenOptions, - ctx: ScriptCodegenContext, - isClassComponent: boolean -): Generator { - ctx.generatedTemplate = true; - if (!options.vueCompilerOptions.skipTemplateCodegen) { - if (isClassComponent) { - yield `__VLS_template = (() => {${newLine}`; - } - else { - yield `const __VLS_template = (() => {${newLine}`; - } - const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), edited: options.edited }); - yield `const __VLS_template_return = () => {${newLine}`; - yield* generateCtx(options, isClassComponent); - yield* generateTemplateContext(options, templateCodegenCtx); - yield* generateExportOptions(options); - yield* generateConstNameOption(options); - yield `}${endOfLine}`; - yield* generateInternalComponent(options, ctx, templateCodegenCtx); - yield `return __VLS_template_return${endOfLine}`; - yield `})()${endOfLine}`; +export function* generateTemplateCtx(options: ScriptCodegenOptions, isClassComponent: boolean): Generator { + const types = []; + if (isClassComponent) { + types.push(`typeof this`); } else { - yield `function __VLS_template() {${newLine}`; - const templateUsageVars = [...getTemplateUsageVars(options, ctx)]; - yield `// @ts-ignore${newLine}`; - yield `[${templateUsageVars.join(', ')}]${newLine}`; - yield `return { slots: {}, refs: {}, attrs: {} }${endOfLine}`; - yield `}${newLine}`; + types.push(`InstanceType<__VLS_PickNotAny {}>>`); } + if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileBaseName.endsWith(ext))) { + types.push(`typeof globalThis`); + } + if (options.sfc.styles.some(style => style.module)) { + types.push(`__VLS_StyleModules`); + } + yield `let __VLS_ctx!: ${types.join(' & ')}${endOfLine}`; } -function* generateExportOptions(options: ScriptCodegenOptions): Generator { - yield newLine; - yield `const __VLS_componentsOption = `; +export function* generateTemplateComponents(options: ScriptCodegenOptions): Generator { + const exps: Code[] = []; + if (options.sfc.script && options.scriptRanges?.exportDefault?.componentsOption) { - const componentsOption = options.scriptRanges.exportDefault.componentsOption; - yield [ + const { componentsOption } = options.scriptRanges.exportDefault; + exps.push([ options.sfc.script.content.substring(componentsOption.start, componentsOption.end), 'script', componentsOption.start, codeFeatures.navigation, - ]; - } - else { - yield `{}`; + ]); } - yield endOfLine; -} -function* generateConstNameOption(options: ScriptCodegenOptions): Generator { + let nameType: Code | undefined; if (options.sfc.script && options.scriptRanges?.exportDefault?.nameOption) { - const nameOption = options.scriptRanges.exportDefault.nameOption; - yield `const __VLS_name = `; - yield `${options.sfc.script.content.substring(nameOption.start, nameOption.end)} as const`; - yield endOfLine; + const { nameOption } = options.scriptRanges.exportDefault; + nameType = options.sfc.script.content.substring(nameOption.start, nameOption.end); } else if (options.sfc.scriptSetup) { yield `let __VLS_name!: '${options.scriptSetupRanges?.options.name ?? options.fileBaseName.substring(0, options.fileBaseName.lastIndexOf('.'))}'${endOfLine}`; + nameType = 'typeof __VLS_name'; } - else { - yield `const __VLS_name = undefined${endOfLine}`; + if (nameType) { + exps.push(`{} as { + [K in ${nameType}]: typeof __VLS_internalComponent + & (new () => { + ${getSlotsPropertyName(options.vueCompilerOptions.target)}: typeof ${options.scriptSetupRanges?.slots?.name ?? '__VLS_slots'} + }) + }`); + } + + exps.push(`{} as NonNullable`); + exps.push(`{} as __VLS_GlobalComponents`); + exps.push(`{} as typeof __VLS_ctx`); + + yield `const __VLS_components = {${newLine}`; + for (const type of exps) { + yield `...`; + yield type; + yield `,${newLine}`; } + yield `}${endOfLine}`; } -function* generateCtx( +export function* generateTemplate( options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, isClassComponent: boolean ): Generator { - yield `let __VLS_ctx!: `; - if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileBaseName.endsWith(ext))) { - yield `typeof globalThis & `; - } - if (!isClassComponent) { - yield `InstanceType<__VLS_PickNotAny {}>>`; + ctx.generatedTemplate = true; + + if (!options.vueCompilerOptions.skipTemplateCodegen) { + const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), edited: options.edited }); + yield* generateTemplateCtx(options, isClassComponent); + yield* generateTemplateComponents(options); + yield* generateTemplateBody(options, templateCodegenCtx); + yield* generateInternalComponent(options, ctx, templateCodegenCtx); } else { - yield `typeof this`; - } - /* CSS Module */ - if (options.sfc.styles.some(style => style.module)) { - yield ` & __VLS_StyleModules`; + const templateUsageVars = [...getTemplateUsageVars(options, ctx)]; + yield `// @ts-ignore${newLine}`; + yield `[${templateUsageVars.join(', ')}]${newLine}`; + yield `const __VLS_templateResult { slots: {}, refs: {}, attrs: {} }${endOfLine}`; } - yield endOfLine; } -function* generateTemplateContext( +function* generateTemplateBody( options: ScriptCodegenOptions, templateCodegenCtx: TemplateCodegenContext ): Generator { - /* Components */ - yield `/* Components */${newLine}`; - yield `let __VLS_otherComponents!: NonNullable & typeof __VLS_componentsOption${endOfLine}`; - yield `let __VLS_own!: __VLS_SelfComponent { ${getSlotsPropertyName(options.vueCompilerOptions.target)}: typeof ${options.scriptSetupRanges?.slots?.name ?? '__VLS_slots'} })>${endOfLine}`; - yield `let __VLS_localComponents!: typeof __VLS_otherComponents & Omit${endOfLine}`; - yield `let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents & typeof __VLS_ctx${endOfLine}`; // for html completion, TS references... - - /* Style Scoped */ const firstClasses = new Set(); - yield `/* Style Scoped */${newLine}`; yield `let __VLS_styleScopedClasses!: {}`; for (let i = 0; i < options.sfc.styles.length; i++) { const style = options.sfc.styles[i]; @@ -155,7 +140,7 @@ function* generateTemplateContext( } } - yield `return {${newLine}`; + yield `const __VLS_templateResult = {`; yield `slots: ${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'},${newLine}`; yield `refs: __VLS_refs as __VLS_PickRefsExpose,${newLine}`; yield `attrs: {} as Partial,${newLine}`; diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index b6aa1f43ce..c68a820da3 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -120,7 +120,7 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator `"${name}"`) .join(', '); diff --git a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap index d708893965..878dcd52ac 100644 --- a/packages/tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/tsc/tests/__snapshots__/dts.spec.ts.snap @@ -634,24 +634,30 @@ export {}; `; exports[`vue-tsc-dts > Input: template-slots/component.vue, Output: template-slots/component.vue.d.ts 1`] = ` -"declare const __VLS_template: () => { +"declare var __VLS_0: {}; +declare var __VLS_1: { + num: number; +}; +declare var __VLS_2: { + str: string; +}; +declare var __VLS_3: { + num: number; + str: string; +}; +declare var __VLS_inheritedAttrs: {}; +declare const __VLS_refs: {}; +declare const __VLS_templateResult: { slots: { - "no-bind"?(_: {}): any; - default?(_: { - num: number; - }): any; - "named-slot"?(_: { - str: string; - }): any; - vbind?(_: { - num: number; - str: string; - }): any; + "no-bind"?(_: typeof __VLS_0): any; + default?(_: typeof __VLS_1): any; + "named-slot"?(_: typeof __VLS_2): any; + vbind?(_: typeof __VLS_3): any; }; - refs: __VLS_PickRefsExpose<{}>; - attrs: Partial<{}>; + refs: __VLS_PickRefsExpose; + attrs: Partial; }; -type __VLS_Slots = ReturnType['slots']; +type __VLS_Slots = typeof __VLS_templateResult['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; declare const _default: __VLS_WithTemplateSlots; export default _default; @@ -665,7 +671,9 @@ type __VLS_WithTemplateSlots = T & { exports[`vue-tsc-dts > Input: template-slots/component-define-slots.vue, Output: template-slots/component-define-slots.vue.d.ts 1`] = ` "import { VNode } from 'vue'; -declare const __VLS_template: () => { +declare var __VLS_inheritedAttrs: {}; +declare const __VLS_refs: {}; +declare const __VLS_templateResult: { slots: Readonly<{ default: (props: { num: number; @@ -691,10 +699,10 @@ declare const __VLS_template: () => { }) => VNode[]; 'no-bind': () => VNode[]; }; - refs: __VLS_PickRefsExpose<{}>; - attrs: Partial<{}>; + refs: __VLS_PickRefsExpose; + attrs: Partial; }; -type __VLS_Slots = ReturnType['slots']; +type __VLS_Slots = typeof __VLS_templateResult['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; declare const _default: __VLS_WithTemplateSlots; export default _default; @@ -707,7 +715,9 @@ type __VLS_WithTemplateSlots = T & { `; exports[`vue-tsc-dts > Input: template-slots/component-destructuring.vue, Output: template-slots/component-destructuring.vue.d.ts 1`] = ` -"declare const __VLS_template: () => { +"declare var __VLS_inheritedAttrs: {}; +declare const __VLS_refs: {}; +declare const __VLS_templateResult: { slots: Readonly<{ bottom: (props: { num: number; @@ -717,10 +727,10 @@ exports[`vue-tsc-dts > Input: template-slots/component-destructuring.vue, Output num: number; }) => any[]; }; - refs: __VLS_PickRefsExpose<{}>; - attrs: Partial<{}>; + refs: __VLS_PickRefsExpose; + attrs: Partial; }; -type __VLS_Slots = ReturnType['slots']; +type __VLS_Slots = typeof __VLS_templateResult['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; declare const _default: __VLS_WithTemplateSlots; export default _default; @@ -733,24 +743,30 @@ type __VLS_WithTemplateSlots = T & { `; exports[`vue-tsc-dts > Input: template-slots/component-no-script.vue, Output: template-slots/component-no-script.vue.d.ts 1`] = ` -"declare const __VLS_template: () => { +"declare var __VLS_0: {}; +declare var __VLS_1: { + num: number; +}; +declare var __VLS_2: { + str: string; +}; +declare var __VLS_3: { + num: number; + str: string; +}; +declare var __VLS_inheritedAttrs: {}; +declare const __VLS_refs: {}; +declare const __VLS_templateResult: { slots: { - "no-bind"?(_: {}): any; - default?(_: { - num: number; - }): any; - "named-slot"?(_: { - str: string; - }): any; - vbind?(_: { - num: number; - str: string; - }): any; + "no-bind"?(_: typeof __VLS_0): any; + default?(_: typeof __VLS_1): any; + "named-slot"?(_: typeof __VLS_2): any; + vbind?(_: typeof __VLS_3): any; }; - refs: __VLS_PickRefsExpose<{}>; - attrs: Partial<{}>; + refs: __VLS_PickRefsExpose; + attrs: Partial; }; -type __VLS_Slots = ReturnType['slots']; +type __VLS_Slots = typeof __VLS_templateResult['slots']; declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>, {}, {}>; declare const _default: __VLS_WithTemplateSlots; export default _default; From a162f043abcb54af9c372b5d82b970bb0f0a2fbd Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 29 Aug 2024 06:36:24 +0800 Subject: [PATCH 11/93] refactor(component-meta): remove deprecated functions and exports --- packages/component-meta/index.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/component-meta/index.ts b/packages/component-meta/index.ts index ec0300e6d1..f38e447dfb 100644 --- a/packages/component-meta/index.ts +++ b/packages/component-meta/index.ts @@ -4,16 +4,6 @@ import type { MetaCheckerOptions } from './lib/types'; export * from './lib/types'; -/** - * @deprecated Use `createCheckerByJson` instead. - */ -export const createComponentMetaCheckerByJsonConfig = createCheckerByJson; - -/** - * @deprecated Use `createChecker` instead. - */ -export const createComponentMetaChecker = createChecker; - export function createCheckerByJson( rootPath: string, json: any, From 38c1e48eca720e0c8faf892d1aff8b44fede3951 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 29 Aug 2024 07:09:22 +0800 Subject: [PATCH 12/93] refactor: write real files to FS for shared global types (#4736) --- package.json | 16 +- packages/component-meta/lib/base.ts | 46 ++-- packages/language-core/index.ts | 7 +- .../language-core/lib/codegen/globalTypes.ts | 125 +++++++++++ .../language-core/lib/codegen/localTypes.ts | 148 +++++++++++++ .../lib/codegen/script/component.ts | 8 +- .../lib/codegen/script/context.ts | 120 +--------- .../lib/codegen/script/globalTypes.ts | 146 ------------- .../language-core/lib/codegen/script/index.ts | 16 +- .../lib/codegen/script/scriptSetup.ts | 12 +- .../lib/codegen/script/template.ts | 15 +- .../lib/codegen/template/objectProperty.ts | 4 +- packages/language-core/lib/languagePlugin.ts | 91 +------- packages/language-core/lib/plugins/vue-tsx.ts | 1 - packages/language-core/lib/types.ts | 1 - .../schemas/vue-tsconfig.schema.json | 2 +- packages/language-server/lib/initialize.ts | 48 +++- packages/language-server/node.ts | 17 +- .../language-server/tests/completions.spec.ts | 14 -- .../language-server/tests/definitions.spec.ts | 12 - .../language-server/tests/inlayHints.spec.ts | 13 -- .../language-server/tests/references.spec.ts | 12 - .../language-server/tests/renaming.spec.ts | 15 -- .../language-service/tests/utils/format.ts | 9 +- packages/tsc/index.ts | 32 ++- .../tsc/tests/__snapshots__/dts.spec.ts.snap | 53 +++-- packages/tsc/tests/dts.spec.ts | 25 ++- packages/typescript-plugin/index.ts | 42 +++- pnpm-lock.yaml | 206 +++++++++--------- 29 files changed, 608 insertions(+), 648 deletions(-) create mode 100644 packages/language-core/lib/codegen/globalTypes.ts create mode 100644 packages/language-core/lib/codegen/localTypes.ts delete mode 100644 packages/language-core/lib/codegen/script/globalTypes.ts diff --git a/package.json b/package.json index 2e95edd2db..8ca48ee9a7 100644 --- a/package.json +++ b/package.json @@ -32,14 +32,14 @@ }, "pnpm": { "overrides": { - "@volar/kit": "https://pkg.pr.new/volarjs/volar.js/@volar/kit@28cbdee", - "@volar/language-core": "https://pkg.pr.new/volarjs/volar.js/@volar/language-core@28cbdee", - "@volar/language-server": "https://pkg.pr.new/volarjs/volar.js/@volar/language-server@28cbdee", - "@volar/language-service": "https://pkg.pr.new/volarjs/volar.js/@volar/language-service@28cbdee", - "@volar/source-map": "https://pkg.pr.new/volarjs/volar.js/@volar/source-map@28cbdee", - "@volar/test-utils": "https://pkg.pr.new/volarjs/volar.js/@volar/test-utils@28cbdee", - "@volar/typescript": "https://pkg.pr.new/volarjs/volar.js/@volar/typescript@28cbdee", - "@volar/vscode": "https://pkg.pr.new/volarjs/volar.js/@volar/vscode@28cbdee", + "@volar/kit": "https://pkg.pr.new/volarjs/volar.js/@volar/kit@0e1be44", + "@volar/language-core": "https://pkg.pr.new/volarjs/volar.js/@volar/language-core@0e1be44", + "@volar/language-server": "https://pkg.pr.new/volarjs/volar.js/@volar/language-server@0e1be44", + "@volar/language-service": "https://pkg.pr.new/volarjs/volar.js/@volar/language-service@0e1be44", + "@volar/source-map": "https://pkg.pr.new/volarjs/volar.js/@volar/source-map@0e1be44", + "@volar/test-utils": "https://pkg.pr.new/volarjs/volar.js/@volar/test-utils@0e1be44", + "@volar/typescript": "https://pkg.pr.new/volarjs/volar.js/@volar/typescript@0e1be44", + "@volar/vscode": "https://pkg.pr.new/volarjs/volar.js/@volar/vscode@0e1be44", "volar-service-typescript": "https://pkg.pr.new/volarjs/services/volar-service-typescript@177b9ed", "inquirer": "9.2.23" } diff --git a/packages/component-meta/lib/base.ts b/packages/component-meta/lib/base.ts index e71738e5e4..cda971c749 100644 --- a/packages/component-meta/lib/base.ts +++ b/packages/component-meta/lib/base.ts @@ -1,19 +1,19 @@ +import { TypeScriptProjectHost, createLanguageServiceHost, resolveFileLanguageId } from '@volar/typescript'; import * as vue from '@vue/language-core'; -import type * as ts from 'typescript'; import * as path from 'path-browserify'; +import type * as ts from 'typescript'; import { code as typeHelpersCode } from 'vue-component-type-helpers'; import { code as vue2TypeHelpersCode } from 'vue-component-type-helpers/vue2'; -import { TypeScriptProjectHost, createLanguageServiceHost, resolveFileLanguageId } from '@volar/typescript'; import type { - MetaCheckerOptions, ComponentMeta, + Declaration, EventMeta, ExposeMeta, + MetaCheckerOptions, PropertyMeta, PropertyMetaSchema, - SlotMeta, - Declaration + SlotMeta } from './types'; export * from './types'; @@ -83,16 +83,11 @@ export function baseCreate( ]; }; - const vueLanguagePlugin = vue.createVueLanguagePlugin2( + const vueLanguagePlugin = vue.createVueLanguagePlugin( ts, - id => id, - vue.createRootFileChecker( - projectHost.getProjectVersion ? () => projectHost.getProjectVersion!() : undefined, - () => projectHost.getScriptFileNames(), - ts.sys.useCaseSensitiveFileNames - ), projectHost.getCompilationSettings(), - commandLine.vueOptions + commandLine.vueOptions, + id => id ); const language = vue.createLanguage( [ @@ -140,6 +135,31 @@ export function baseCreate( const { languageServiceHost } = createLanguageServiceHost(ts, ts.sys, language, s => s, projectHost); const tsLs = ts.createLanguageService(languageServiceHost); + const fileExists = languageServiceHost.fileExists.bind(languageServiceHost); + const getScriptSnapshot = languageServiceHost.getScriptSnapshot.bind(languageServiceHost); + const globalTypesName = `__globalTypes_${commandLine.vueOptions.target}_${commandLine.vueOptions.strictTemplates}.d.ts`; + const snapshots = new Map(); + languageServiceHost.fileExists = path => { + if (path.endsWith(globalTypesName)) { + return true; + } + return fileExists(path); + }; + languageServiceHost.getScriptSnapshot = path => { + if (path.endsWith(globalTypesName)) { + if (!snapshots.has(path)) { + const contents = vue.generateGlobalTypes(commandLine.vueOptions.lib, commandLine.vueOptions.target, commandLine.vueOptions.strictTemplates); + snapshots.set(path, { + getText: (start, end) => contents.substring(start, end), + getLength: () => contents.length, + getChangeRange: () => undefined, + }); + } + return snapshots.get(path)!; + } + return getScriptSnapshot(path); + }; + if (checkerOptions.forceUseTs) { const getScriptKind = languageServiceHost.getScriptKind?.bind(languageServiceHost); languageServiceHost.getScriptKind = fileName => { diff --git a/packages/language-core/index.ts b/packages/language-core/index.ts index d539d6591c..2ca1dc11cc 100644 --- a/packages/language-core/index.ts +++ b/packages/language-core/index.ts @@ -1,15 +1,16 @@ +export * from './lib/codegen/globalTypes'; export * from './lib/codegen/template'; export * from './lib/languagePlugin'; export * from './lib/parsers/scriptSetupRanges'; export * from './lib/plugins'; -export * from './lib/virtualFile/vueFile'; export * from './lib/types'; -export * from './lib/utils/ts'; export * from './lib/utils/parseSfc'; +export * from './lib/utils/ts'; +export * from './lib/virtualFile/vueFile'; export * as scriptRanges from './lib/parsers/scriptRanges'; -export * from './lib/utils/shared'; export { tsCodegen } from './lib/plugins/vue-tsx'; +export * from './lib/utils/shared'; export * from '@volar/language-core'; export type * as CompilerDOM from '@vue/compiler-dom'; diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts new file mode 100644 index 0000000000..19689701d5 --- /dev/null +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -0,0 +1,125 @@ +import { getSlotsPropertyName } from '../utils/shared'; + +export function generateGlobalTypes(lib: string, target: number, strictTemplates: boolean) { + const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${strictTemplates ? '' : ' & Record'}`; + return ` +const __VLS_globalComponents = { ...{} as import('${lib}').GlobalComponents }; + +declare const __VLS_intrinsicElements: __VLS_IntrinsicElements; +declare const __VLS_directiveBindingRestFields = { instance: null, oldValue: null, modifiers: null as any, dir: null as any }; + +type __VLS_IntrinsicElements = ${( + target >= 3.3 + ? `import('${lib}/jsx-runtime').JSX.IntrinsicElements;` + : `globalThis.JSX.IntrinsicElements;` + )} +type __VLS_Element = ${( + target >= 3.3 + ? `import('${lib}/jsx-runtime').JSX.Element;` + : `globalThis.JSX.Element;` + )} +type __VLS_GlobalComponents = ${( + target >= 3.5 + ? `void extends typeof __VLS_globalComponents ? {} : typeof __VLS_globalComponents;` + : `(void extends typeof __VLS_globalComponents ? {} : typeof __VLS_globalComponents) & Pick;` + )} +type __VLS_IsAny = 0 extends 1 & T ? true : false; +type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; +type __VLS_unknownDirective = (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void; +type __VLS_WithComponent = + N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : + N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : + N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : + N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } : + N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } : + N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } : + ${strictTemplates ? '{}' : '{ [K in N0]: unknown }'} +type __VLS_FunctionalComponentProps = + '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: { props?: infer P } } ? NonNullable

: never + : T extends (props: infer P, ...args: any) => any ? P : + {}; +type __VLS_IsFunction = K extends keyof T + ? __VLS_IsAny extends false + ? unknown extends T[K] + ? false + : true + : false + : false; +// fix https://github.com/vuejs/language-tools/issues/926 +type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; +type __VLS_OverloadUnionInner = U & T extends (...args: infer A) => infer R + ? U extends T + ? never + : __VLS_OverloadUnionInner & U & ((...args: A) => R)> | ((...args: A) => R) + : never; +type __VLS_OverloadUnion = Exclude< + __VLS_OverloadUnionInner<(() => never) & T>, + T extends () => never ? never : () => never +>; +type __VLS_ConstructorOverloads = __VLS_OverloadUnion extends infer F + ? F extends (event: infer E, ...args: infer A) => any + ? { [K in E & string]: (...args: A) => void; } + : never + : never; +type __VLS_NormalizeEmits = __VLS_PrettifyGlobal< + __VLS_UnionToIntersection< + __VLS_ConstructorOverloads & { + [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never + } + > +>; +type __VLS_PrettifyGlobal = { [K in keyof T]: T[K]; } & {}; + +declare function __VLS_getVForSourceType(source: number): [number, number, number][]; +declare function __VLS_getVForSourceType(source: string): [string, number, number][]; +declare function __VLS_getVForSourceType(source: T): [ + item: T[number], + key: number, + index: number, +][]; +declare function __VLS_getVForSourceType }>(source: T): [ + item: T extends { [Symbol.iterator](): Iterator } ? T1 : never, + key: number, + index: undefined, +][]; +// #3845 +declare function __VLS_getVForSourceType }>(source: T): [ + item: number | (Exclude extends { [Symbol.iterator](): Iterator } ? T1 : never), + key: number, + index: undefined, +][]; +declare function __VLS_getVForSourceType(source: T): [ + item: T[keyof T], + key: keyof T, + index: number, +][]; +// @ts-ignore +declare function __VLS_getSlotParams(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>; +// @ts-ignore +declare function __VLS_getSlotParam(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>[0]; +declare function __VLS_directiveAsFunction(dir: T): T extends (...args: any) => any + ? T | __VLS_unknownDirective + : NonNullable<(T & Record)['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>; +declare function __VLS_withScope(ctx: T, scope: K): ctx is T & K; +declare function __VLS_makeOptional(t: T): { [K in keyof T]?: T[K] }; +declare function __VLS_nonNullable(t: T): T extends null | undefined ? never : T; +declare function __VLS_asFunctionalComponent any ? InstanceType : unknown>(t: T, instance?: K): + T extends new (...args: any) => any + ? (props: ${fnPropsType}, ctx?: any) => __VLS_Element & { __ctx?: { + attrs?: any, + slots?: K extends { ${getSlotsPropertyName(target)}: infer Slots } ? Slots : any, + emit?: K extends { $emit: infer Emit } ? Emit : any + } & { props?: ${fnPropsType}; expose?(exposed: K): void; } } + : T extends () => any ? (props: {}, ctx?: any) => ReturnType + : T extends (...args: any) => any ? T + : (_: {}${strictTemplates ? '' : ' & Record'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates ? '' : ' & Record'} } }; +declare function __VLS_elementAsFunction(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record'}) => void; +declare function __VLS_functionalComponentArgsRest any>(t: T): Parameters['length'] extends 2 ? [any] : []; +declare function __VLS_pickFunctionalComponentCtx(comp: T, compInstance: K): NonNullable<__VLS_PickNotAny< + '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: infer Ctx } ? Ctx : never : any + , T extends (props: any, ctx: infer Ctx) => any ? Ctx : any +>>; +declare function __VLS_normalizeSlot(s: S): S extends () => infer R ? (props: {}) => R : S; +declare function __VLS_tryAsConstant(t: T): T; +`; +}; diff --git a/packages/language-core/lib/codegen/localTypes.ts b/packages/language-core/lib/codegen/localTypes.ts new file mode 100644 index 0000000000..e244f16f0f --- /dev/null +++ b/packages/language-core/lib/codegen/localTypes.ts @@ -0,0 +1,148 @@ +import type * as ts from 'typescript'; +import { VueCompilerOptions } from '../types'; +import { getSlotsPropertyName } from '../utils/shared'; +import { endOfLine } from './common'; + +export function getLocalTypesGenerator(compilerOptions: ts.CompilerOptions, vueCompilerOptions: VueCompilerOptions) { + const used = new Set(); + + const OmitKeepDiscriminatedUnion = defineHelper( + `__VLS_OmitKeepDiscriminatedUnion`, + () => ` +type __VLS_OmitKeepDiscriminatedUnion = T extends any + ? Pick> + : never; +`.trimStart() + ); + const WithDefaults = defineHelper( + `__VLS_WithDefaults`, + () => ` +type __VLS_WithDefaults = { + [K in keyof Pick]: K extends keyof D + ? ${PrettifyLocal.name} + : P[K] +}; +`.trimStart() + ); + const PrettifyLocal = defineHelper( + `__VLS_PrettifyLocal`, + () => `type __VLS_PrettifyLocal = { [K in keyof T]: T[K]; } & {}${endOfLine}` + ); + const WithTemplateSlots = defineHelper( + `__VLS_WithTemplateSlots`, + () => ` +type __VLS_WithTemplateSlots = T & { + new(): { + ${getSlotsPropertyName(vueCompilerOptions.target)}: S; + ${vueCompilerOptions.jsxSlots ? `$props: ${PropsChildren.name};` : ''} + } +}; +`.trimStart() + ); + const PropsChildren = defineHelper( + `__VLS_PropsChildren`, + () => ` +type __VLS_PropsChildren = { + [K in keyof ( + boolean extends ( + // @ts-ignore + JSX.ElementChildrenAttribute extends never + ? true + : false + ) + ? never + // @ts-ignore + : JSX.ElementChildrenAttribute + )]?: S; +}; +`.trimStart() + ); + const TypePropsToOption = defineHelper( + `__VLS_TypePropsToOption`, + () => compilerOptions.exactOptionalPropertyTypes ? + ` +type __VLS_TypePropsToOption = { + [K in keyof T]-?: {} extends Pick + ? { type: import('${vueCompilerOptions.lib}').PropType } + : { type: import('${vueCompilerOptions.lib}').PropType, required: true } +}; +`.trimStart() : + ` +type __VLS_NonUndefinedable = T extends undefined ? never : T; +type __VLS_TypePropsToOption = { + [K in keyof T]-?: {} extends Pick + ? { type: import('${vueCompilerOptions.lib}').PropType<__VLS_NonUndefinedable> } + : { type: import('${vueCompilerOptions.lib}').PropType, required: true } +}; +`.trimStart() + ); + const OmitIndexSignature = defineHelper( + `__VLS_OmitIndexSignature`, + () => `type __VLS_OmitIndexSignature = { [K in keyof T as {} extends Record ? never : K]: T[K]; }${endOfLine}` + ); + const PickRefsExpose = defineHelper( + `__VLS_PickRefsExpose`, + () => ` +type __VLS_PickRefsExpose = T extends object + ? { [K in keyof T]: (T[K] extends any[] + ? Parameters[0][] + : T[K] extends { expose?: (exposed: infer E) => void } + ? E + : T[K]) | null } + : never; +`.trimStart() + ); + + const helpers = { + [PrettifyLocal.name]: PrettifyLocal, + [OmitKeepDiscriminatedUnion.name]: OmitKeepDiscriminatedUnion, + [WithDefaults.name]: WithDefaults, + [WithTemplateSlots.name]: WithTemplateSlots, + [PropsChildren.name]: PropsChildren, + [TypePropsToOption.name]: TypePropsToOption, + [OmitIndexSignature.name]: OmitIndexSignature, + [PickRefsExpose.name]: PickRefsExpose, + }; + used.clear(); + + return { + generate, + getUsedNames() { + return used; + }, + get PrettifyLocal() { return PrettifyLocal.name; }, + get OmitKeepDiscriminatedUnion() { return OmitKeepDiscriminatedUnion.name; }, + get WithDefaults() { return WithDefaults.name; }, + get WithTemplateSlots() { return WithTemplateSlots.name; }, + get PropsChildren() { return PropsChildren.name; }, + get TypePropsToOption() { return TypePropsToOption.name; }, + get OmitIndexSignature() { return OmitIndexSignature.name; }, + get PickRefsExpose() { return PickRefsExpose.name; }, + }; + + function* generate(names: string[]) { + const generated = new Set(); + while (names.length) { + used.clear(); + for (const name of names) { + if (generated.has(name)) { + continue; + } + const helper = helpers[name as keyof typeof helpers]; + yield helper.generate(); + generated.add(name); + } + names = [...used].filter(name => !generated.has(name)); + } + } + + function defineHelper(name: string, generate: () => string) { + return { + get name() { + used.add(name); + return name; + }, + generate, + }; + } +} diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index 457ba0c133..929d1f370d 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -131,8 +131,8 @@ export function* generatePropsOption( if (ctx.generatedPropsType) { optionExpCodes.push([ `{} as `, - scriptSetupRanges.props.withDefaults?.arg ? `${ctx.helperTypes.WithDefaults.name}<` : '', - `${ctx.helperTypes.TypePropsToOption.name}<__VLS_PublicProps>`, + scriptSetupRanges.props.withDefaults?.arg ? `${ctx.localTypes.WithDefaults}<` : '', + `${ctx.localTypes.TypePropsToOption}<__VLS_PublicProps>`, scriptSetupRanges.props.withDefaults?.arg ? `, typeof __VLS_withDefaultsArg>` : '', ].join('')); typeOptionExpCodes.push(`{} as __VLS_PublicProps`); @@ -146,8 +146,8 @@ export function* generatePropsOption( if (hasEmitsOption) { attrsType = `Omit<${attrsType}, \`on\${string}\`>`; } - const propsType = `__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}<${attrsType}>, {}>`; - const optionType = `${ctx.helperTypes.TypePropsToOption.name}<${propsType}>`; + const propsType = `__VLS_PickNotAny<${ctx.localTypes.OmitIndexSignature}<${attrsType}>, {}>`; + const optionType = `${ctx.localTypes.TypePropsToOption}<${propsType}>`; if (optionExpCodes.length) { optionExpCodes.unshift(`{} as ${optionType}`); } diff --git a/packages/language-core/lib/codegen/script/context.ts b/packages/language-core/lib/codegen/script/context.ts index 8bb0ad2176..60d30f82ce 100644 --- a/packages/language-core/lib/codegen/script/context.ts +++ b/packages/language-core/lib/codegen/script/context.ts @@ -1,6 +1,5 @@ -import { getSlotsPropertyName } from '../../utils/shared'; -import { newLine } from '../common'; import { InlayHintInfo } from '../types'; +import { getLocalTypesGenerator } from '../localTypes'; import type { ScriptCodegenOptions } from './index'; export interface HelperType { @@ -13,105 +12,7 @@ export interface HelperType { export type ScriptCodegenContext = ReturnType; export function createScriptCodegenContext(options: ScriptCodegenOptions) { - const helperTypes = { - OmitKeepDiscriminatedUnion: { - get name() { - this.used = true; - return `__VLS_OmitKeepDiscriminatedUnion`; - }, - get code() { - return `type __VLS_OmitKeepDiscriminatedUnion = T extends any - ? Pick> - : never;`; - }, - } satisfies HelperType as HelperType, - WithDefaults: { - get name() { - this.used = true; - return `__VLS_WithDefaults`; - }, - get code(): string { - return `type __VLS_WithDefaults = { - [K in keyof Pick]: K extends keyof D - ? ${helperTypes.Prettify.name} - : P[K] - };`; - }, - } satisfies HelperType as HelperType, - Prettify: { - get name() { - this.used = true; - return `__VLS_Prettify`; - }, - get code() { - return `type __VLS_Prettify = { [K in keyof T]: T[K]; } & {};`; - }, - } satisfies HelperType as HelperType, - WithTemplateSlots: { - get name() { - this.used = true; - return `__VLS_WithTemplateSlots`; - }, - get code(): string { - return `type __VLS_WithTemplateSlots = T & { - new(): { - ${getSlotsPropertyName(options.vueCompilerOptions.target)}: S; - ${options.vueCompilerOptions.jsxSlots ? `$props: ${helperTypes.PropsChildren.name};` : ''} - } - };`; - }, - } satisfies HelperType as HelperType, - PropsChildren: { - get name() { - this.used = true; - return `__VLS_PropsChildren`; - }, - get code() { - return `type __VLS_PropsChildren = { - [K in keyof ( - boolean extends ( - // @ts-ignore - JSX.ElementChildrenAttribute extends never - ? true - : false - ) - ? never - // @ts-ignore - : JSX.ElementChildrenAttribute - )]?: S; - };`; - }, - } satisfies HelperType as HelperType, - TypePropsToOption: { - get name() { - this.used = true; - return `__VLS_TypePropsToOption`; - }, - get code() { - return options.compilerOptions.exactOptionalPropertyTypes ? - `type __VLS_TypePropsToOption = { - [K in keyof T]-?: {} extends Pick - ? { type: import('${options.vueCompilerOptions.lib}').PropType } - : { type: import('${options.vueCompilerOptions.lib}').PropType, required: true } - };` : - `type __VLS_NonUndefinedable = T extends undefined ? never : T; - type __VLS_TypePropsToOption = { - [K in keyof T]-?: {} extends Pick - ? { type: import('${options.vueCompilerOptions.lib}').PropType<__VLS_NonUndefinedable> } - : { type: import('${options.vueCompilerOptions.lib}').PropType, required: true } - };`; - }, - } satisfies HelperType as HelperType, - OmitIndexSignature: { - get name() { - this.used = true; - return `__VLS_OmitIndexSignature`; - }, - get code() { - return `type __VLS_OmitIndexSignature = { [K in keyof T as {} extends Record ? never : K]: T[K]; };`; - } - } satisfies HelperType as HelperType, - }; + const localTypes = getLocalTypesGenerator(options.compilerOptions, options.vueCompilerOptions); const inlayHints: InlayHintInfo[] = []; return { @@ -123,22 +24,7 @@ export function createScriptCodegenContext(options: ScriptCodegenOptions) { ...options.scriptRanges?.bindings.map(range => options.sfc.script!.content.substring(range.start, range.end)) ?? [], ...options.scriptSetupRanges?.bindings.map(range => options.sfc.scriptSetup!.content.substring(range.start, range.end)) ?? [], ]), - helperTypes, + localTypes, inlayHints, - generateHelperTypes, }; - - function* generateHelperTypes() { - let shouldCheck = true; - while (shouldCheck) { - shouldCheck = false; - for (const helperType of Object.values(helperTypes)) { - if (helperType.used && !helperType.generated) { - shouldCheck = true; - helperType.generated = true; - yield newLine + helperType.code + newLine; - } - } - } - } } diff --git a/packages/language-core/lib/codegen/script/globalTypes.ts b/packages/language-core/lib/codegen/script/globalTypes.ts deleted file mode 100644 index 541a1512d3..0000000000 --- a/packages/language-core/lib/codegen/script/globalTypes.ts +++ /dev/null @@ -1,146 +0,0 @@ -import type { VueCompilerOptions } from '../../types'; -import { getSlotsPropertyName } from '../../utils/shared'; - -export function generateGlobalTypes(vueCompilerOptions: VueCompilerOptions) { - const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${vueCompilerOptions.strictTemplates ? '' : ' & Record'}`; - return `export const __VLS_globalTypesStart = {}; -declare module '${vueCompilerOptions.lib}' { - interface GlobalComponents {} -} -declare global { - type __VLS_IntrinsicElements = ${vueCompilerOptions.target >= 3.3 - ? `import('${vueCompilerOptions.lib}/jsx-runtime').JSX.IntrinsicElements;` - : `globalThis.JSX.IntrinsicElements;` - } - type __VLS_Element = ${vueCompilerOptions.target >= 3.3 - ? `import('${vueCompilerOptions.lib}/jsx-runtime').JSX.Element;` - : `globalThis.JSX.Element;` - } - type __VLS_GlobalComponents = ${vueCompilerOptions.target >= 3.5 - ? `import('${vueCompilerOptions.lib}').GlobalComponents;` - : `import('${vueCompilerOptions.lib}').GlobalComponents & Pick;` - } - type __VLS_IsAny = 0 extends 1 & T ? true : false; - type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; - - const __VLS_intrinsicElements: __VLS_IntrinsicElements; - - // v-for - function __VLS_getVForSourceType(source: number): [number, number, number][]; - function __VLS_getVForSourceType(source: string): [string, number, number][]; - function __VLS_getVForSourceType(source: T): [ - item: T[number], - key: number, - index: number, - ][]; - function __VLS_getVForSourceType }>(source: T): [ - item: T extends { [Symbol.iterator](): Iterator } ? T1 : never, - key: number, - index: undefined, - ][]; - // #3845 - function __VLS_getVForSourceType }>(source: T): [ - item: number | (Exclude extends { [Symbol.iterator](): Iterator } ? T1 : never), - key: number, - index: undefined, - ][]; - function __VLS_getVForSourceType(source: T): [ - item: T[keyof T], - key: keyof T, - index: number, - ][]; - - // @ts-ignore - function __VLS_getSlotParams(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>; - // @ts-ignore - function __VLS_getSlotParam(slot: T): Parameters<__VLS_PickNotAny, (...args: any[]) => any>>[0]; - - // Custom Directives - type __VLS_unknownDirective = (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void; - function __VLS_directiveAsFunction(dir: T): T extends (...args: any) => any - ? T | __VLS_unknownDirective - : NonNullable<(T & Record)['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>; - const __VLS_directiveBindingRestFields = { instance: null, oldValue: null, modifiers: null as any, dir: null as any }; - - function __VLS_withScope(ctx: T, scope: K): ctx is T & K; - function __VLS_makeOptional(t: T): { [K in keyof T]?: T[K] }; - function __VLS_nonNullable(t: T): T extends null | undefined ? never : T; - - type __VLS_WithComponent = - N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : - N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : - N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : - N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } : - N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } : - N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } : - ${vueCompilerOptions.strictTemplates ? '{}' : '{ [K in N0]: unknown }'} - - function __VLS_asFunctionalComponent any ? InstanceType : unknown>(t: T, instance?: K): - T extends new (...args: any) => any - ? (props: ${fnPropsType}, ctx?: any) => __VLS_Element & { __ctx?: { - attrs?: any, - slots?: K extends { ${getSlotsPropertyName(vueCompilerOptions.target)}: infer Slots } ? Slots : any, - emit?: K extends { $emit: infer Emit } ? Emit : any - } & { props?: ${fnPropsType}; expose?(exposed: K): void; } } - : T extends () => any ? (props: {}, ctx?: any) => ReturnType - : T extends (...args: any) => any ? T - : (_: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record'} } }; - function __VLS_elementAsFunction(tag: T, endTag?: T): (_: T${vueCompilerOptions.strictTemplates ? '' : ' & Record'}) => void; - function __VLS_functionalComponentArgsRest any>(t: T): Parameters['length'] extends 2 ? [any] : []; - function __VLS_pickFunctionalComponentCtx(comp: T, compInstance: K): NonNullable<__VLS_PickNotAny< - '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: infer Ctx } ? Ctx : never : any - , T extends (props: any, ctx: infer Ctx) => any ? Ctx : any - >>; - type __VLS_FunctionalComponentProps = - '__ctx' extends keyof __VLS_PickNotAny ? K extends { __ctx?: { props?: infer P } } ? NonNullable

: never - : T extends (props: infer P, ...args: any) => any ? P : - {}; - type __VLS_IsFunction = K extends keyof T - ? __VLS_IsAny extends false - ? unknown extends T[K] - ? false - : true - : false - : false; - - function __VLS_normalizeSlot(s: S): S extends () => infer R ? (props: {}) => R : S; - function __VLS_tryAsConstant(t: T): T; - - /** - * emit - */ - // fix https://github.com/vuejs/language-tools/issues/926 - type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; - type __VLS_OverloadUnionInner = U & T extends (...args: infer A) => infer R - ? U extends T - ? never - : __VLS_OverloadUnionInner & U & ((...args: A) => R)> | ((...args: A) => R) - : never; - type __VLS_OverloadUnion = Exclude< - __VLS_OverloadUnionInner<(() => never) & T>, - T extends () => never ? never : () => never - >; - type __VLS_ConstructorOverloads = __VLS_OverloadUnion extends infer F - ? F extends (event: infer E, ...args: infer A) => any - ? { [K in E & string]: (...args: A) => void; } - : never - : never; - type __VLS_NormalizeEmits = __VLS_PrettifyGlobal< - __VLS_UnionToIntersection< - __VLS_ConstructorOverloads & { - [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never - } - > - >; - type __VLS_PrettifyGlobal = { [K in keyof T]: T[K]; } & {}; - type __VLS_PickRefsExpose = T extends object - ? { [K in keyof T]: (T[K] extends any[] - ? Parameters[0][] - : T[K] extends { expose?: (exposed: infer E) => void } - ? E - : T[K]) | null } - : never; -} -export const __VLS_globalTypesEnd = {}; -`; -}; diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index 322eaebd01..3ae28f41a5 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -6,7 +6,6 @@ import type { Code, Sfc, VueCodeInformation, VueCompilerOptions } from '../../ty import { endOfLine, generateSfcBlockSection, newLine } from '../common'; import type { TemplateCodegenContext } from '../template/context'; import { createScriptCodegenContext, ScriptCodegenContext } from './context'; -import { generateGlobalTypes } from './globalTypes'; import { generateScriptSetup, generateScriptSetupImports } from './scriptSetup'; import { generateSrc } from './src'; import { generateTemplate } from './template'; @@ -44,7 +43,6 @@ export interface ScriptCodegenOptions { scriptRanges: ScriptRanges | undefined; scriptSetupRanges: ScriptSetupRanges | undefined; templateCodegen: TemplateCodegenContext & { codes: Code[]; } | undefined; - globalTypes: boolean; edited: boolean; getGeneratedLength: () => number; linkedCodeMappings: Mapping[]; @@ -53,7 +51,8 @@ export interface ScriptCodegenOptions { export function* generateScript(options: ScriptCodegenOptions): Generator { const ctx = createScriptCodegenContext(options); - yield `/* __placeholder__ */${newLine}`; + yield `/// ${newLine}`; + if (options.sfc.script?.src) { yield* generateSrc(options.sfc.script, options.sfc.script.src); } @@ -129,16 +128,15 @@ export function* generateScript(options: ScriptCodegenOptions): Generator(${newLine}` + ` __VLS_props: NonNullable>['props'],${newLine}` - + ` __VLS_ctx?: ${ctx.helperTypes.Prettify.name}>, 'attrs' | 'emit' | 'slots'>>,${newLine}` // use __VLS_Prettify for less dts code + + ` __VLS_ctx?: ${ctx.localTypes.PrettifyLocal}>, 'attrs' | 'emit' | 'slots'>>,${newLine}` // use __VLS_Prettify for less dts code + ` __VLS_expose?: NonNullable>['expose'],${newLine}` + ` __VLS_setup = (async () => {${newLine}`; yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, undefined, definePropMirrors); @@ -67,7 +67,7 @@ export function* generateScriptSetup( } yield ` return {} as {${newLine}` - + ` props: ${ctx.helperTypes.Prettify.name} & __VLS_BuiltInPublicProps,${newLine}` + + ` props: ${ctx.localTypes.PrettifyLocal} & __VLS_BuiltInPublicProps,${newLine}` + ` expose(exposed: import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,${newLine}` + ` attrs: any,${newLine}` + ` slots: __VLS_Slots,${newLine}` @@ -289,7 +289,7 @@ function* generateSetupFunction( yield* generateComponent(options, ctx, scriptSetup, scriptSetupRanges); yield endOfLine; yield `${syntax} `; - yield `{} as ${ctx.helperTypes.WithTemplateSlots.name}${endOfLine}`; + yield `{} as ${ctx.localTypes.WithTemplateSlots}${endOfLine}`; } else { yield `${syntax} `; @@ -329,7 +329,7 @@ function* generateComponentProps( yield endOfLine; yield `let __VLS_functionalComponentProps!: `; - yield `${ctx.helperTypes.OmitKeepDiscriminatedUnion.name}['$props'], keyof __VLS_BuiltInPublicProps>`; + yield `${ctx.localTypes.OmitKeepDiscriminatedUnion}['$props'], keyof __VLS_BuiltInPublicProps>`; yield endOfLine; if (scriptSetupRanges.defineProp.length) { @@ -361,7 +361,7 @@ function* generateComponentProps( yield ` & `; } ctx.generatedPropsType = true; - yield `${ctx.helperTypes.PropsChildren.name}`; + yield `${ctx.localTypes.PropsChildren}`; } if (scriptSetupRanges.defineProp.length) { if (ctx.generatedPropsType) { @@ -470,7 +470,7 @@ function* generateStyleModules( else { yield name; } - yield `: Record & ${ctx.helperTypes.Prettify.name}<{}`; + yield `: Record & ${ctx.localTypes.PrettifyLocal}<{}`; for (const className of style.classNames) { yield* generateCssClassProperty( i, diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 7507e2dffd..695d0ace06 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -9,7 +9,10 @@ import type { ScriptCodegenContext } from './context'; import { codeFeatures, type ScriptCodegenOptions } from './index'; import { generateInternalComponent } from './internalComponent'; -export function* generateTemplateCtx(options: ScriptCodegenOptions, isClassComponent: boolean): Generator { +export function* generateTemplateCtx( + options: ScriptCodegenOptions, + isClassComponent: boolean +): Generator { const types = []; if (isClassComponent) { types.push(`typeof this`); @@ -78,10 +81,13 @@ export function* generateTemplate( ctx.generatedTemplate = true; if (!options.vueCompilerOptions.skipTemplateCodegen) { - const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), edited: options.edited }); + const templateCodegenCtx = createTemplateCodegenContext({ + scriptSetupBindingNames: new Set(), + edited: options.edited, + }); yield* generateTemplateCtx(options, isClassComponent); yield* generateTemplateComponents(options); - yield* generateTemplateBody(options, templateCodegenCtx); + yield* generateTemplateBody(options, ctx, templateCodegenCtx); yield* generateInternalComponent(options, ctx, templateCodegenCtx); } else { @@ -94,6 +100,7 @@ export function* generateTemplate( function* generateTemplateBody( options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, templateCodegenCtx: TemplateCodegenContext ): Generator { const firstClasses = new Set(); @@ -142,7 +149,7 @@ function* generateTemplateBody( yield `const __VLS_templateResult = {`; yield `slots: ${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'},${newLine}`; - yield `refs: __VLS_refs as __VLS_PickRefsExpose,${newLine}`; + yield `refs: __VLS_refs as ${ctx.localTypes.PickRefsExpose},${newLine}`; yield `attrs: {} as Partial,${newLine}`; yield `}${endOfLine}`; } diff --git a/packages/language-core/lib/codegen/template/objectProperty.ts b/packages/language-core/lib/codegen/template/objectProperty.ts index 5d01f92fe9..812943c8da 100644 --- a/packages/language-core/lib/codegen/template/objectProperty.ts +++ b/packages/language-core/lib/codegen/template/objectProperty.ts @@ -26,8 +26,8 @@ export function* generateObjectProperty( astHolder, offset + 1, features, - '[__VLS_tryAsConstant(', - ')]' + `[__VLS_tryAsConstant(`, + `)]` ); } else { diff --git a/packages/language-core/lib/languagePlugin.ts b/packages/language-core/lib/languagePlugin.ts index d6bb0ff457..8d6055f169 100644 --- a/packages/language-core/lib/languagePlugin.ts +++ b/packages/language-core/lib/languagePlugin.ts @@ -1,6 +1,6 @@ /// -import { FileMap, forEachEmbeddedCode, type LanguagePlugin } from '@volar/language-core'; +import { forEachEmbeddedCode, LanguagePlugin } from '@volar/language-core'; import * as CompilerDOM from '@vue/compiler-dom'; import type * as ts from 'typescript'; import { createPlugins } from './plugins'; @@ -8,15 +8,13 @@ import type { VueCompilerOptions, VueLanguagePlugin, VueLanguagePluginReturn } f import * as CompilerVue2 from './utils/vue2TemplateCompiler'; import { VueVirtualCode } from './virtualFile/vueFile'; -const normalFileRegistries: { +const fileRegistries: { key: string; plugins: VueLanguagePlugin[]; files: Map; }[] = []; -const holderFileRegistries: typeof normalFileRegistries = []; -function getVueFileRegistry(isGlobalTypesHolder: boolean, key: string, plugins: VueLanguagePlugin[]) { - const fileRegistries = isGlobalTypesHolder ? holderFileRegistries : normalFileRegistries; +function getVueFileRegistry(key: string, plugins: VueLanguagePlugin[]) { let fileRegistry = fileRegistries.find(r => r.key === key && r.plugins.length === plugins.length @@ -50,49 +48,11 @@ function getFileRegistryKey( return JSON.stringify(values); } -export function createRootFileChecker( - getProjectVersion: (() => string) | undefined, - getRootFileNames: () => string[], - caseSensitive: boolean -) { - const fileNames = new FileMap(caseSensitive); - let projectVersion: string | undefined; - return (fileName: string) => { - if (!getProjectVersion || projectVersion !== getProjectVersion()) { - projectVersion = getProjectVersion?.(); - fileNames.clear(); - for (const rootFileName of getRootFileNames()) { - fileNames.set(rootFileName, undefined); - } - } - return fileNames.has(fileName); - }; -} - -// TODO: replace `createVueLanguagePlugin` with `createVueLanguagePlugin2` in 2.1 export function createVueLanguagePlugin( ts: typeof import('typescript'), - asFileName: (scriptId: T) => string, - _getProjectVersion: (() => string) | undefined, - isRootFile: (fileName: string) => boolean, - compilerOptions: ts.CompilerOptions, - vueCompilerOptions: VueCompilerOptions -): LanguagePlugin { - return createVueLanguagePlugin2( - ts, - asFileName, - isRootFile, - compilerOptions, - vueCompilerOptions - ); -} - -export function createVueLanguagePlugin2( - ts: typeof import('typescript'), - asFileName: (scriptId: T) => string, - isRootFile: (fileName: string) => boolean, compilerOptions: ts.CompilerOptions, - vueCompilerOptions: VueCompilerOptions + vueCompilerOptions: VueCompilerOptions, + asFileName: (scriptId: T) => string ): LanguagePlugin { const pluginContext: Parameters[0] = { modules: { @@ -106,9 +66,12 @@ export function createVueLanguagePlugin2( }, compilerOptions, vueCompilerOptions, - globalTypesHolder: undefined, }; const plugins = createPlugins(pluginContext); + const fileRegistry = getVueFileRegistry( + getFileRegistryKey(compilerOptions, vueCompilerOptions, plugins), + vueCompilerOptions.plugins + ); return { getLanguageId(scriptId) { @@ -123,10 +86,6 @@ export function createVueLanguagePlugin2( createVirtualCode(scriptId, languageId, snapshot) { const fileName = asFileName(scriptId); if (plugins.some(plugin => plugin.isValidFile?.(fileName, languageId))) { - if (!pluginContext.globalTypesHolder && isRootFile(fileName)) { - pluginContext.globalTypesHolder = fileName; - } - const fileRegistry = getFileRegistry(pluginContext.globalTypesHolder === fileName); const code = fileRegistry.get(fileName); if (code) { code.update(snapshot); @@ -150,30 +109,6 @@ export function createVueLanguagePlugin2( code.update(snapshot); return code; }, - // TODO: when global types holder deleted, move global types to another file - // disposeVirtualCode(fileId, code) { - // const isGlobalTypesHolder = code.fileName === pluginContext.globalTypesHolder; - // const fileRegistry = getFileRegistry(isGlobalTypesHolder); - // fileRegistry.delete(fileId); - // if (isGlobalTypesHolder) { - // pluginContext.globalTypesHolder = undefined; - // const fileRegistry2 = getFileRegistry(false); - // for (const [fileId, code] of fileRegistry2) { - // if (isValidGlobalTypesHolder(code.fileName)) { - // pluginContext.globalTypesHolder = code.fileName; - // fileRegistry2.delete(fileId); - // // force dirty - // files?.delete(fileId); - // files?.set( - // fileId, - // code.languageId, - // code.snapshot, - // ); - // break; - // } - // } - // } - // }, typescript: { extraFileExtensions: getAllExtensions(vueCompilerOptions) .map(ext => ({ @@ -198,14 +133,6 @@ export function createVueLanguagePlugin2( }, }, }; - - function getFileRegistry(isGlobalTypesHolder: boolean) { - return getVueFileRegistry( - isGlobalTypesHolder, - getFileRegistryKey(compilerOptions, vueCompilerOptions, plugins), - vueCompilerOptions.plugins - ); - } } export function getAllExtensions(options: VueCompilerOptions) { diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index c5e1308e9c..d2c1b93310 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -152,7 +152,6 @@ function createTsx( const codegen = generateScript({ ts, fileBaseName: path.basename(fileName), - globalTypes: ctx.globalTypesHolder === fileName, sfc: _sfc, lang: lang(), scriptRanges: scriptRanges(), diff --git a/packages/language-core/lib/types.ts b/packages/language-core/lib/types.ts index 57dfd05898..b5c3b557ad 100644 --- a/packages/language-core/lib/types.ts +++ b/packages/language-core/lib/types.ts @@ -85,7 +85,6 @@ export type VueLanguagePlugin = (ctx: { }; compilerOptions: ts.CompilerOptions; vueCompilerOptions: VueCompilerOptions; - globalTypesHolder: string | undefined; }) => VueLanguagePluginReturn | VueLanguagePluginReturn[]; export interface SfcBlock { diff --git a/packages/language-core/schemas/vue-tsconfig.schema.json b/packages/language-core/schemas/vue-tsconfig.schema.json index ce927dbd6d..f3e756a766 100644 --- a/packages/language-core/schemas/vue-tsconfig.schema.json +++ b/packages/language-core/schemas/vue-tsconfig.schema.json @@ -32,7 +32,7 @@ "markdownDescription": "Valid file extensions that should be considered as regular PetiteVue SFC." }, "lib": { - "default": "", + "default": "vue", "markdownDescription": "Specify module name for import regular types. (If empty, will use `@vue/runtime-dom` for target < 2.7, `vue` for target >= 2.7)" }, "jsxSlots": { diff --git a/packages/language-server/lib/initialize.ts b/packages/language-server/lib/initialize.ts index bfb91fb916..cb2db4cccd 100644 --- a/packages/language-server/lib/initialize.ts +++ b/packages/language-server/lib/initialize.ts @@ -1,6 +1,6 @@ import type { LanguageServer } from '@volar/language-server'; import { createTypeScriptProject } from '@volar/language-server/node'; -import { createParsedCommandLine, createRootFileChecker, createVueLanguagePlugin2, getAllExtensions, resolveVueCompilerOptions, VueCompilerOptions } from '@vue/language-core'; +import { createParsedCommandLine, createVueLanguagePlugin, generateGlobalTypes, getAllExtensions, resolveVueCompilerOptions, VueCompilerOptions } from '@vue/language-core'; import { Disposable, getFullLanguageServicePlugins, InitializeParams } from '@vue/language-service'; import type * as ts from 'typescript'; @@ -18,7 +18,7 @@ export function initialize( createTypeScriptProject( ts, tsLocalized, - async ({ configFileName, sys, projectHost, uriConverter }) => { + async ({ configFileName, sys, uriConverter }) => { let compilerOptions: ts.CompilerOptions; let vueCompilerOptions: VueCompilerOptions; if (configFileName) { @@ -40,19 +40,43 @@ export function initialize( vueCompilerOptions.__test = params.initializationOptions.typescript.disableAutoImportCache; updateFileWatcher(vueCompilerOptions); return { - languagePlugins: [createVueLanguagePlugin2( - ts, - s => uriConverter.asFileName(s), - createRootFileChecker( - projectHost.getProjectVersion ? () => projectHost.getProjectVersion!() : undefined, - () => projectHost.getScriptFileNames(), - sys.useCaseSensitiveFileNames + languagePlugins: [ + createVueLanguagePlugin( + ts, + compilerOptions, + vueCompilerOptions, + s => uriConverter.asFileName(s) ), - compilerOptions, - vueCompilerOptions - )], + ], setup({ project }) { project.vue = { compilerOptions: vueCompilerOptions }; + + if (project.typescript) { + const globalTypesName = `__globalTypes_${vueCompilerOptions.target}_${vueCompilerOptions.strictTemplates}.d.ts`; + const fileExists = project.typescript.languageServiceHost.fileExists.bind(project.typescript.languageServiceHost); + const getScriptSnapshot = project.typescript.languageServiceHost.getScriptSnapshot.bind(project.typescript.languageServiceHost); + const snapshots = new Map(); + project.typescript.languageServiceHost.fileExists = path => { + if (path.endsWith(globalTypesName)) { + return true; + } + return fileExists(path); + }; + project.typescript.languageServiceHost.getScriptSnapshot = path => { + if (path.endsWith(globalTypesName)) { + if (!snapshots.has(path)) { + const contents = generateGlobalTypes(vueCompilerOptions.lib, vueCompilerOptions.target, vueCompilerOptions.strictTemplates); + snapshots.set(path, { + getText: (start, end) => contents.substring(start, end), + getLength: () => contents.length, + getChangeRange: () => undefined, + }); + } + return snapshots.get(path)!; + } + return getScriptSnapshot(path); + }; + } }, }; } diff --git a/packages/language-server/node.ts b/packages/language-server/node.ts index 65b4c4d787..24c37db0ec 100644 --- a/packages/language-server/node.ts +++ b/packages/language-server/node.ts @@ -1,5 +1,5 @@ import { createConnection, createServer, loadTsdkByPath } from '@volar/language-server/node'; -import { createParsedCommandLine, createVueLanguagePlugin2, resolveVueCompilerOptions } from '@vue/language-core'; +import { createParsedCommandLine, createVueLanguagePlugin, resolveVueCompilerOptions } from '@vue/language-core'; import { getHybridModeLanguageServicePlugins } from '@vue/language-service'; import * as namedPipeClient from '@vue/typescript-plugin/lib/client'; import { createHybridModeProject } from './lib/hybridModeProject'; @@ -28,13 +28,14 @@ connection.onInitialize(params => { }; commandLine.vueOptions.__test = params.initializationOptions.typescript.disableAutoImportCache; return { - languagePlugins: [createVueLanguagePlugin2( - ts, - asFileName, - () => false, - commandLine.options, - commandLine.vueOptions - )], + languagePlugins: [ + createVueLanguagePlugin( + ts, + commandLine.options, + commandLine.vueOptions, + asFileName + ), + ], setup({ project }) { project.vue = { compilerOptions: commandLine.vueOptions }; }, diff --git a/packages/language-server/tests/completions.spec.ts b/packages/language-server/tests/completions.spec.ts index 044f28966f..3460d95c1e 100644 --- a/packages/language-server/tests/completions.spec.ts +++ b/packages/language-server/tests/completions.spec.ts @@ -244,7 +244,6 @@ describe('Completions', async () => { }); it('#2511', async () => { - await ensureGlobalTypesHolder('tsconfigProject'); await prepareDocument('tsconfigProject/component-for-auto-import.vue', 'vue', ``); expect( (await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', ` @@ -290,7 +289,6 @@ describe('Completions', async () => { }); it('Alias path', async () => { - await ensureGlobalTypesHolder('tsconfigProject'); await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', ` `); expect( (await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', ` @@ -375,7 +371,6 @@ describe('Completions', async () => { }); it('core#8811', async () => { - await ensureGlobalTypesHolder('tsconfigProject'); await requestCompletionItem('tsconfigProject/fixture.vue', 'vue', `