From fd8fc3149dcc98b739119ee797e887732601ffae 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: Thu, 17 Jul 2025 18:21:19 +0800 Subject: [PATCH 1/5] fix: avoid redundant increment of block variable depth --- .../language-core/lib/codegen/template/interpolation.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index 8c1c3e8938..dbb5dd7c76 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -211,10 +211,11 @@ function walkIdentifiers( walkIdentifiers(ts, node.expression, ast, cb, ctx, blockVars, false); } else if (ts.isVariableDeclaration(node)) { - collectVars(ts, node.name, ast, blockVars); + const bindingNames = collectVars(ts, node.name, ast); - for (const varName of blockVars) { - ctx.addLocalVariable(varName); + for (const name of bindingNames) { + ctx.addLocalVariable(name); + blockVars.push(name); } if (node.initializer) { From dc3b4d3897e0223dd7b7fd26a9f32e6028d72739 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: Thu, 17 Jul 2025 18:23:15 +0800 Subject: [PATCH 2/5] refactor: rename to `collectBindingNames` --- .../language-core/lib/codegen/template/interpolation.ts | 6 +++--- packages/language-core/lib/codegen/template/vFor.ts | 4 ++-- packages/language-core/lib/codegen/template/vSlot.ts | 4 ++-- packages/language-core/lib/codegen/utils/index.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index dbb5dd7c76..e970aa39fa 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -3,7 +3,7 @@ import type * as ts from 'typescript'; import type { Code, VueCodeInformation } from '../../types'; import { getNodeText, getStartEnd } from '../../utils/shared'; import type { ScriptCodegenOptions } from '../script'; -import { collectVars, createTsAst, identifierRegex } from '../utils'; +import { collectBindingNames, createTsAst, identifierRegex } from '../utils'; import type { TemplateCodegenContext } from './context'; import type { TemplateCodegenOptions } from './index'; @@ -211,7 +211,7 @@ function walkIdentifiers( walkIdentifiers(ts, node.expression, ast, cb, ctx, blockVars, false); } else if (ts.isVariableDeclaration(node)) { - const bindingNames = collectVars(ts, node.name, ast); + const bindingNames = collectBindingNames(ts, node.name, ast); for (const name of bindingNames) { ctx.addLocalVariable(name); @@ -283,7 +283,7 @@ function processFunction( ) { const functionArgs: string[] = []; for (const param of node.parameters) { - collectVars(ts, param.name, ast, functionArgs); + collectBindingNames(ts, param.name, ast, functionArgs); if (param.type) { walkIdentifiers(ts, param.type, ast, cb, ctx); } diff --git a/packages/language-core/lib/codegen/template/vFor.ts b/packages/language-core/lib/codegen/template/vFor.ts index c3f2904352..b55a8c1d7e 100644 --- a/packages/language-core/lib/codegen/template/vFor.ts +++ b/packages/language-core/lib/codegen/template/vFor.ts @@ -1,6 +1,6 @@ import * as CompilerDOM from '@vue/compiler-dom'; import type { Code } from '../../types'; -import { collectVars, createTsAst, endOfLine, newLine } from '../utils'; +import { collectBindingNames, createTsAst, endOfLine, newLine } from '../utils'; import type { TemplateCodegenContext } from './context'; import { generateElementChildren } from './elementChildren'; import type { TemplateCodegenOptions } from './index'; @@ -18,7 +18,7 @@ export function* generateVFor( yield `for (const [`; if (leftExpressionRange && leftExpressionText) { const collectAst = createTsAst(options.ts, ctx.inlineTsAsts, `const [${leftExpressionText}]`); - collectVars(options.ts, collectAst, collectAst, forBlockVars); + collectBindingNames(options.ts, collectAst, collectAst, forBlockVars); yield [ leftExpressionText, 'template', diff --git a/packages/language-core/lib/codegen/template/vSlot.ts b/packages/language-core/lib/codegen/template/vSlot.ts index 5dd5e81576..e58ee2929e 100644 --- a/packages/language-core/lib/codegen/template/vSlot.ts +++ b/packages/language-core/lib/codegen/template/vSlot.ts @@ -2,7 +2,7 @@ import * as CompilerDOM from '@vue/compiler-dom'; import type * as ts from 'typescript'; import type { Code } from '../../types'; import { getStartEnd } from '../../utils/shared'; -import { collectVars, createTsAst, endOfLine, newLine } from '../utils'; +import { collectBindingNames, createTsAst, endOfLine, newLine } from '../utils'; import { wrapWith } from '../utils/wrapWith'; import type { TemplateCodegenContext } from './context'; import { generateElementChildren } from './elementChildren'; @@ -64,7 +64,7 @@ export function* generateVSlot( if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { const slotAst = createTsAst(options.ts, ctx.inlineTsAsts, `(${slotDir.exp.content}) => {}`); - collectVars(options.ts, slotAst, slotAst, slotBlockVars); + collectBindingNames(options.ts, slotAst, slotAst, slotBlockVars); yield* generateSlotParameters(options, ctx, slotAst, slotDir.exp, slotVar); } diff --git a/packages/language-core/lib/codegen/utils/index.ts b/packages/language-core/lib/codegen/utils/index.ts index 0c599bb3d5..a05f22b3a2 100644 --- a/packages/language-core/lib/codegen/utils/index.ts +++ b/packages/language-core/lib/codegen/utils/index.ts @@ -8,7 +8,7 @@ export const endOfLine = `;${newLine}`; export const combineLastMapping: VueCodeInformation = { __combineOffset: 1 }; export const identifierRegex = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/; -export function collectVars( +export function collectBindingNames( ts: typeof import('typescript'), node: ts.Node, ast: ts.SourceFile, From 6ca8c9eccc88238fad5501dd1dec66812aed3b6a 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: Thu, 17 Jul 2025 18:25:35 +0800 Subject: [PATCH 3/5] refactor: set `isRoot` to `false` by default --- .../lib/codegen/template/interpolation.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index e970aa39fa..bca2b7dd12 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -126,7 +126,7 @@ function* forEachInterpolationSegment( }); } }; - ts.forEachChild(ast, node => walkIdentifiers(ts, node, ast, varCb, ctx)); + ts.forEachChild(ast, node => walkIdentifiers(ts, node, ast, varCb, ctx, [], true)); } ctxVars = ctxVars.sort((a, b) => a.offset - b.offset); @@ -198,8 +198,8 @@ function walkIdentifiers( ast: ts.SourceFile, cb: (varNode: ts.Identifier, isShorthand: boolean) => void, ctx: TemplateCodegenContext, - blockVars: string[] = [], - isRoot: boolean = true, + blockVars: string[], + isRoot: boolean = false, ) { if (ts.isIdentifier(node)) { cb(node, false); @@ -208,7 +208,7 @@ function walkIdentifiers( cb(node.name, true); } else if (ts.isPropertyAccessExpression(node)) { - walkIdentifiers(ts, node.expression, ast, cb, ctx, blockVars, false); + walkIdentifiers(ts, node.expression, ast, cb, ctx, blockVars); } else if (ts.isVariableDeclaration(node)) { const bindingNames = collectBindingNames(ts, node.name, ast); @@ -219,7 +219,7 @@ function walkIdentifiers( } if (node.initializer) { - walkIdentifiers(ts, node.initializer, ast, cb, ctx, blockVars, false); + walkIdentifiers(ts, node.initializer, ast, cb, ctx, blockVars); } } else if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) { @@ -230,18 +230,18 @@ function walkIdentifiers( if (ts.isPropertyAssignment(prop)) { // fix https://github.com/vuejs/language-tools/issues/1176 if (ts.isComputedPropertyName(prop.name)) { - walkIdentifiers(ts, prop.name.expression, ast, cb, ctx, blockVars, false); + walkIdentifiers(ts, prop.name.expression, ast, cb, ctx, blockVars); } - walkIdentifiers(ts, prop.initializer, ast, cb, ctx, blockVars, false); + walkIdentifiers(ts, prop.initializer, ast, cb, ctx, blockVars); } // fix https://github.com/vuejs/language-tools/issues/1156 else if (ts.isShorthandPropertyAssignment(prop)) { - walkIdentifiers(ts, prop, ast, cb, ctx, blockVars, false); + walkIdentifiers(ts, prop, ast, cb, ctx, blockVars); } // fix https://github.com/vuejs/language-tools/issues/1148#issuecomment-1094378126 else if (ts.isSpreadAssignment(prop)) { // TODO: cannot report "Spread types may only be created from object types.ts(2698)" - walkIdentifiers(ts, prop.expression, ast, cb, ctx, blockVars, false); + walkIdentifiers(ts, prop.expression, ast, cb, ctx, blockVars); } // fix https://github.com/vuejs/language-tools/issues/4604 else if (ts.isFunctionLike(prop) && prop.body) { @@ -258,7 +258,7 @@ function walkIdentifiers( if (ts.isBlock(node)) { blockVars = []; } - ts.forEachChild(node, node => walkIdentifiers(ts, node, ast, cb, ctx, blockVars, false)); + ts.forEachChild(node, node => walkIdentifiers(ts, node, ast, cb, ctx, blockVars)); if (ts.isBlock(node)) { for (const varName of blockVars) { ctx.removeLocalVariable(varName); @@ -285,14 +285,14 @@ function processFunction( for (const param of node.parameters) { collectBindingNames(ts, param.name, ast, functionArgs); if (param.type) { - walkIdentifiers(ts, param.type, ast, cb, ctx); + walkIdentifiers(ts, param.type, ast, cb, ctx, [], true); } } for (const varName of functionArgs) { ctx.addLocalVariable(varName); } if (node.body) { - walkIdentifiers(ts, node.body, ast, cb, ctx); + walkIdentifiers(ts, node.body, ast, cb, ctx, [], true); } for (const varName of functionArgs) { ctx.removeLocalVariable(varName); From c2787dee8ccbe86efa2ab4063ff26b090d382bac 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: Thu, 17 Jul 2025 18:26:47 +0800 Subject: [PATCH 4/5] refactor: `processFunction` -> `walkIdentifiersInFunction` --- .../language-core/lib/codegen/template/interpolation.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index bca2b7dd12..5878ff32f9 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -223,7 +223,7 @@ function walkIdentifiers( } } else if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) { - processFunction(ts, node, ast, cb, ctx); + walkIdentifiersInFunction(ts, node, ast, cb, ctx); } else if (ts.isObjectLiteralExpression(node)) { for (const prop of node.properties) { @@ -245,7 +245,7 @@ function walkIdentifiers( } // fix https://github.com/vuejs/language-tools/issues/4604 else if (ts.isFunctionLike(prop) && prop.body) { - processFunction(ts, prop, ast, cb, ctx); + walkIdentifiersInFunction(ts, prop, ast, cb, ctx); } } } @@ -274,7 +274,7 @@ function walkIdentifiers( } } -function processFunction( +function walkIdentifiersInFunction( ts: typeof import('typescript'), node: ts.ArrowFunction | ts.FunctionExpression | ts.AccessorDeclaration | ts.MethodDeclaration, ast: ts.SourceFile, From 9d3fe9cb09a272f7bdcc4a1ce8d26771a617a6e0 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: Thu, 17 Jul 2025 18:31:33 +0800 Subject: [PATCH 5/5] refactor: remove `result` parameter to avoid side effects --- .../language-core/lib/codegen/template/interpolation.ts | 6 +++--- packages/language-core/lib/codegen/template/vFor.ts | 2 +- packages/language-core/lib/codegen/template/vSlot.ts | 2 +- packages/language-core/lib/codegen/utils/index.ts | 7 +------ packages/language-core/lib/parsers/scriptSetupRanges.ts | 2 +- 5 files changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index 5878ff32f9..064bb0b2d0 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -249,8 +249,8 @@ function walkIdentifiers( } } } + // fix https://github.com/vuejs/language-tools/issues/1422 else if (ts.isTypeNode(node)) { - // fix https://github.com/vuejs/language-tools/issues/1422 walkIdentifiersInTypeNode(ts, node, cb); } else { @@ -283,9 +283,9 @@ function walkIdentifiersInFunction( ) { const functionArgs: string[] = []; for (const param of node.parameters) { - collectBindingNames(ts, param.name, ast, functionArgs); + functionArgs.push(...collectBindingNames(ts, param.name, ast)); if (param.type) { - walkIdentifiers(ts, param.type, ast, cb, ctx, [], true); + walkIdentifiersInTypeNode(ts, param.type, cb); } } for (const varName of functionArgs) { diff --git a/packages/language-core/lib/codegen/template/vFor.ts b/packages/language-core/lib/codegen/template/vFor.ts index b55a8c1d7e..794602f42a 100644 --- a/packages/language-core/lib/codegen/template/vFor.ts +++ b/packages/language-core/lib/codegen/template/vFor.ts @@ -18,7 +18,7 @@ export function* generateVFor( yield `for (const [`; if (leftExpressionRange && leftExpressionText) { const collectAst = createTsAst(options.ts, ctx.inlineTsAsts, `const [${leftExpressionText}]`); - collectBindingNames(options.ts, collectAst, collectAst, forBlockVars); + forBlockVars.push(...collectBindingNames(options.ts, collectAst, collectAst)); yield [ leftExpressionText, 'template', diff --git a/packages/language-core/lib/codegen/template/vSlot.ts b/packages/language-core/lib/codegen/template/vSlot.ts index e58ee2929e..e1e5377294 100644 --- a/packages/language-core/lib/codegen/template/vSlot.ts +++ b/packages/language-core/lib/codegen/template/vSlot.ts @@ -64,7 +64,7 @@ export function* generateVSlot( if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { const slotAst = createTsAst(options.ts, ctx.inlineTsAsts, `(${slotDir.exp.content}) => {}`); - collectBindingNames(options.ts, slotAst, slotAst, slotBlockVars); + slotBlockVars.push(...collectBindingNames(options.ts, slotAst, slotAst)); yield* generateSlotParameters(options, ctx, slotAst, slotDir.exp, slotVar); } diff --git a/packages/language-core/lib/codegen/utils/index.ts b/packages/language-core/lib/codegen/utils/index.ts index a05f22b3a2..92b80bc630 100644 --- a/packages/language-core/lib/codegen/utils/index.ts +++ b/packages/language-core/lib/codegen/utils/index.ts @@ -12,13 +12,8 @@ export function collectBindingNames( ts: typeof import('typescript'), node: ts.Node, ast: ts.SourceFile, - results: string[] = [], ) { - const identifiers = collectIdentifiers(ts, node, []); - for (const { id } of identifiers) { - results.push(getNodeText(ts, id, ast)); - } - return results; + return collectIdentifiers(ts, node).map(({ id }) => getNodeText(ts, id, ast)); } export function collectIdentifiers( diff --git a/packages/language-core/lib/parsers/scriptSetupRanges.ts b/packages/language-core/lib/parsers/scriptSetupRanges.ts index 64bf59d34b..5dabc257ed 100644 --- a/packages/language-core/lib/parsers/scriptSetupRanges.ts +++ b/packages/language-core/lib/parsers/scriptSetupRanges.ts @@ -236,7 +236,7 @@ export function parseScriptSetupRanges( }; if (ts.isVariableDeclaration(parent) && ts.isObjectBindingPattern(parent.name)) { defineProps.destructured = new Map(); - const identifiers = collectIdentifiers(ts, parent.name, []); + const identifiers = collectIdentifiers(ts, parent.name); for (const { id, isRest, initializer } of identifiers) { const name = _getNodeText(id); if (isRest) {