From 78e90f138aa2b2df2eb4b1e178d8b8a9052a4d39 Mon Sep 17 00:00:00 2001 From: Rizumu Ayaka Date: Wed, 9 Jul 2025 04:13:42 +0800 Subject: [PATCH 1/2] fix(compiler-vapor): empty interpolation --- .../__snapshots__/expression.spec.ts.snap | 47 +++++++++++++++++++ .../__tests__/transforms/expression.spec.ts | 21 +++++++++ .../src/transforms/transformText.ts | 26 ++++++++-- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap index 7e157236bf9..fda0121d632 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap @@ -11,6 +11,53 @@ export function render(_ctx) { }" `; +exports[`compiler: expression > empty interpolation 1`] = ` +"import { template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const n0 = t0() + return n0 +}" +`; + +exports[`compiler: expression > empty interpolation 2`] = ` +"import { template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const n0 = t0() + return n0 +}" +`; + +exports[`compiler: expression > empty interpolation 3`] = ` +"import { template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + return n0 +}" +`; + +exports[`compiler: expression > empty interpolation 4`] = ` +"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n1 = t0() + const n0 = _child(n1) + const x1 = _child(n1) + _renderEffect(() => { + const _foo = _ctx.foo + _setText(n0, _toDisplayString(_foo)) + _setText(x1, _toDisplayString(_foo)) + }) + return n1 +}" +`; + exports[`compiler: expression > props 1`] = ` "import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template(" ") diff --git a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts index 5983bde67d1..9257a714b13 100644 --- a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts @@ -47,4 +47,25 @@ describe('compiler: expression', () => { expect(code).toMatchSnapshot() expect(code).contains(`_String(_foo.id++)`) }) + + test('empty interpolation', () => { + const { code } = compileWithExpression(`{{}}`) + const { code: code2 } = compileWithExpression(`{{ }}`) + const { code: code3 } = compileWithExpression(`
{{ }}
`) + const { code: code4 } = compileWithExpression(`
{{ foo }}{{ }}
`) + + expect(code).toMatchSnapshot() + expect(code).not.toContain(`_toDisplayString`) + expect(code).not.toContain(`_setText`) + + expect(code2).toMatchSnapshot() + expect(code2).not.toContain(`_toDisplayString`) + expect(code2).not.toContain(`_setText`) + + expect(code3).toMatchSnapshot() + expect(code3).not.toContain(`_toDisplayString`) + expect(code3).not.toContain(`_setText`) + + expect(code4).toMatchSnapshot() + }) }) diff --git a/packages/compiler-vapor/src/transforms/transformText.ts b/packages/compiler-vapor/src/transforms/transformText.ts index 5f858058f27..9dca7d9633f 100644 --- a/packages/compiler-vapor/src/transforms/transformText.ts +++ b/packages/compiler-vapor/src/transforms/transformText.ts @@ -87,7 +87,8 @@ export const transformText: NodeTransform = (node, context) => { } function processInterpolation(context: TransformContext) { - const children = context.parent!.node.children + const parentNode = context.parent!.node + const children = parentNode.children const nexts = children.slice(context.index) const idx = nexts.findIndex(n => !isTextLike(n)) const nodes = (idx > -1 ? nexts.slice(0, idx) : nexts) as Array @@ -98,9 +99,22 @@ function processInterpolation(context: TransformContext) { nodes.unshift(prev) } + const values = nodes + .map(node => createTextLikeExpression(node, context)) + .filter(v => + v.type === NodeTypes.SIMPLE_EXPRESSION ? v.content.length !== 0 : true, + ) + + if (values.length === 0 && parentNode.type !== NodeTypes.ROOT) { + return + } + context.template += ' ' const id = context.reference() - const values = nodes.map(node => createTextLikeExpression(node, context)) + + if (values.length === 0) { + return + } const nonConstantExps = values.filter(v => !isConstantExpression(v)) const isStatic = @@ -129,8 +143,14 @@ function processTextContainer( children: TextLike[], context: TransformContext, ) { - const values = children.map(child => createTextLikeExpression(child, context)) + const values = children + .map(child => createTextLikeExpression(child, context)) + .filter(v => + v.type === NodeTypes.SIMPLE_EXPRESSION ? v.content.length !== 0 : true, + ) + const literals = values.map(getLiteralExpressionValue) + if (literals.every(l => l != null)) { context.childrenTemplate = literals.map(l => String(l)) } else { From 214fd4aa4cfcaaa4d20758d35f40f07a443fb7d8 Mon Sep 17 00:00:00 2001 From: Rizumu Ayaka Date: Thu, 10 Jul 2025 09:29:16 +0800 Subject: [PATCH 2/2] refactor: avoid repetition --- .../src/transforms/transformText.ts | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/compiler-vapor/src/transforms/transformText.ts b/packages/compiler-vapor/src/transforms/transformText.ts index 9dca7d9633f..e9c273b85c7 100644 --- a/packages/compiler-vapor/src/transforms/transformText.ts +++ b/packages/compiler-vapor/src/transforms/transformText.ts @@ -98,12 +98,7 @@ function processInterpolation(context: TransformContext) { if (prev && prev.type === NodeTypes.TEXT) { nodes.unshift(prev) } - - const values = nodes - .map(node => createTextLikeExpression(node, context)) - .filter(v => - v.type === NodeTypes.SIMPLE_EXPRESSION ? v.content.length !== 0 : true, - ) + const values = processTextLikeChildren(nodes, context) if (values.length === 0 && parentNode.type !== NodeTypes.ROOT) { return @@ -143,11 +138,7 @@ function processTextContainer( children: TextLike[], context: TransformContext, ) { - const values = children - .map(child => createTextLikeExpression(child, context)) - .filter(v => - v.type === NodeTypes.SIMPLE_EXPRESSION ? v.content.length !== 0 : true, - ) + const values = processTextLikeChildren(children, context) const literals = values.map(getLiteralExpressionValue) @@ -169,13 +160,22 @@ function processTextContainer( } } -function createTextLikeExpression(node: TextLike, context: TransformContext) { - markNonTemplate(node, context) - if (node.type === NodeTypes.TEXT) { - return createSimpleExpression(node.content, true, node.loc) - } else { - return node.content as SimpleExpressionNode +function processTextLikeChildren(nodes: TextLike[], context: TransformContext) { + const exps: SimpleExpressionNode[] = [] + for (const node of nodes) { + let exp: SimpleExpressionNode + markNonTemplate(node, context) + + if (node.type === NodeTypes.TEXT) { + exp = createSimpleExpression(node.content, true, node.loc) + } else { + exp = node.content as SimpleExpressionNode + } + + if (exp.content) exps.push(exp) } + + return exps } function isTextLike(node: TemplateChildNode): node is TextLike {