]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-vapor): handle empty interpolation (#13592)
authorRizumu Ayaka <rizumu@ayaka.moe>
Fri, 18 Jul 2025 07:24:52 +0000 (15:24 +0800)
committerGitHub <noreply@github.com>
Fri, 18 Jul 2025 07:24:52 +0000 (15:24 +0800)
packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/expression.spec.ts
packages/compiler-vapor/src/transforms/transformText.ts

index 7e157236bf9f8e6971757aa2256307eda625f1fe..fda0121d632294ccde069e362a6d2af3a59d091b 100644 (file)
@@ -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("<div></div>", 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("<div> </div>", 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(" ")
index 5983bde67d14ba3f51face24b98c63214afada82..9257a714b13a7fc3ae62f8afebc191dcf8b46395 100644 (file)
@@ -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(`<div>{{ }}</div>`)
+    const { code: code4 } = compileWithExpression(`<div>{{ foo }}{{ }}</div>`)
+
+    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()
+  })
 })
index 5f858058f2799943c06bce549b9393d77ee05c30..e9c273b85c73f583a3ed0261e4f2a1e19722255a 100644 (file)
@@ -87,7 +87,8 @@ export const transformText: NodeTransform = (node, context) => {
 }
 
 function processInterpolation(context: TransformContext<InterpolationNode>) {
-  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<TextLike>
@@ -97,10 +98,18 @@ function processInterpolation(context: TransformContext<InterpolationNode>) {
   if (prev && prev.type === NodeTypes.TEXT) {
     nodes.unshift(prev)
   }
+  const values = processTextLikeChildren(nodes, context)
+
+  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 +138,10 @@ function processTextContainer(
   children: TextLike[],
   context: TransformContext<ElementNode>,
 ) {
-  const values = children.map(child => createTextLikeExpression(child, context))
+  const values = processTextLikeChildren(children, context)
+
   const literals = values.map(getLiteralExpressionValue)
+
   if (literals.every(l => l != null)) {
     context.childrenTemplate = literals.map(l => String(l))
   } else {
@@ -149,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 {