From a6824a8d7b917487be893544bbd8b07a52cccd31 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Fri, 6 Sep 2024 23:21:54 +0800 Subject: [PATCH 1/7] fix: avoid generic type loss due to destructured props --- packages/language-core/lib/codegen/script/componentSelf.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/language-core/lib/codegen/script/componentSelf.ts b/packages/language-core/lib/codegen/script/componentSelf.ts index 061285e30f..ad3ea0b9fc 100644 --- a/packages/language-core/lib/codegen/script/componentSelf.ts +++ b/packages/language-core/lib/codegen/script/componentSelf.ts @@ -31,6 +31,9 @@ export function* generateComponentSelf( if (!templateUsageVars.has(varName) && !templateCodegenCtx.accessExternalVariables.has(varName)) { continue; } + if (options.scriptSetupRanges.props.destructured?.includes(varName)) { + continue; + } const templateOffset = options.getGeneratedLength(); yield `${varName}: ${varName} as typeof `; From 4cf221704fcb55ac63fc15b61bf308697eeb6b10 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sat, 7 Sep 2024 00:26:31 +0800 Subject: [PATCH 2/7] perf: use Set --- packages/language-core/lib/codegen/script/componentSelf.ts | 2 +- packages/language-core/lib/parsers/scriptSetupRanges.ts | 4 ++-- packages/language-service/lib/plugins/vue-inlayhints.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/language-core/lib/codegen/script/componentSelf.ts b/packages/language-core/lib/codegen/script/componentSelf.ts index ad3ea0b9fc..ecc41e8995 100644 --- a/packages/language-core/lib/codegen/script/componentSelf.ts +++ b/packages/language-core/lib/codegen/script/componentSelf.ts @@ -31,7 +31,7 @@ export function* generateComponentSelf( if (!templateUsageVars.has(varName) && !templateCodegenCtx.accessExternalVariables.has(varName)) { continue; } - if (options.scriptSetupRanges.props.destructured?.includes(varName)) { + if (options.scriptSetupRanges.props.destructured?.has(varName)) { continue; } const templateOffset = options.getGeneratedLength(); diff --git a/packages/language-core/lib/parsers/scriptSetupRanges.ts b/packages/language-core/lib/parsers/scriptSetupRanges.ts index 077bc4bdc9..f59b0105ec 100644 --- a/packages/language-core/lib/parsers/scriptSetupRanges.ts +++ b/packages/language-core/lib/parsers/scriptSetupRanges.ts @@ -15,7 +15,7 @@ export function parseScriptSetupRanges( const props: { name?: string; - destructured?: string[]; + destructured?: Set; define?: ReturnType & { statement: TextRange; }; @@ -308,7 +308,7 @@ export function parseScriptSetupRanges( else if (vueCompilerOptions.macros.defineProps.includes(callText)) { if (ts.isVariableDeclaration(parent)) { if (ts.isObjectBindingPattern(parent.name)) { - props.destructured = collectVars(ts, parent.name, ast, [], false); + props.destructured = new Set(collectVars(ts, parent.name, ast, [], false)); } else { props.name = getNodeText(ts, parent.name, ast); diff --git a/packages/language-service/lib/plugins/vue-inlayhints.ts b/packages/language-service/lib/plugins/vue-inlayhints.ts index c740bcdff9..58d61ed30f 100644 --- a/packages/language-service/lib/plugins/vue-inlayhints.ts +++ b/packages/language-service/lib/plugins/vue-inlayhints.ts @@ -100,7 +100,7 @@ type Scope = Record; export function findDestructuredProps( ts: typeof import('typescript'), ast: ts.SourceFile, - props: string[] + props: Set ) { const rootScope: Scope = {}; const scopeStack: Scope[] = [rootScope]; From dad761b90c4940c1eb95c4575f59c9762bcee7ce Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sat, 7 Sep 2024 01:25:26 +0800 Subject: [PATCH 3/7] test: add --- .../tsc/passedFixtures/vue3/#4820/main.vue | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test-workspace/tsc/passedFixtures/vue3/#4820/main.vue diff --git a/test-workspace/tsc/passedFixtures/vue3/#4820/main.vue b/test-workspace/tsc/passedFixtures/vue3/#4820/main.vue new file mode 100644 index 0000000000..40870f6b4a --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3/#4820/main.vue @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file From 615049c14e63e43eb86c080cec7e0d982789bba5 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sat, 7 Sep 2024 01:30:42 +0800 Subject: [PATCH 4/7] test: fix --- test-workspace/tsc/passedFixtures/vue2/tsconfig.json | 1 + test-workspace/tsc/passedFixtures/vue3.4/tsconfig.json | 1 + 2 files changed, 2 insertions(+) diff --git a/test-workspace/tsc/passedFixtures/vue2/tsconfig.json b/test-workspace/tsc/passedFixtures/vue2/tsconfig.json index 1ff36d1175..e6ac58082d 100644 --- a/test-workspace/tsc/passedFixtures/vue2/tsconfig.json +++ b/test-workspace/tsc/passedFixtures/vue2/tsconfig.json @@ -25,6 +25,7 @@ "../vue3/#4646", "../vue3/#4649", "../vue3/#4777", + "../vue3/#4820", "../vue3/components", "../vue3/defineEmits", "../vue3/defineModel", diff --git a/test-workspace/tsc/passedFixtures/vue3.4/tsconfig.json b/test-workspace/tsc/passedFixtures/vue3.4/tsconfig.json index 795db51b7f..5a47ab2082 100644 --- a/test-workspace/tsc/passedFixtures/vue3.4/tsconfig.json +++ b/test-workspace/tsc/passedFixtures/vue3.4/tsconfig.json @@ -11,6 +11,7 @@ "exclude": [ "../vue3/#3820", "../vue3/#4777", + "../vue3/#4820", "../vue3/templateRef", "../vue3/templateRef_native", "../vue3/components", From 2d2ecdb2bbc887b87aa3b169e0e85acd47254a78 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sat, 7 Sep 2024 02:25:59 +0800 Subject: [PATCH 5/7] refactor: don't generate `__VLS_ctx.` before it --- .../lib/codegen/script/componentSelf.ts | 3 --- .../language-core/lib/codegen/script/template.ts | 1 + .../language-core/lib/codegen/template/index.ts | 1 + .../lib/codegen/template/interpolation.ts | 15 ++++++++++++--- packages/language-core/lib/plugins/vue-tsx.ts | 8 ++++++++ 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/language-core/lib/codegen/script/componentSelf.ts b/packages/language-core/lib/codegen/script/componentSelf.ts index ecc41e8995..061285e30f 100644 --- a/packages/language-core/lib/codegen/script/componentSelf.ts +++ b/packages/language-core/lib/codegen/script/componentSelf.ts @@ -31,9 +31,6 @@ export function* generateComponentSelf( if (!templateUsageVars.has(varName) && !templateCodegenCtx.accessExternalVariables.has(varName)) { continue; } - if (options.scriptSetupRanges.props.destructured?.has(varName)) { - continue; - } const templateOffset = options.getGeneratedLength(); yield `${varName}: ${varName} as typeof `; diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 6debf6c809..7ad0f73e53 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -231,6 +231,7 @@ function* generateCssVars(options: ScriptCodegenOptions, ctx: TemplateCodegenCon for (const [segment, offset, onlyError] of forEachInterpolationSegment( options.ts, undefined, + undefined, ctx, cssBind.text, cssBind.offset, diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index f7a55b2caf..feac4f497a 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -17,6 +17,7 @@ export interface TemplateCodegenOptions { edited: boolean; scriptSetupBindingNames: Set; scriptSetupImportComponentNames: Set; + destructuredPropNames: Set; templateRefNames: Set; hasDefineSlots?: boolean; slotsAssignName?: string; diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index fb6f4611cb..f2b35d5f12 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -25,6 +25,7 @@ export function* generateInterpolation( }[] = []; for (let [section, offset, onlyError] of forEachInterpolationSegment( options.ts, + options.destructuredPropNames, options.templateRefNames, ctx, code, @@ -72,6 +73,7 @@ export function* generateInterpolation( export function* forEachInterpolationSegment( ts: typeof import('typescript'), + destructuredPropNames: Set | undefined, templateRefNames: Set | undefined, ctx: TemplateCodegenContext, code: string, @@ -101,6 +103,9 @@ export function* forEachInterpolationSegment( isShorthand: isShorthand, offset: getStartEnd(ts, id, ast).start, }); + if (destructuredPropNames?.has(text)) { + return; + } if (offset !== undefined) { ctx.accessExternalVariable(text, offset + getStartEnd(ts, id, ast).start); } @@ -127,7 +132,7 @@ export function* forEachInterpolationSegment( const curVar = ctxVars[i]; const nextVar = ctxVars[i + 1]; - yield* generateVar(code, templateRefNames, curVar, nextVar); + yield* generateVar(code, destructuredPropNames, templateRefNames, curVar, nextVar); if (nextVar.isShorthand) { yield [code.substring(curVar.offset + curVar.text.length, nextVar.offset + nextVar.text.length), curVar.offset + curVar.text.length]; @@ -139,7 +144,7 @@ export function* forEachInterpolationSegment( } const lastVar = ctxVars.at(-1)!; - yield* generateVar(code, templateRefNames, lastVar); + yield* generateVar(code, destructuredPropNames, templateRefNames, lastVar); yield [code.substring(lastVar.offset + lastVar.text.length), lastVar.offset + lastVar.text.length]; } else { @@ -149,6 +154,7 @@ export function* forEachInterpolationSegment( function* generateVar( code: string, + destructuredPropNames: Set | undefined, templateRefNames: Set | undefined, curVar: { text: string, @@ -165,6 +171,7 @@ function* generateVar( // fix https://github.com/vuejs/language-tools/issues/1264 yield ['', nextVar.offset, true]; + const isDestructuredProp = destructuredPropNames?.has(curVar.text) ?? false; const isTemplateRef = templateRefNames?.has(curVar.text) ?? false; if (isTemplateRef) { yield [`__VLS_unref(`, undefined]; @@ -172,7 +179,9 @@ function* generateVar( yield [`)`, undefined]; } else { - yield [`__VLS_ctx.`, undefined]; + if (!isDestructuredProp) { + yield [`__VLS_ctx.`, undefined]; + } yield [code.substring(curVar.offset, curVar.offset + curVar.text.length), curVar.offset]; } } diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index c529addb79..3afcf24a4a 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -104,6 +104,7 @@ function createTsx( edited: ctx.vueCompilerOptions.__test || (fileEditTimes.get(fileName) ?? 0) >= 2, scriptSetupBindingNames: scriptSetupBindingNames(), scriptSetupImportComponentNames: scriptSetupImportComponentNames(), + destructuredPropNames: destructuredPropNames(), templateRefNames: templateRefNames(), hasDefineSlots: hasDefineSlots(), slotsAssignName: slotsAssignName(), @@ -144,6 +145,13 @@ function createTsx( } return newNames; }); + const destructuredPropNames = computed>(oldNames => { + const newNames = scriptSetupRanges()?.props.destructured ?? new Set(); + if (oldNames && twoSetsEqual(newNames, oldNames)) { + return oldNames; + } + return newNames; + }); const templateRefNames = computed>(oldNames => { const newNames = new Set( scriptSetupRanges()?.templateRefs From f73483ba66830b4dfcff9fd8e4c302a0ce9c9678 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 7 Sep 2024 02:37:01 +0800 Subject: [PATCH 6/7] Update failed test --- .../tsc/passedFixtures/vue3/#4820/main.vue | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test-workspace/tsc/passedFixtures/vue3/#4820/main.vue b/test-workspace/tsc/passedFixtures/vue3/#4820/main.vue index 40870f6b4a..3a43973dc8 100644 --- a/test-workspace/tsc/passedFixtures/vue3/#4820/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3/#4820/main.vue @@ -1,19 +1,22 @@ \ No newline at end of file + {{ func(foo, fooKey) }} + {{ func(props.bar, props.barKey) }} + From 71d14318a56d073022fce98f404b69ca3c9e62ec Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sat, 7 Sep 2024 02:56:39 +0800 Subject: [PATCH 7/7] fix: collect rest props --- packages/language-core/lib/codegen/common.ts | 21 ++++++++----------- .../lib/parsers/scriptSetupRanges.ts | 15 +++++++++++-- packages/language-core/lib/plugins/vue-tsx.ts | 4 ++++ .../lib/plugins/vue-inlayhints.ts | 4 ++-- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/language-core/lib/codegen/common.ts b/packages/language-core/lib/codegen/common.ts index 5c8814310e..a2d925b5d3 100644 --- a/packages/language-core/lib/codegen/common.ts +++ b/packages/language-core/lib/codegen/common.ts @@ -45,11 +45,10 @@ export function collectVars( ts: typeof import('typescript'), node: ts.Node, ast: ts.SourceFile, - results: string[] = [], - includesRest = true + results: string[] = [] ) { - const identifiers = collectIdentifiers(ts, node, [], includesRest); - for (const id of identifiers) { + const identifiers = collectIdentifiers(ts, node, []); + for (const [id] of identifiers) { results.push(getNodeText(ts, id, ast)); } return results; @@ -58,28 +57,26 @@ export function collectVars( export function collectIdentifiers( ts: typeof import('typescript'), node: ts.Node, - results: ts.Identifier[] = [], - includesRest = true + results: [id: ts.Identifier, isRest: boolean][] = [], + isRest = false ) { if (ts.isIdentifier(node)) { - results.push(node); + results.push([node, isRest]); } else if (ts.isObjectBindingPattern(node)) { for (const el of node.elements) { - if (includesRest || !el.dotDotDotToken) { - collectIdentifiers(ts, el.name, results, includesRest); - } + collectIdentifiers(ts, el.name, results, !!el.dotDotDotToken); } } else if (ts.isArrayBindingPattern(node)) { for (const el of node.elements) { if (ts.isBindingElement(el)) { - collectIdentifiers(ts, el.name, results, includesRest); + collectIdentifiers(ts, el.name, results, !!el.dotDotDotToken); } } } else { - ts.forEachChild(node, node => collectIdentifiers(ts, node, results, includesRest)); + ts.forEachChild(node, node => collectIdentifiers(ts, node, results, false)); } return results; } diff --git a/packages/language-core/lib/parsers/scriptSetupRanges.ts b/packages/language-core/lib/parsers/scriptSetupRanges.ts index f59b0105ec..4feccbde61 100644 --- a/packages/language-core/lib/parsers/scriptSetupRanges.ts +++ b/packages/language-core/lib/parsers/scriptSetupRanges.ts @@ -1,6 +1,6 @@ import type * as ts from 'typescript'; import type { VueCompilerOptions, TextRange } from '../types'; -import { collectVars } from '../codegen/common'; +import { collectIdentifiers } from '../codegen/common'; export interface ScriptSetupRanges extends ReturnType { } @@ -16,6 +16,7 @@ export function parseScriptSetupRanges( const props: { name?: string; destructured?: Set; + destructuredRest?: string; define?: ReturnType & { statement: TextRange; }; @@ -308,7 +309,17 @@ export function parseScriptSetupRanges( else if (vueCompilerOptions.macros.defineProps.includes(callText)) { if (ts.isVariableDeclaration(parent)) { if (ts.isObjectBindingPattern(parent.name)) { - props.destructured = new Set(collectVars(ts, parent.name, ast, [], false)); + props.destructured = new Set(); + const identifiers = collectIdentifiers(ts, parent.name, []); + for (const [id, isRest] of identifiers) { + const name = getNodeText(ts, id, ast); + if (isRest) { + props.destructuredRest = name; + } + else { + props.destructured.add(name); + } + } } else { props.name = getNodeText(ts, parent.name, ast); diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index 3afcf24a4a..5bbea9aec1 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -147,6 +147,10 @@ function createTsx( }); const destructuredPropNames = computed>(oldNames => { const newNames = scriptSetupRanges()?.props.destructured ?? new Set(); + const rest = scriptSetupRanges()?.props.destructuredRest; + if (rest) { + newNames.add(rest); + } if (oldNames && twoSetsEqual(newNames, oldNames)) { return oldNames; } diff --git a/packages/language-service/lib/plugins/vue-inlayhints.ts b/packages/language-service/lib/plugins/vue-inlayhints.ts index 8b1b413bcb..1e0f28f2c3 100644 --- a/packages/language-service/lib/plugins/vue-inlayhints.ts +++ b/packages/language-service/lib/plugins/vue-inlayhints.ts @@ -180,7 +180,7 @@ export function findDestructuredProps( && ts.isCallExpression(initializer) && initializer.expression.getText(ast) === 'defineProps'; - for (const id of collectIdentifiers(ts, name)) { + for (const [id] of collectIdentifiers(ts, name)) { if (isDefineProps) { excludedIds.add(id); } else { @@ -196,7 +196,7 @@ export function findDestructuredProps( } for (const p of parameters) { - for (const id of collectIdentifiers(ts, p)) { + for (const [id] of collectIdentifiers(ts, p)) { registerLocalBinding(id); } }