]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(vapor): refactor interpolation transform to make it hydration friendly
authorEvan You <evan@vuejs.org>
Fri, 7 Mar 2025 12:38:03 +0000 (20:38 +0800)
committerEvan You <evan@vuejs.org>
Fri, 7 Mar 2025 12:49:21 +0000 (20:49 +0800)
12 files changed:
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformText.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts
packages/compiler-vapor/__tests__/transforms/transformText.spec.ts
packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts
packages/compiler-vapor/src/generators/template.ts
packages/compiler-vapor/src/transforms/transformText.ts
packages/runtime-vapor/src/dom/template.ts

index 323b9df78447c4da3b49d223b86d763e50ce5466..04e7d793b8be5e5d174e61c8c6c2addab9df5718 100644 (file)
@@ -149,17 +149,17 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
 `;
 
 exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, createTextNode as _createTextNode, insert as _insert, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, child as _child, createComponentWithFallback as _createComponentWithFallback, prepend as _prepend, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
-const t1 = _template("<div></div>")
+const t1 = _template("<div> </div>")
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const _component_Comp = _resolveComponent("Comp")
   const n0 = t0()
   const n3 = t1()
+  const n2 = _child(n3)
   const n1 = _createComponentWithFallback(_component_Comp)
-  const n2 = _createTextNode()
-  _insert([n1, n2], n3)
+  _prepend(n3, n1)
   _renderEffect(() => {
     _setText(n2, _toDisplayString(_ctx.bar))
     _setProp(n3, "id", _ctx.foo)
@@ -169,10 +169,12 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
 `;
 
 exports[`compile > dynamic root 1`] = `
-"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString } from 'vue';
+"import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
-  const n0 = _createTextNode(_toDisplayString(1) + _toDisplayString(2))
+  const n0 = t0()
+  _setText(n0, _toDisplayString(1) + _toDisplayString(2))
   return n0
 }"
 `;
@@ -197,7 +199,7 @@ export function render(_ctx) {
 
 exports[`compile > expression parsing > interpolation 1`] = `
 "
-  const n0 = _createTextNode()
+  const n0 = t0()
   _renderEffect(() => _setText(n0, _toDisplayString(a + b.value)))
   return n0
 "
@@ -229,10 +231,12 @@ export function render(_ctx) {
 `;
 
 exports[`compile > static + dynamic root 1`] = `
-"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString } from 'vue';
+"import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
-  const n0 = _createTextNode(_toDisplayString(1) + _toDisplayString(2) + "3" + _toDisplayString(4) + _toDisplayString(5) + "6" + _toDisplayString(7) + _toDisplayString(8) + "9" + 'A' + 'B')
+  const n0 = t0()
+  _setText(n0, _toDisplayString(1) + _toDisplayString(2) + "3" + _toDisplayString(4) + _toDisplayString(5) + "6" + _toDisplayString(7) + _toDisplayString(8) + "9" + 'A' + 'B')
   return n0
 }"
 `;
index ec449cbd2d34eea4961a473d678819520499f1c4..518c2a5fe70ab860765c010af51dd5eb39020785 100644 (file)
@@ -1,30 +1,33 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: expression > basic 1`] = `
-"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
+"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
-  const n0 = _createTextNode()
+  const n0 = t0()
   _renderEffect(() => _setText(n0, _toDisplayString(_ctx.a)))
   return n0
 }"
 `;
 
 exports[`compiler: expression > props 1`] = `
-"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
+"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
-  const n0 = _createTextNode()
+  const n0 = t0()
   _renderEffect(() => _setText(n0, _toDisplayString($props.foo)))
   return n0
 }"
 `;
 
 exports[`compiler: expression > props aliased 1`] = `
-"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
+"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
-  const n0 = _createTextNode()
+  const n0 = t0()
   _renderEffect(() => _setText(n0, _toDisplayString($props['bar'])))
   return n0
 }"
index 215ab9036ecb0931e7c2d0b95dbde899d1175a98..5f0121b5e9173ff6495754915530587f3794977f 100644 (file)
@@ -1,24 +1,22 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: children transform > children & sibling references 1`] = `
-"import { child as _child, nthChild as _nthChild, next as _next, createTextNode as _createTextNode, insert as _insert, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
-const t0 = _template("<div><p> </p> <!><p> </p></div>", true)
+"import { child as _child, next as _next, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div><p> </p> <p> </p></div>", true)
 
 export function render(_ctx) {
-  const n4 = t0()
-  const n0 = _child(n4)
-  const n3 = _nthChild(n4, 2)
-  const n2 = _next(n3)
+  const n3 = t0()
+  const n0 = _child(n3)
+  const n1 = _next(n0)
+  const n2 = _next(n1)
   const x0 = _child(n0)
-  const n1 = _createTextNode()
   const x2 = _child(n2)
-  _insert(n1, n4, n3)
   _renderEffect(() => {
     _setText(x0, _toDisplayString(_ctx.first))
-    _setText(n1, _toDisplayString(_ctx.second) + " " + _toDisplayString(_ctx.third) + " ")
+    _setText(n1, " " + _toDisplayString(_ctx.second) + " " + _toDisplayString(_ctx.third) + " ")
     _setText(x2, _toDisplayString(_ctx.forth))
   })
-  return n4
+  return n3
 }"
 `;
 
index 706b44c9a3eda646fa48988f687f4f9a6625aa18..f18f8520353013ac714314de175386ae4af5e974 100644 (file)
@@ -1,20 +1,23 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: text transform > consecutive text 1`] = `
-"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
+"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
-  const n0 = _createTextNode()
+  const n0 = t0()
   _renderEffect(() => _setText(n0, _toDisplayString(_ctx.msg)))
   return n0
 }"
 `;
 
 exports[`compiler: text transform > no consecutive text 1`] = `
-"import { createTextNode as _createTextNode } from 'vue';
+"import { setText as _setText, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
-  const n0 = _createTextNode("hello world")
+  const n0 = t0()
+  _setText(n0, "hello world")
   return n0
 }"
 `;
index 9cbe1bd0d348d93d82bc6ab6fdbdf84798931d60..d7ec3ceed1edbad4a477d2fa859e439b01195664 100644 (file)
@@ -12,15 +12,15 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-once > basic 1`] = `
-"import { child as _child, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setClass as _setClass, prepend as _prepend, template as _template } from 'vue';
-const t0 = _template("<div><span></span></div>", true)
+"import { child as _child, next as _next, toDisplayString as _toDisplayString, setText as _setText, setClass as _setClass, template as _template } from 'vue';
+const t0 = _template("<div> <span></span></div>", true)
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n2 = t0()
-  const n1 = _child(n2)
-  const n0 = _createTextNode(_toDisplayString(_ctx.msg) + " ")
+  const n0 = _child(n2)
+  const n1 = _next(n0)
+  _setText(n0, _toDisplayString(_ctx.msg) + " ")
   _setClass(n1, _ctx.clz)
-  _prepend(n2, n0)
   return n2
 }"
 `;
index bab5c1046059564cdca1eb74996e7ce805f0bca8..6649e333bf1eb998ec6966b13c187379b5e6e83e 100644 (file)
@@ -22,7 +22,8 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
@@ -31,7 +32,7 @@ export function render(_ctx) {
       () => (_createForSlots(_ctx.list, (item) => ({
         name: item, 
         fn: (_slotProps0) => {
-          const n0 = _createTextNode()
+          const n0 = t0()
           _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["bar"])))
           return n0
         }
@@ -158,7 +159,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > nested slots scoping 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" ")
 
 export function render(_ctx) {
@@ -166,17 +167,16 @@ export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n5 = _createComponentWithFallback(_component_Comp, null, {
     "default": (_slotProps0) => {
-      const n2 = t0()
+      const n3 = t0()
       const n1 = _createComponentWithFallback(_component_Inner, null, {
         "default": (_slotProps1) => {
-          const n0 = _createTextNode()
+          const n0 = t0()
           _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz)))
           return n0
         }
       })
-      const n3 = _createTextNode()
-      _renderEffect(() => _setText(n3, _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz)))
-      return [n1, n2, n3]
+      _renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz)))
+      return [n1, n3]
     }
   }, true)
   return n5
@@ -184,7 +184,8 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > on component dynamically named slot 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
@@ -193,7 +194,7 @@ export function render(_ctx) {
       () => ({
         name: _ctx.named, 
         fn: (_slotProps0) => {
-          const n0 = _createTextNode()
+          const n0 = t0()
           _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
           return n0
         }
@@ -205,13 +206,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > on component named slot 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
     "named": (_slotProps0) => {
-      const n0 = _createTextNode()
+      const n0 = t0()
       _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
       return n0
     }
@@ -221,13 +223,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > on-component default slot 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+const t0 = _template(" ")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
     "default": (_slotProps0) => {
-      const n0 = _createTextNode()
+      const n0 = t0()
       _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
       return n0
     }
index 152911dcdfafbd9aa8f7d8faf8e53f096d562343..b94e555351833d593e366e696a949aae4638a655 100644 (file)
@@ -27,10 +27,11 @@ describe('compiler: children transform', () => {
     )
     expect(code).toMatchSnapshot()
     expect(Array.from(helpers)).containSubset([
+      'child',
+      'toDisplayString',
+      'renderEffect',
       'next',
       'setText',
-      'createTextNode',
-      'insert',
       'template',
     ])
   })
index a668c76258ff49d55788617e0997a2f18a00c637..20fa6d1fd008861d359be28b9fe1f0097af5cdb9 100644 (file)
@@ -1,4 +1,5 @@
 // TODO: add tests for this transform
+import { NodeTypes } from '@vue/compiler-dom'
 import {
   IRNodeTypes,
   transformChildren,
@@ -24,14 +25,14 @@ describe('compiler: text transform', () => {
       '{{ "hello world" }}',
     )
     expect(code).toMatchSnapshot()
-    expect(helpers).contains.all.keys('createTextNode')
+    expect(helpers).contains.all.keys('setText', 'template')
     expect(ir.block.operation).toMatchObject([
       {
-        type: IRNodeTypes.CREATE_TEXT_NODE,
-        id: 0,
+        type: IRNodeTypes.SET_TEXT,
+        element: 0,
         values: [
           {
-            type: IRNodeTypes.SET_TEXT,
+            type: NodeTypes.SIMPLE_EXPRESSION,
             content: '"hello world"',
             isStatic: false,
           },
@@ -43,14 +44,8 @@ describe('compiler: text transform', () => {
   it('consecutive text', () => {
     const { code, ir, helpers } = compileWithTextTransform('{{ msg }}')
     expect(code).toMatchSnapshot()
-    expect(helpers).contains.all.keys('createTextNode')
-    expect(ir.block.operation).toMatchObject([
-      {
-        type: IRNodeTypes.CREATE_TEXT_NODE,
-        id: 0,
-        values: undefined,
-      },
-    ])
+    expect(helpers).contains.all.keys('setText', 'template')
+    expect(ir.block.operation).toMatchObject([])
     expect(ir.block.effect.length).toBe(1)
   })
 })
index 228350486bffa3e8cd64145e81c2a1147408df37..a3fb18aa1cb1e9224db2a0b8be01a72aed9bd581 100644 (file)
@@ -28,8 +28,8 @@ describe('compiler: v-once', () => {
     expect(ir.block.effect).lengthOf(0)
     expect(ir.block.operation).toMatchObject([
       {
-        type: IRNodeTypes.CREATE_TEXT_NODE,
-        id: 0,
+        type: IRNodeTypes.SET_TEXT,
+        element: 0,
         values: [
           {
             type: NodeTypes.SIMPLE_EXPRESSION,
@@ -61,11 +61,6 @@ describe('compiler: v-once', () => {
           ],
         },
       },
-      {
-        type: IRNodeTypes.PREPEND_NODE,
-        elements: [0],
-        parent: 2,
-      },
     ])
   })
 
index 0c27a919246dccaa755fdbd7e6ceffdc0309497e..90694447983faab2814268c4f9ac5c90b8edb018 100644 (file)
@@ -66,11 +66,10 @@ export function genChildren(
     push(NEWLINE, `const ${variable} = `)
 
     if (prev) {
-      const offset = elementIndex - prev[1]
-      if (offset === 1) {
+      if (elementIndex - prev[1] === 1) {
         push(...genCall(helper('next'), prev[0]))
       } else {
-        push(...genCall(helper('nthChild'), from, String(offset)))
+        push(...genCall(helper('nthChild'), from, String(elementIndex)))
       }
     } else {
       if (newPath.length === 1 && newPath[0] === 0) {
index 12f072818d1315ac86f0ca544bd27f7f37db018c..7cdb1b1a78c4abbf456e22bd5e56453edced82f3 100644 (file)
@@ -30,32 +30,71 @@ export const transformText: NodeTransform = (node, context) => {
     return
   }
 
+  const isFragment =
+    node.type === NodeTypes.ROOT ||
+    (node.type === NodeTypes.ELEMENT &&
+      (node.tagType === ElementTypes.TEMPLATE ||
+        node.tagType === ElementTypes.COMPONENT))
+
   if (
-    node.type === NodeTypes.ELEMENT &&
-    node.tagType === ElementTypes.ELEMENT &&
-    isAllTextLike(node.children)
+    (isFragment ||
+      (node.type === NodeTypes.ELEMENT &&
+        node.tagType === ElementTypes.ELEMENT)) &&
+    node.children.length
   ) {
-    processTextLikeContainer(
-      node.children,
-      context as TransformContext<ElementNode>,
-    )
+    let hasInterp = false
+    let isAllTextLike = true
+    for (const c of node.children) {
+      if (c.type === NodeTypes.INTERPOLATION) {
+        hasInterp = true
+      } else if (c.type !== NodeTypes.TEXT) {
+        isAllTextLike = false
+      }
+    }
+    // all text like with interpolation
+    if (!isFragment && isAllTextLike && hasInterp) {
+      processTextContainer(
+        node.children as TextLike[],
+        context as TransformContext<ElementNode>,
+      )
+    } else if (hasInterp) {
+      // check if there's any text before interpolation, it needs to be merged
+      for (let i = 0; i < node.children.length; i++) {
+        const c = node.children[i]
+        const prev = node.children[i - 1]
+        if (
+          c.type === NodeTypes.INTERPOLATION &&
+          prev &&
+          prev.type === NodeTypes.TEXT
+        ) {
+          // mark leading text node for skipping
+          seen.get(context.root)!.add(prev)
+        }
+      }
+    }
   } else if (node.type === NodeTypes.INTERPOLATION) {
-    processTextLike(context as TransformContext<InterpolationNode>)
+    processInterpolation(context as TransformContext<InterpolationNode>)
   } else if (node.type === NodeTypes.TEXT) {
     context.template += node.content
   }
 }
 
-function processTextLike(context: TransformContext<InterpolationNode>) {
-  const nexts = context.parent!.node.children.slice(context.index)
+function processInterpolation(context: TransformContext<InterpolationNode>) {
+  const children = context.parent!.node.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>
 
+  // merge leading text
+  const prev = children[context.index - 1]
+  if (prev && prev.type === NodeTypes.TEXT) {
+    nodes.unshift(prev)
+  }
+
+  context.template += ' '
   const id = context.reference()
   const values = nodes.map(node => createTextLikeExpression(node, context))
 
-  context.dynamic.flags |= DynamicFlag.INSERT | DynamicFlag.NON_TEMPLATE
-
   const nonConstantExps = values.filter(v => !isConstantExpression(v))
   const isStatic =
     !nonConstantExps.length ||
@@ -64,12 +103,13 @@ function processTextLike(context: TransformContext<InterpolationNode>) {
     ) ||
     context.inVOnce
 
-  context.registerOperation({
-    type: IRNodeTypes.CREATE_TEXT_NODE,
-    id,
-    values: isStatic ? values : undefined,
-  })
-  if (!isStatic) {
+  if (isStatic) {
+    context.registerOperation({
+      type: IRNodeTypes.SET_TEXT,
+      element: id,
+      values,
+    })
+  } else {
     context.registerEffect(values, {
       type: IRNodeTypes.SET_TEXT,
       element: id,
@@ -78,7 +118,7 @@ function processTextLike(context: TransformContext<InterpolationNode>) {
   }
 }
 
-function processTextLikeContainer(
+function processTextContainer(
   children: TextLike[],
   context: TransformContext<ElementNode>,
 ) {
@@ -111,15 +151,6 @@ function createTextLikeExpression(node: TextLike, context: TransformContext) {
   }
 }
 
-function isAllTextLike(children: TemplateChildNode[]): children is TextLike[] {
-  return (
-    !!children.length &&
-    children.every(isTextLike) &&
-    // at least one an interpolation
-    children.some(n => n.type === NodeTypes.INTERPOLATION)
-  )
-}
-
 function isTextLike(node: TemplateChildNode): node is TextLike {
   return node.type === NodeTypes.INTERPOLATION || node.type === NodeTypes.TEXT
 }
index 8069301d2ef464306a964a6368b42aa72acbc754..dbe83364150ea1608978192feef356ab33ea8758 100644 (file)
@@ -3,7 +3,7 @@ import {
   currentHydrationNode,
   isHydrating,
 } from './hydration'
-import { child } from './node'
+import { child, createTextNode } from './node'
 
 let t: HTMLTemplateElement
 
@@ -18,6 +18,10 @@ export function template(html: string, root?: boolean) {
       }
       return adoptHydrationNode(currentHydrationNode, html)!
     }
+    // fast path for text nodes
+    if (html[0] !== '<') {
+      return createTextNode(html)
+    }
     if (!node) {
       t = t || document.createElement('template')
       t.innerHTML = html