From f6d8c7cdf5108c502d99052b5e887ba0221c4720 Mon Sep 17 00:00:00 2001 From: KazariEX Date: Fri, 29 Aug 2025 13:24:47 +0800 Subject: [PATCH 1/2] fix: generate slot parameters in the same way as interpolation --- .../lib/codegen/template/interpolation.ts | 34 ++++++++--- .../lib/codegen/template/vSlot.ts | 58 ++++++++++--------- .../tsc/passedFixtures/vue3/#5617/main.vue | 15 +++++ 3 files changed, 73 insertions(+), 34 deletions(-) create mode 100644 test-workspace/tsc/passedFixtures/vue3/#5617/main.vue diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index c097deeb4e..85f27813f3 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -214,14 +214,17 @@ function walkIdentifiers( } else if (ts.isVariableDeclaration(node)) { const bindingNames = collectBindingNames(ts, node.name, ast); - for (const name of bindingNames) { ctx.addLocalVariable(name); blockVars.push(name); } - - if (node.initializer) { - walkIdentifiers(ts, node.initializer, ast, cb, ctx, blockVars); + walkIdentifiersInBinding(ts, node, ast, cb, ctx, blockVars); + } + else if (ts.isArrayBindingPattern(node) || ts.isObjectBindingPattern(node)) { + for (const element of node.elements) { + if (ts.isBindingElement(element)) { + walkIdentifiersInBinding(ts, element, ast, cb, ctx, blockVars); + } } } else if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) { @@ -276,6 +279,25 @@ function walkIdentifiers( } } +function walkIdentifiersInBinding( + ts: typeof import('typescript'), + node: ts.BindingElement | ts.ParameterDeclaration | ts.VariableDeclaration, + ast: ts.SourceFile, + cb: (varNode: ts.Identifier, isShorthand: boolean) => void, + ctx: TemplateCodegenContext, + blockVars: string[], +) { + if ('type' in node && node.type) { + walkIdentifiersInTypeNode(ts, node.type, cb); + } + if (!ts.isIdentifier(node.name)) { + walkIdentifiers(ts, node.name, ast, cb, ctx, blockVars); + } + if (node.initializer) { + walkIdentifiers(ts, node.initializer, ast, cb, ctx, blockVars); + } +} + function walkIdentifiersInFunction( ts: typeof import('typescript'), node: ts.ArrowFunction | ts.FunctionExpression | ts.AccessorDeclaration | ts.MethodDeclaration, @@ -286,9 +308,7 @@ function walkIdentifiersInFunction( const functionArgs: string[] = []; for (const param of node.parameters) { functionArgs.push(...collectBindingNames(ts, param.name, ast)); - if (param.type) { - walkIdentifiersInTypeNode(ts, param.type, cb); - } + walkIdentifiersInBinding(ts, param, ast, cb, ctx, functionArgs); } for (const varName of functionArgs) { ctx.addLocalVariable(varName); diff --git a/packages/language-core/lib/codegen/template/vSlot.ts b/packages/language-core/lib/codegen/template/vSlot.ts index 670ea9229c..96e5626a62 100644 --- a/packages/language-core/lib/codegen/template/vSlot.ts +++ b/packages/language-core/lib/codegen/template/vSlot.ts @@ -1,14 +1,15 @@ import * as CompilerDOM from '@vue/compiler-dom'; +import { replaceSourceRange } from 'muggle-string'; import type * as ts from 'typescript'; import type { Code } from '../../types'; import { collectBindingNames } from '../../utils/collectBindings'; -import { getStartEnd } from '../../utils/shared'; import { codeFeatures } from '../codeFeatures'; import { createTsAst, endOfLine, newLine } from '../utils'; import { wrapWith } from '../utils/wrapWith'; import type { TemplateCodegenContext } from './context'; import { generateElementChildren } from './elementChildren'; import type { TemplateCodegenOptions } from './index'; +import { generateInterpolation } from './interpolation'; import { generateObjectProperty } from './objectProperty'; export function* generateVSlot( @@ -67,7 +68,7 @@ export function* generateVSlot( if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { const slotAst = createTsAst(options.ts, ctx.inlineTsAsts, `(${slotDir.exp.content}) => {}`); slotBlockVars.push(...collectBindingNames(options.ts, slotAst, slotAst)); - yield* generateSlotParameters(options, slotAst, slotDir.exp, slotVar); + yield* generateSlotParameters(options, ctx, slotAst, slotDir.exp, slotVar); } for (const varName of slotBlockVars) { @@ -107,6 +108,7 @@ export function* generateVSlot( function* generateSlotParameters( options: TemplateCodegenOptions, + ctx: TemplateCodegenContext, ast: ts.SourceFile, exp: CompilerDOM.SimpleExpressionNode, slotVar: string, @@ -120,31 +122,42 @@ function* generateSlotParameters( const { expression } = statement; const startOffset = exp.loc.start.offset - 1; - const modifies: [Code[], number, number][] = []; - const types: (Code | null)[] = []; + const types: (Code | undefined)[] = []; + + const interpolation = [...generateInterpolation( + options, + ctx, + 'template', + codeFeatures.all, + ast.text, + startOffset, + )]; + + replaceSourceRange(interpolation, 'template', startOffset, startOffset + `(`.length); + replaceSourceRange( + interpolation, + 'template', + startOffset + ast.text.length - `) => {}`.length, + startOffset + ast.text.length, + ); for (const { name, type } of expression.parameters) { if (type) { - modifies.push([ - [``], - name.end, - type.end, + types.push([ + ast.text.slice(name.end, type.end), + 'template', + startOffset + name.end, + codeFeatures.all, ]); - types.push(chunk(getStartEnd(ts, type, ast).start, type.end)); + replaceSourceRange(interpolation, 'template', startOffset + name.end, startOffset + type.end); } else { - types.push(null); + types.push(undefined); } } yield `const [`; - let nextStart = 1; - for (const [codes, start, end] of modifies) { - yield chunk(nextStart, start); - yield* codes; - nextStart = end; - } - yield chunk(nextStart, expression.equalsGreaterThanToken.pos - 1); + yield* interpolation; yield `] = __VLS_getSlotParameters(${slotVar}!`; if (types.some(t => t)) { @@ -154,18 +167,9 @@ function* generateSlotParameters( exp.loc.end.offset, codeFeatures.verification, `(`, - ...types.flatMap(type => type ? [`_: `, type, `, `] : `_, `), + ...types.flatMap(type => type ? [`_`, type, `, `] : `_, `), `) => [] as any`, ); } yield `)${endOfLine}`; - - function chunk(start: number, end: number): Code { - return [ - ast.text.slice(start, end), - 'template', - startOffset + start, - codeFeatures.all, - ]; - } } diff --git a/test-workspace/tsc/passedFixtures/vue3/#5617/main.vue b/test-workspace/tsc/passedFixtures/vue3/#5617/main.vue new file mode 100644 index 0000000000..1c62995a57 --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3/#5617/main.vue @@ -0,0 +1,15 @@ + + + From 3f86bb190aeab1f5c93b66a37bb483fd78e7161b Mon Sep 17 00:00:00 2001 From: KazariEX Date: Fri, 29 Aug 2025 13:31:15 +0800 Subject: [PATCH 2/2] refactor: restore `Code | null` --- packages/language-core/lib/codegen/template/vSlot.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/language-core/lib/codegen/template/vSlot.ts b/packages/language-core/lib/codegen/template/vSlot.ts index 96e5626a62..f42ce04de7 100644 --- a/packages/language-core/lib/codegen/template/vSlot.ts +++ b/packages/language-core/lib/codegen/template/vSlot.ts @@ -122,7 +122,7 @@ function* generateSlotParameters( const { expression } = statement; const startOffset = exp.loc.start.offset - 1; - const types: (Code | undefined)[] = []; + const types: (Code | null)[] = []; const interpolation = [...generateInterpolation( options, @@ -152,7 +152,7 @@ function* generateSlotParameters( replaceSourceRange(interpolation, 'template', startOffset + name.end, startOffset + type.end); } else { - types.push(undefined); + types.push(null); } }