]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: vapor hydration (#13850)
authoredison <daiwei521@126.com>
Mon, 8 Sep 2025 06:57:35 +0000 (14:57 +0800)
committerGitHub <noreply@github.com>
Mon, 8 Sep 2025 06:57:35 +0000 (14:57 +0800)
38 files changed:
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/options.ts
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/vSlot.ts
packages/compiler-sfc/src/compileTemplate.ts
packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts [deleted file]
packages/compiler-ssr/src/ssrCodegenTransform.ts
packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts
packages/compiler-ssr/src/transforms/ssrVFor.ts
packages/compiler-ssr/src/transforms/ssrVIf.ts
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-vapor/__tests__/compile.spec.ts
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/generators/block.ts
packages/compiler-vapor/src/generators/operation.ts
packages/compiler-vapor/src/generators/template.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transforms/transformChildren.ts
packages/runtime-vapor/__tests__/hydration.spec.ts
packages/runtime-vapor/src/apiCreateDynamicComponent.ts
packages/runtime-vapor/src/apiCreateFor.ts
packages/runtime-vapor/src/apiCreateIf.ts
packages/runtime-vapor/src/block.ts
packages/runtime-vapor/src/componentSlots.ts
packages/runtime-vapor/src/dom/hydration.ts
packages/runtime-vapor/src/dom/node.ts
packages/runtime-vapor/src/dom/template.ts
packages/runtime-vapor/src/fragment.ts
packages/runtime-vapor/src/insertionState.ts
packages/runtime-vapor/src/vdomInterop.ts
packages/server-renderer/src/helpers/ssrRenderSlot.ts
packages/server-renderer/src/render.ts
packages/shared/src/domAnchors.ts [deleted file]
packages/shared/src/index.ts

index 4b3d16579c614d380a6116b1b0e81f415973d046..bae13372a98be3d205d15cc1c045edd0b0220197 100644 (file)
@@ -163,7 +163,6 @@ export interface ComponentNode extends BaseElementNode {
     | MemoExpression // when cached by v-memo
     | undefined
   ssrCodegenNode?: CallExpression
-  needAnchor?: boolean
 }
 
 export interface SlotOutletNode extends BaseElementNode {
@@ -173,14 +172,12 @@ export interface SlotOutletNode extends BaseElementNode {
     | CacheExpression // when cached by v-once
     | undefined
   ssrCodegenNode?: CallExpression
-  needAnchor?: boolean
 }
 
 export interface TemplateNode extends BaseElementNode {
   tagType: ElementTypes.TEMPLATE
   // TemplateNode is a container type that always gets compiled away
   codegenNode: undefined
-  needAnchor?: boolean
 }
 
 export interface TextNode extends Node {
@@ -290,7 +287,6 @@ export interface IfNode extends Node {
   type: NodeTypes.IF
   branches: IfBranchNode[]
   codegenNode?: IfConditionalExpression | CacheExpression // <div v-if v-once>
-  needAnchor?: boolean
 }
 
 export interface IfBranchNode extends Node {
@@ -310,7 +306,6 @@ export interface ForNode extends Node {
   parseResult: ForParseResult
   children: TemplateChildNode[]
   codegenNode?: ForCodegenNode
-  needAnchor?: boolean
 }
 
 export interface ForParseResult {
index 0b5dcca8f70a980d751394c07608a12d42793929..99020bcf1ae1f93eda6ab6a7f458cd539293b701 100644 (file)
@@ -167,7 +167,6 @@ function createCodegenContext(
     ssr = false,
     isTS = false,
     inSSR = false,
-    vapor = false,
   }: CodegenOptions,
 ): CodegenContext {
   const context: CodegenContext = {
@@ -183,7 +182,6 @@ function createCodegenContext(
     ssr,
     isTS,
     inSSR,
-    vapor,
     source: ast.source,
     code: ``,
     column: 1,
index 03a32e0113cf20a41d3bcfebaebc2ce935e72c8f..9983071609eaad3d1337543438771eed7a8d7c61 100644 (file)
@@ -220,11 +220,6 @@ interface SharedTransformCodegenOptions {
    * @default 'template.vue.html'
    */
   filename?: string
-
-  /**
-   * Indicates vapor component
-   */
-  vapor?: boolean
 }
 
 export interface TransformOptions
index 11077df6252b7f4a976020285777f013cec50b93..10121fb5d5cb1fcfdb36e04d2371fea430bcd0ec 100644 (file)
@@ -146,7 +146,6 @@ export function createTransformContext(
     slotted = true,
     ssr = false,
     inSSR = false,
-    vapor = false,
     ssrCssVars = ``,
     bindingMetadata = EMPTY_OBJ,
     inline = false,
@@ -174,7 +173,6 @@ export function createTransformContext(
     slotted,
     ssr,
     inSSR,
-    vapor,
     ssrCssVars,
     bindingMetadata,
     inline,
index da29d0de8977c343b1009fd4155299ff494db58d..43296dcc9b67a606c4cfafb4309a28bf41b5d1a3 100644 (file)
@@ -100,7 +100,6 @@ export type SlotFnBuilder = (
   vFor: DirectiveNode | undefined,
   slotChildren: TemplateChildNode[],
   loc: SourceLocation,
-  parent: ElementNode,
 ) => FunctionExpression
 
 const buildClientSlotFn: SlotFnBuilder = (props, _vForExp, children, loc) =>
@@ -148,7 +147,7 @@ export function buildSlots(
     slotsProperties.push(
       createObjectProperty(
         arg || createSimpleExpression('default', true),
-        buildSlotFn(exp, undefined, children, loc, node),
+        buildSlotFn(exp, undefined, children, loc),
       ),
     )
   }
@@ -201,13 +200,7 @@ export function buildSlots(
     }
 
     const vFor = findDir(slotElement, 'for')
-    const slotFunction = buildSlotFn(
-      slotProps,
-      vFor,
-      slotChildren,
-      slotLoc,
-      slotElement,
-    )
+    const slotFunction = buildSlotFn(slotProps, vFor, slotChildren, slotLoc)
 
     // check if this slot is conditional (v-if/v-for)
     let vIf: DirectiveNode | undefined
@@ -311,7 +304,7 @@ export function buildSlots(
       props: ExpressionNode | undefined,
       children: TemplateChildNode[],
     ) => {
-      const fn = buildSlotFn(props, undefined, children, loc, node)
+      const fn = buildSlotFn(props, undefined, children, loc)
       if (__COMPAT__ && context.compatConfig) {
         fn.isNonScopedSlot = true
       }
index 1d832388fe1a3fc3a286eab69294842edb906cdc..29d1853d2d6233836ade13f34040de58ccf47fb8 100644 (file)
@@ -253,7 +253,6 @@ function doCompileTemplate({
       slotted,
       sourceMap: true,
       ...compilerOptions,
-      vapor,
       hmr: !isProd,
       nodeTransforms: nodeTransforms.concat(
         compilerOptions.nodeTransforms || [],
diff --git a/packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts b/packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts
deleted file mode 100644 (file)
index 321dce5..0000000
+++ /dev/null
@@ -1,828 +0,0 @@
-import { getCompiledString } from './utils'
-
-describe('block anchors', () => {
-  describe('prepend', () => {
-    test('prepend anchor with component', () => {
-      expect(
-        getCompiledString('<div><Comp/><Comp/><span/></div>', { vapor: true }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with component in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <Comp/><Comp/><span/>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-                _push(\`<!--]]--><!--[[-->\`)
-                _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-                _push(\`<!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    _createVNode(_component_Comp),
-                    _createCommentVNode("]]"),
-                    _createCommentVNode("[["),
-                    _createVNode(_component_Comp),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with slot', () => {
-      expect(
-        getCompiledString('<div><slot name="foo"/><slot/><span/></div>', {
-          vapor: true,
-        }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent)
-          _push(\`<!--slot--><!--]]--><!--[[-->\`)
-          _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent)
-          _push(\`<!--slot--><!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with slot in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <slot name="foo"/>
-              <slot/>
-              <span/>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent, _scopeId)
-                _push(\`<!--slot--><!--]]--><!--[[-->\`)
-                _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)
-                _push(\`<!--slot--><!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    _renderSlot(_ctx.$slots, "foo"),
-                    _createCommentVNode("slot"),
-                    _createCommentVNode("]]"),
-                    _createCommentVNode("[["),
-                    _renderSlot(_ctx.$slots, "default"),
-                    _createCommentVNode("slot"),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 3 /* FORWARDED */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with v-if/else-if/else', () => {
-      expect(
-        getCompiledString(
-          `<div>
-            <span v-if="foo"/>
-            <span v-else-if="bar"/>
-            <span v-else/>
-            <span/>
-          </div>`,
-          {
-            vapor: true,
-          },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          if (_ctx.foo) {
-            _push(\`<span></span>\`)
-            _push(\`<!--if-->\`)
-          } else if (_ctx.bar) {
-            _push(\`<span></span>\`)
-            _push(\`<!--if--><!--if-->\`)
-          } else {
-            _push(\`<span></span>\`)
-            _push(\`<!--if--><!--if-->\`)
-          }
-          _push(\`<!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with v-if/else-if/else in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <span v-if="foo"/>
-              <span v-else-if="bar"/>
-              <span v-else/>
-              <span/>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                if (_ctx.foo) {
-                  _push(\`<span\${_scopeId}></span>\`)
-                  _push(\`<!--if-->\`)
-                } else if (_ctx.bar) {
-                  _push(\`<span\${_scopeId}></span>\`)
-                  _push(\`<!--if--><!--if-->\`)
-                } else {
-                  _push(\`<span\${_scopeId}></span>\`)
-                  _push(\`<!--if--><!--if-->\`)
-                }
-                _push(\`<!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    (_ctx.foo)
-                      ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                          _createVNode("span"),
-                          _createCommentVNode("<!--if-->")
-                        ], 64 /* STABLE_FRAGMENT */))
-                      : (_ctx.bar)
-                        ? (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
-                            _createVNode("span"),
-                            _createCommentVNode("<!--if--><!--if-->")
-                          ], 64 /* STABLE_FRAGMENT */))
-                        : (_openBlock(), _createBlock(_Fragment, { key: 2 }, [
-                            _createVNode("span"),
-                            _createCommentVNode("<!--if--><!--if-->")
-                          ], 64 /* STABLE_FRAGMENT */)),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with nested v-if', () => {
-      expect(
-        getCompiledString(
-          `<div>
-            <span v-if="foo">
-              <span v-if="foo1" />
-              <span />
-            </span>
-            <span v-else-if="bar">
-              <span v-if="bar1" />
-              <span />
-            </span>
-            <span v-else>
-              <span v-if="bar2" />
-              <span />
-            </span>
-            <span />
-          </div>`,
-          {
-            vapor: true,
-          },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          if (_ctx.foo) {
-            _push(\`<span><!--[[-->\`)
-            if (_ctx.foo1) {
-              _push(\`<span></span>\`)
-              _push(\`<!--if-->\`)
-            } else {
-              _push(\`<!--if-->\`)
-            }
-            _push(\`<!--]]--><span></span></span>\`)
-            _push(\`<!--if-->\`)
-          } else if (_ctx.bar) {
-            _push(\`<span><!--[[-->\`)
-            if (_ctx.bar1) {
-              _push(\`<span></span>\`)
-              _push(\`<!--if-->\`)
-            } else {
-              _push(\`<!--if-->\`)
-            }
-            _push(\`<!--]]--><span></span></span>\`)
-            _push(\`<!--if--><!--if-->\`)
-          } else {
-            _push(\`<span><!--[[-->\`)
-            if (_ctx.bar2) {
-              _push(\`<span></span>\`)
-              _push(\`<!--if-->\`)
-            } else {
-              _push(\`<!--if-->\`)
-            }
-            _push(\`<!--]]--><span></span></span>\`)
-            _push(\`<!--if--><!--if-->\`)
-          }
-          _push(\`<!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with nested v-if in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <span v-if="foo">
-                <span v-if="foo1" />
-                <span />
-              </span>
-              <span v-else-if="bar">
-                <span v-if="bar1" />
-                <span />
-              </span>
-              <span v-else>
-                <span v-if="bar2" />
-                <span />
-              </span>
-              <span />
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                if (_ctx.foo) {
-                  _push(\`<span\${_scopeId}><!--[[-->\`)
-                  if (_ctx.foo1) {
-                    _push(\`<span\${_scopeId}></span>\`)
-                    _push(\`<!--if-->\`)
-                  } else {
-                    _push(\`<!--if-->\`)
-                  }
-                  _push(\`<!--]]--><span\${_scopeId}></span></span>\`)
-                  _push(\`<!--if-->\`)
-                } else if (_ctx.bar) {
-                  _push(\`<span\${_scopeId}><!--[[-->\`)
-                  if (_ctx.bar1) {
-                    _push(\`<span\${_scopeId}></span>\`)
-                    _push(\`<!--if-->\`)
-                  } else {
-                    _push(\`<!--if-->\`)
-                  }
-                  _push(\`<!--]]--><span\${_scopeId}></span></span>\`)
-                  _push(\`<!--if--><!--if-->\`)
-                } else {
-                  _push(\`<span\${_scopeId}><!--[[-->\`)
-                  if (_ctx.bar2) {
-                    _push(\`<span\${_scopeId}></span>\`)
-                    _push(\`<!--if-->\`)
-                  } else {
-                    _push(\`<!--if-->\`)
-                  }
-                  _push(\`<!--]]--><span\${_scopeId}></span></span>\`)
-                  _push(\`<!--if--><!--if-->\`)
-                }
-                _push(\`<!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    (_ctx.foo)
-                      ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                          _createVNode("span", null, [
-                            _createCommentVNode("[["),
-                            (_ctx.foo1)
-                              ? (_openBlock(), _createBlock("span", { key: 0 }))
-                              : _createCommentVNode("v-if", true),
-                            _createCommentVNode("if"),
-                            _createCommentVNode("]]"),
-                            _createVNode("span")
-                          ]),
-                          _createCommentVNode("<!--if-->")
-                        ], 64 /* STABLE_FRAGMENT */))
-                      : (_ctx.bar)
-                        ? (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
-                            _createVNode("span", null, [
-                              _createCommentVNode("[["),
-                              (_ctx.bar1)
-                                ? (_openBlock(), _createBlock("span", { key: 0 }))
-                                : _createCommentVNode("v-if", true),
-                              _createCommentVNode("if"),
-                              _createCommentVNode("]]"),
-                              _createVNode("span")
-                            ]),
-                            _createCommentVNode("<!--if--><!--if-->")
-                          ], 64 /* STABLE_FRAGMENT */))
-                        : (_openBlock(), _createBlock(_Fragment, { key: 2 }, [
-                            _createVNode("span", null, [
-                              _createCommentVNode("[["),
-                              (_ctx.bar2)
-                                ? (_openBlock(), _createBlock("span", { key: 0 }))
-                                : _createCommentVNode("v-if", true),
-                              _createCommentVNode("if"),
-                              _createCommentVNode("]]"),
-                              _createVNode("span")
-                            ]),
-                            _createCommentVNode("<!--if--><!--if-->")
-                          ], 64 /* STABLE_FRAGMENT */)),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with template v-if', () => {
-      expect(
-        getCompiledString(
-          `<component :is="tag">
-            <div v-if="foo">
-              <template v-if="depth < 5">
-                foo
-              </template>
-              <div></div>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                if (_ctx.foo) {
-                  _push(\`<div\${_scopeId}><!--[[-->\`)
-                  if (_ctx.depth < 5) {
-                    _push(\` foo \`)
-                    _push(\`<!--if-->\`)
-                  } else {
-                    _push(\`<!--if-->\`)
-                  }
-                  _push(\`<!--]]--><div\${_scopeId}></div></div>\`)
-                  _push(\`<!--if-->\`)
-                } else {
-                  _push(\`<!--if-->\`)
-                }
-              } else {
-                return [
-                  (_ctx.foo)
-                    ? (_openBlock(), _createBlock("div", { key: 0 }, [
-                        _createCommentVNode("[["),
-                        (_ctx.depth < 5)
-                          ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                              _createTextVNode(" foo ")
-                            ], 64 /* STABLE_FRAGMENT */))
-                          : _createCommentVNode("v-if", true),
-                        _createCommentVNode("if"),
-                        _createCommentVNode("]]"),
-                        _createVNode("div")
-                      ]))
-                    : _createCommentVNode("v-if", true),
-                  _createCommentVNode("if")
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with v-for', () => {
-      expect(
-        getCompiledString('<div><span v-for="item in items"/><span/></div>', {
-          vapor: true,
-        }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          _ssrRenderList(_ctx.items, (item) => {
-            _push(\`<span></span>\`)
-          })
-          _push(\`<!--for--><!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with v-for in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <span v-for="item in items"/><span/>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                _ssrRenderList(_ctx.items, (item) => {
-                  _push(\`<span\${_scopeId}></span>\`)
-                })
-                _push(\`<!--for--><!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.items, (item) => {
-                      return (_openBlock(), _createBlock("span"))
-                    }), 256 /* UNKEYED_FRAGMENT */)),
-                    _createCommentVNode("for"),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-  })
-
-  // TODO add more tests
-  describe('insert', () => {
-    test('insertion anchor with component', () => {
-      expect(
-        getCompiledString('<div><span/><Comp/><span/></div>', { vapor: true }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><span></span><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--><span></span></div>\`"
-      `)
-    })
-  })
-
-  // TODO add more tests
-  describe('append', () => {
-    test('append anchor', () => {
-      expect(
-        getCompiledString('<div><span/><Comp/><Comp/></div>', { vapor: true }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><span></span><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--></div>\`"
-      `)
-    })
-  })
-
-  test('mixed anchors', () => {
-    expect(
-      getCompiledString('<div><Comp/><span/><Comp/><span/><Comp/></div>', {
-        vapor: true,
-      }),
-    ).toMatchInlineSnapshot(`
-      "\`<div><!--[[-->\`)
-        _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-        _push(\`<!--]]--><span></span><!--[[-->\`)
-        _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-        _push(\`<!--]]--><span></span><!--[[-->\`)
-        _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-        _push(\`<!--]]--></div>\`"
-    `)
-  })
-
-  test('mixed anchors in ssr slot vnode fallback', () => {
-    expect(
-      getCompiledString(
-        `<component :is="'div'">
-          <div>
-            <Comp/><span/>
-            <Comp/><span/>
-            <Comp/>
-          </div>
-        </component>`,
-        {
-          vapor: true,
-        },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              _push(\`<div\${_scopeId}><!--[[-->\`)
-              _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-              _push(\`<!--]]--><span\${_scopeId}></span><!--[[-->\`)
-              _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-              _push(\`<!--]]--><span\${_scopeId}></span><!--[[-->\`)
-              _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-              _push(\`<!--]]--></div>\`)
-            } else {
-              return [
-                _createVNode("div", null, [
-                  _createCommentVNode("[["),
-                  _createVNode(_component_Comp),
-                  _createCommentVNode("]]"),
-                  _createVNode("span"),
-                  _createCommentVNode("[["),
-                  _createVNode(_component_Comp),
-                  _createCommentVNode("]]"),
-                  _createVNode("span"),
-                  _createCommentVNode("[["),
-                  _createVNode(_component_Comp),
-                  _createCommentVNode("]]")
-                ])
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-})
-
-describe('fragment anchors', () => {
-  test('if', () => {
-    expect(
-      getCompiledString(
-        `<component :is="tag">
-          <span v-if="count === 1">1</span>
-          <span v-else-if="count === 2">2</span>
-          <span v-else-if="count === 3">3</span>
-          <span v-else>4</span>
-        </component>`,
-        {
-          vapor: true,
-        },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              if (_ctx.count === 1) {
-                _push(\`<span\${_scopeId}>1</span>\`)
-                _push(\`<!--if-->\`)
-              } else if (_ctx.count === 2) {
-                _push(\`<span\${_scopeId}>2</span>\`)
-                _push(\`<!--if--><!--if-->\`)
-              } else if (_ctx.count === 3) {
-                _push(\`<span\${_scopeId}>3</span>\`)
-                _push(\`<!--if--><!--if--><!--if-->\`)
-              } else {
-                _push(\`<span\${_scopeId}>4</span>\`)
-                _push(\`<!--if--><!--if--><!--if-->\`)
-              }
-            } else {
-              return [
-                (_ctx.count === 1)
-                  ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                      _createVNode("span", null, "1"),
-                      _createCommentVNode("<!--if-->")
-                    ], 64 /* STABLE_FRAGMENT */))
-                  : (_ctx.count === 2)
-                    ? (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
-                        _createVNode("span", null, "2"),
-                        _createCommentVNode("<!--if--><!--if-->")
-                      ], 64 /* STABLE_FRAGMENT */))
-                    : (_ctx.count === 3)
-                      ? (_openBlock(), _createBlock(_Fragment, { key: 2 }, [
-                          _createVNode("span", null, "3"),
-                          _createCommentVNode("<!--if--><!--if--><!--if-->")
-                        ], 64 /* STABLE_FRAGMENT */))
-                      : (_openBlock(), _createBlock(_Fragment, { key: 3 }, [
-                          _createVNode("span", null, "4"),
-                          _createCommentVNode("<!--if--><!--if--><!--if-->")
-                        ], 64 /* STABLE_FRAGMENT */))
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-
-  test('if + v-html/v-text', () => {
-    expect(
-      getCompiledString(
-        `<component :is="tag">
-          <span v-if="count === 1" v-html="html"></span>
-          <span v-else-if="count === 2" v-text="txt"></span>
-          <span v-else>4</span>
-        </component>`,
-        {
-          vapor: true,
-        },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              if (_ctx.count === 1) {
-                _push(\`<span\${
-                  _scopeId
-                }>\${
-                  (_ctx.html) ?? ''
-                }</span>\`)
-                _push(\`<!--if-->\`)
-              } else if (_ctx.count === 2) {
-                _push(\`<span\${
-                  _scopeId
-                }>\${
-                  _ssrInterpolate(_ctx.txt)
-                }</span>\`)
-                _push(\`<!--if--><!--if-->\`)
-              } else {
-                _push(\`<span\${_scopeId}>4</span>\`)
-                _push(\`<!--if--><!--if-->\`)
-              }
-            } else {
-              return [
-                (_ctx.count === 1)
-                  ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                      _createVNode("span", { innerHTML: _ctx.html }, null, 8 /* PROPS */, ["innerHTML"]),
-                      _createCommentVNode("<!--if-->")
-                    ], 64 /* STABLE_FRAGMENT */))
-                  : (_ctx.count === 2)
-                    ? (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
-                        _createVNode("span", {
-                          textContent: _toDisplayString(_ctx.txt)
-                        }, null, 8 /* PROPS */, ["textContent"]),
-                        _createCommentVNode("<!--if--><!--if-->")
-                      ], 64 /* STABLE_FRAGMENT */))
-                    : (_openBlock(), _createBlock(_Fragment, { key: 2 }, [
-                        _createVNode("span", null, "4"),
-                        _createCommentVNode("<!--if--><!--if-->")
-                      ], 64 /* STABLE_FRAGMENT */))
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-
-  test('for', () => {
-    expect(
-      getCompiledString(
-        `<component :is="tag">
-          <span v-for="item in items">{{item}}</span>
-        </component>`,
-        {
-          vapor: true,
-        },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              _ssrRenderList(_ctx.items, (item) => {
-                _push(\`<span\${
-                  _scopeId
-                }>\${
-                  _ssrInterpolate(item)
-                }</span>\`)
-              })
-              _push(\`<!--for-->\`)
-            } else {
-              return [
-                (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.items, (item) => {
-                  return (_openBlock(), _createBlock("span", null, _toDisplayString(item), 1 /* TEXT */))
-                }), 256 /* UNKEYED_FRAGMENT */)),
-                _createCommentVNode("for")
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-
-  test('slot', () => {
-    expect(
-      getCompiledString(
-        `<div>
-          <slot name="foo"/>
-          <slot/>
-        </div>`,
-        { vapor: true },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`<div><!--[[-->\`)
-        _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent)
-        _push(\`<!--slot--><!--]]--><!--[[-->\`)
-        _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent)
-        _push(\`<!--slot--><!--]]--></div>\`"
-    `)
-  })
-
-  test('forwarded slot', () => {
-    expect(
-      getCompiledString(
-        `<component :is="tag">
-          <slot name="foo"/>
-          <slot/>
-        </component>`,
-        { vapor: true },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent, _scopeId)
-              _push(\`<!--slot-->\`)
-              _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)
-              _push(\`<!--slot-->\`)
-            } else {
-              return [
-                _renderSlot(_ctx.$slots, "foo"),
-                _createCommentVNode("slot"),
-                _renderSlot(_ctx.$slots, "default"),
-                _createCommentVNode("slot")
-              ]
-            }
-          }),
-          _: 3 /* FORWARDED */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-
-  test('dynamic component', () => {
-    expect(
-      getCompiledString(
-        `<component is='tag'>
-          <div>
-            <component is="foo"/>
-          </div>
-        </component>`,
-        { vapor: true },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent("tag"), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              _push(\`<div\${_scopeId}>\`)
-              _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent("foo"), null, null), _parent, _scopeId)
-              _push(\`<!--dynamic-component--></div>\`)
-            } else {
-              return [
-                _createVNode("div", null, [
-                  (_openBlock(), _createBlock(_resolveDynamicComponent("foo"))),
-                  _createCommentVNode("dynamic-component")
-                ])
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-})
index d9e48c47d04c5c5e1b6b180ed1c8024ca579d529..fe5b1ee267e281e4e4e878fa70c45871665a56f7 100644 (file)
@@ -1,16 +1,13 @@
 import {
-  type AttributeNode,
   type BlockStatement,
   type CallExpression,
   type CompilerError,
   type CompilerOptions,
-  type DirectiveNode,
   type ElementNode,
   ElementTypes,
   type IfStatement,
   type JSChildNode,
   NodeTypes,
-  type PlainElementNode,
   type RootNode,
   type TemplateChildNode,
   type TemplateLiteral,
@@ -24,12 +21,7 @@ import {
   isText,
   processExpression,
 } from '@vue/compiler-dom'
-import {
-  BLOCK_ANCHOR_END_LABEL,
-  BLOCK_ANCHOR_START_LABEL,
-  escapeHtml,
-  isString,
-} from '@vue/shared'
+import { escapeHtml, isString } from '@vue/shared'
 import { SSR_INTERPOLATE, ssrHelpers } from './runtimeHelpers'
 import { ssrProcessIf } from './transforms/ssrVIf'
 import { ssrProcessFor } from './transforms/ssrVFor'
@@ -167,17 +159,12 @@ export function processChildren(
   disableNestedFragments = false,
   disableComment = false,
 ): void {
-  const vapor = context.options.vapor
-  if (asFragment && !vapor) {
+  if (asFragment) {
     context.pushStringPart(`<!--[-->`)
   }
 
   const { children } = parent
 
-  if (vapor && isElementWithChildren(parent as PlainElementNode)) {
-    processBlockNodeAnchor(children)
-  }
-
   for (let i = 0; i < children.length; i++) {
     const child = children[i]
     switch (child.type) {
@@ -187,19 +174,11 @@ export function processChildren(
             ssrProcessElement(child, context)
             break
           case ElementTypes.COMPONENT:
-            if (child.needAnchor)
-              context.pushStringPart(`<!--${BLOCK_ANCHOR_START_LABEL}-->`)
             ssrProcessComponent(child, context, parent)
-            if (child.needAnchor)
-              context.pushStringPart(`<!--${BLOCK_ANCHOR_END_LABEL}-->`)
 
             break
           case ElementTypes.SLOT:
-            if (child.needAnchor)
-              context.pushStringPart(`<!--${BLOCK_ANCHOR_START_LABEL}-->`)
             ssrProcessSlotOutlet(child, context)
-            if (child.needAnchor)
-              context.pushStringPart(`<!--${BLOCK_ANCHOR_END_LABEL}-->`)
             break
           case ElementTypes.TEMPLATE:
             // TODO
@@ -234,18 +213,10 @@ export function processChildren(
         )
         break
       case NodeTypes.IF:
-        if (child.needAnchor)
-          context.pushStringPart(`<!--${BLOCK_ANCHOR_START_LABEL}-->`)
         ssrProcessIf(child, context, disableNestedFragments, disableComment)
-        if (child.needAnchor)
-          context.pushStringPart(`<!--${BLOCK_ANCHOR_END_LABEL}-->`)
         break
       case NodeTypes.FOR:
-        if (child.needAnchor)
-          context.pushStringPart(`<!--${BLOCK_ANCHOR_START_LABEL}-->`)
         ssrProcessFor(child, context, disableNestedFragments)
-        if (child.needAnchor)
-          context.pushStringPart(`<!--${BLOCK_ANCHOR_END_LABEL}-->`)
         break
       case NodeTypes.IF_BRANCH:
         // no-op - handled by ssrProcessIf
@@ -267,7 +238,7 @@ export function processChildren(
         return exhaustiveCheck
     }
   }
-  if (asFragment && !vapor) {
+  if (asFragment) {
     context.pushStringPart(`<!--]-->`)
   }
 }
@@ -283,67 +254,6 @@ export function processChildrenAsStatement(
   return createBlockStatement(childContext.body)
 }
 
-export function processBlockNodeAnchor(children: TemplateChildNode[]): void {
-  let prevBlocks: (TemplateChildNode & { needAnchor?: boolean })[] = []
-  let hasStaticNode = false
-  let blockCount = 0
-  for (const child of children) {
-    if (isBlockNode(child)) {
-      prevBlocks.push(child)
-      blockCount++
-    }
-
-    if (isStaticNode(child)) {
-      if (prevBlocks.length) {
-        if (hasStaticNode) {
-          // insert
-          prevBlocks.forEach(child => (child.needAnchor = true))
-        } else {
-          // prepend
-          prevBlocks.forEach(child => (child.needAnchor = true))
-        }
-        prevBlocks = []
-      }
-      hasStaticNode = true
-    }
-  }
-
-  // When there is only one block node, no anchor is needed,
-  // firstChild is used as the hydration node
-  if (prevBlocks.length && !(blockCount === 1 && !hasStaticNode)) {
-    // append
-    prevBlocks.forEach(child => (child.needAnchor = true))
-  }
-}
-
-export function hasBlockDir(
-  props: Array<AttributeNode | DirectiveNode>,
-): boolean {
-  return props.some(p => p.name === 'if' || p.name === 'for')
-}
-
-function isBlockNode(child: TemplateChildNode): boolean {
-  return (
-    child.type === NodeTypes.IF ||
-    child.type === NodeTypes.FOR ||
-    (child.type === NodeTypes.ELEMENT &&
-      (child.tagType === ElementTypes.COMPONENT ||
-        child.tagType === ElementTypes.SLOT ||
-        hasBlockDir(child.props)))
-  )
-}
-
-function isStaticNode(child: TemplateChildNode): boolean {
-  return (
-    child.type === NodeTypes.TEXT ||
-    child.type === NodeTypes.INTERPOLATION ||
-    child.type === NodeTypes.COMMENT ||
-    (child.type === NodeTypes.ELEMENT &&
-      child.tagType === ElementTypes.ELEMENT &&
-      !hasBlockDir(child.props))
-  )
-}
-
 export function isElementWithChildren(
   node: TemplateChildNode,
 ): node is ElementNode {
index 47c906a07cd7d4087a83ee6f4d85df09c07021e8..08f78aa561fec571df7296ee58a8232a689921fd 100644 (file)
@@ -1,7 +1,6 @@
 import {
   CREATE_VNODE,
   type CallExpression,
-  type CommentNode,
   type CompilerOptions,
   type ComponentNode,
   DOMDirectiveTransforms,
@@ -9,14 +8,11 @@ import {
   type DirectiveNode,
   ElementTypes,
   type ExpressionNode,
-  type ForNode,
   type FunctionExpression,
-  type IfNode,
   type JSChildNode,
   Namespaces,
   type NodeTransform,
   NodeTypes,
-  type PlainElementNode,
   RESOLVE_DYNAMIC_COMPONENT,
   type ReturnStatement,
   type RootNode,
@@ -47,8 +43,6 @@ import {
 import { SSR_RENDER_COMPONENT, SSR_RENDER_VNODE } from '../runtimeHelpers'
 import {
   type SSRTransformContext,
-  isElementWithChildren,
-  processBlockNodeAnchor,
   processChildren,
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
@@ -61,19 +55,7 @@ import {
   ssrProcessTransitionGroup,
   ssrTransformTransitionGroup,
 } from './ssrTransformTransitionGroup'
-import {
-  BLOCK_ANCHOR_END_LABEL,
-  BLOCK_ANCHOR_START_LABEL,
-  DYNAMIC_COMPONENT_ANCHOR_LABEL,
-  FOR_ANCHOR_LABEL,
-  IF_ANCHOR_LABEL,
-  SLOT_ANCHOR_LABEL,
-  extend,
-  isArray,
-  isObject,
-  isPlainObject,
-  isSymbol,
-} from '@vue/shared'
+import { extend, isArray, isObject, isPlainObject, isSymbol } from '@vue/shared'
 import { buildSSRProps } from './ssrTransformElement'
 import {
   ssrProcessTransition,
@@ -144,9 +126,9 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
     // fallback in case the child is render-fn based). Store them in an array
     // for later use.
     if (clonedNode.children.length) {
-      buildSlots(clonedNode, context, (props, vFor, children, _loc, parent) => {
+      buildSlots(clonedNode, context, (props, vFor, children) => {
         vnodeBranches.push(
-          createVNodeSlotBranch(props, vFor, children, context, parent),
+          createVNodeSlotBranch(props, vFor, children, context),
         )
         return createFunctionExpression(undefined)
       })
@@ -282,11 +264,6 @@ export function ssrProcessComponent(
       // dynamic component (`resolveDynamicComponent` call)
       // the codegen node is a `renderVNode` call
       context.pushStatement(node.ssrCodegenNode)
-
-      // anchor for vapor dynamic component
-      if (context.options.vapor) {
-        context.pushStringPart(`<!--${DYNAMIC_COMPONENT_ANCHOR_LABEL}-->`)
-      }
     }
   }
 }
@@ -309,7 +286,6 @@ function createVNodeSlotBranch(
   vFor: DirectiveNode | undefined,
   children: TemplateChildNode[],
   parentContext: TransformContext,
-  parent: TemplateChildNode,
 ): ReturnStatement {
   // apply a sub-transform using vnode-based transforms.
   const rawOptions = rawOptionsMap.get(parentContext.root)!
@@ -344,9 +320,6 @@ function createVNodeSlotBranch(
   if (vFor) {
     wrapperProps.push(extend({}, vFor))
   }
-  if (parentContext.vapor) {
-    children = injectVaporAnchors(children, parent)
-  }
 
   const wrapperNode: TemplateNode = {
     type: NodeTypes.ELEMENT,
@@ -400,209 +373,6 @@ function subTransform(
   // - hoists are not enabled for the client branch here
 }
 
-function injectVaporAnchors(
-  children: TemplateChildNode[],
-  parent: TemplateChildNode,
-): TemplateChildNode[] {
-  if (isElementWithChildren(parent)) {
-    processBlockNodeAnchor(children)
-  }
-
-  const newChildren: TemplateChildNode[] = []
-  for (let i = 0; i < children.length; i++) {
-    const child = children[i]
-
-    if (child.type !== NodeTypes.ELEMENT) {
-      newChildren.push(child)
-      continue
-    }
-
-    const { tagType, props } = child
-    let needBlockAnchor: boolean | undefined
-
-    if (
-      tagType === ElementTypes.COMPONENT ||
-      tagType === ElementTypes.SLOT ||
-      tagType === ElementTypes.TEMPLATE
-    ) {
-      needBlockAnchor = child.needAnchor
-    } else if (tagType === ElementTypes.ELEMENT) {
-      let hasIf = false
-      let hasFor = false
-
-      for (const prop of props) {
-        if (prop.name === 'if') {
-          hasIf = true
-          break
-        } else if (prop.name === 'for') {
-          hasFor = true
-        }
-      }
-
-      if (hasIf) {
-        needBlockAnchor = (child as any as IfNode).needAnchor
-        const lastBranchIndex = findLastIfBranchIndex(children, i)
-        if (lastBranchIndex > i) {
-          injectIfAnchors(
-            needBlockAnchor,
-            newChildren,
-            i,
-            lastBranchIndex,
-            children,
-          )
-          i = lastBranchIndex
-          continue
-        }
-      } else if (hasFor) {
-        needBlockAnchor = (child as any as ForNode).needAnchor
-      }
-    }
-
-    if (needBlockAnchor) {
-      newChildren.push(createAnchor(BLOCK_ANCHOR_START_LABEL))
-    }
-
-    newChildren.push(child)
-
-    // inject fragment anchor
-    const fragmentAnchorLabel = getFragmentAnchorLabel(child)
-    if (fragmentAnchorLabel) newChildren.push(createAnchor(fragmentAnchorLabel))
-
-    if (needBlockAnchor) {
-      newChildren.push(createAnchor(BLOCK_ANCHOR_END_LABEL))
-    }
-
-    child.children = injectVaporAnchors(child.children, child)
-  }
-
-  return newChildren
-}
-
-function injectIfAnchors(
-  needBlockAnchor: boolean | undefined,
-  newChildren: TemplateChildNode[],
-  i: number,
-  lastBranchIndex: number,
-  children: TemplateChildNode[],
-) {
-  if (needBlockAnchor) {
-    newChildren.push(createAnchor(BLOCK_ANCHOR_START_LABEL))
-  }
-
-  for (let j = i; j <= lastBranchIndex; j++) {
-    const node = children[j] as PlainElementNode
-    const fragmentAnchorLabel = getFragmentAnchorLabel(node)
-    let isElse = false
-
-    const conditionalProps: typeof node.props = []
-    const restProps: typeof node.props = []
-
-    for (const prop of node.props) {
-      if (
-        prop.name === 'if' ||
-        prop.name === 'else-if' ||
-        prop.name === 'else'
-      ) {
-        conditionalProps.push(prop)
-        if (prop.name === 'else') isElse = true
-      } else {
-        restProps.push(prop)
-      }
-    }
-    node.props = restProps
-
-    // wrap the node with a template node
-    const wrapperNode: TemplateNode = {
-      type: NodeTypes.ELEMENT,
-      ns: Namespaces.HTML,
-      tag: 'template',
-      tagType: ElementTypes.TEMPLATE,
-      props: conditionalProps,
-      children: [node],
-      loc: node.loc,
-      codegenNode: undefined,
-    }
-    newChildren.push(wrapperNode)
-
-    if (fragmentAnchorLabel) {
-      const repeatCount = j - i - (isElse ? 1 : 0) + 1
-      wrapperNode.children.push(
-        createAnchor(`<!--${fragmentAnchorLabel}-->`.repeat(repeatCount)),
-      )
-    }
-    node.children = injectVaporAnchors(node.children, node)
-  }
-
-  if (needBlockAnchor) {
-    newChildren.push(createAnchor(BLOCK_ANCHOR_END_LABEL))
-  }
-}
-
-function createAnchor(content: string): CommentNode {
-  return {
-    type: NodeTypes.COMMENT,
-    content,
-    loc: locStub,
-  }
-}
-
-function findLastIfBranchIndex(
-  children: TemplateChildNode[],
-  ifIndex: number,
-): number {
-  let lastIndex = ifIndex
-
-  for (let i = ifIndex + 1; i < children.length; i++) {
-    const sibling = children[i]
-
-    if (sibling.type !== NodeTypes.ELEMENT) {
-      continue
-    }
-
-    let hasElseIf = false
-    let hasElse = false
-
-    for (const prop of sibling.props) {
-      if (prop.name === 'else-if') {
-        hasElseIf = true
-        break
-      } else if (prop.name === 'else') {
-        hasElse = true
-        break
-      }
-    }
-
-    if (hasElseIf || hasElse) {
-      lastIndex = i
-      if (hasElse) {
-        break
-      }
-    } else {
-      break
-    }
-  }
-
-  return lastIndex
-}
-
-function getFragmentAnchorLabel(child: TemplateChildNode): string | undefined {
-  if (child.type !== NodeTypes.ELEMENT) return
-
-  if (child.tagType === ElementTypes.COMPONENT && child.tag === 'component') {
-    return DYNAMIC_COMPONENT_ANCHOR_LABEL
-  } else if (child.tagType === ElementTypes.SLOT) {
-    return SLOT_ANCHOR_LABEL
-  } else if (
-    child.props.some(
-      p => p.name === 'if' || p.name === 'else-if' || p.name === 'else',
-    )
-  ) {
-    return IF_ANCHOR_LABEL
-  } else if (child.props.some(p => p.name === 'for')) {
-    return FOR_ANCHOR_LABEL
-  }
-}
-
 function clone(v: any): any {
   if (isArray(v)) {
     return v.map(clone)
index 5c72c8a2a629b5c866eef55a36faa37d819fe0aa..aae7a31e8b8b01bfec066d31785553b26cb43fb9 100644 (file)
@@ -16,7 +16,6 @@ import {
   type SSRTransformContext,
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
-import { SLOT_ANCHOR_LABEL } from '@vue/shared'
 
 export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
   if (isSlotOutlet(node)) {
@@ -94,9 +93,4 @@ export function ssrProcessSlotOutlet(
   }
 
   context.pushStatement(node.ssrCodegenNode!)
-
-  // anchor for vapor slot
-  if (context.options.vapor) {
-    context.pushStringPart(`<!--${SLOT_ANCHOR_LABEL}-->`)
-  }
 }
index f786751f8bdb1feb03969cba23ef01cfc8104f77..f1541ce4ab012a14ebfdc6f132553ee3f1819d6b 100644 (file)
@@ -13,7 +13,6 @@ import {
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
 import { SSR_RENDER_LIST } from '../runtimeHelpers'
-import { FOR_ANCHOR_LABEL } from '@vue/shared'
 
 // Plugin for the first transform pass, which simply constructs the AST node
 export const ssrTransformFor: NodeTransform =
@@ -38,9 +37,8 @@ export function ssrProcessFor(
     needFragmentWrapper,
   )
 
-  const vapor = context.options.vapor
   // v-for always renders a fragment unless explicitly disabled
-  if (!disableNestedFragments && !vapor) {
+  if (!disableNestedFragments) {
     context.pushStringPart(`<!--[-->`)
   }
   context.pushStatement(
@@ -49,12 +47,7 @@ export function ssrProcessFor(
       renderLoop,
     ]),
   )
-  if (!disableNestedFragments && !vapor) {
+  if (!disableNestedFragments) {
     context.pushStringPart(`<!--]-->`)
   }
-
-  // anchor for vapor v-for fragment
-  if (vapor) {
-    context.pushStringPart(`<!--${FOR_ANCHOR_LABEL}-->`)
-  }
 }
index f565c1be562a63b43154346d77b4e0751e8747a5..775ad835f2f54418191d3b9cd3100505ac579ebb 100644 (file)
@@ -14,7 +14,6 @@ import {
   type SSRTransformContext,
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
-import { IF_ANCHOR_LABEL } from '@vue/shared'
 
 // Plugin for the first transform pass, which simply constructs the AST node
 export const ssrTransformIf: NodeTransform = createStructuralDirectiveTransform(
@@ -37,16 +36,6 @@ export function ssrProcessIf(
   )
   context.pushStatement(ifStatement)
 
-  // anchor addition rules (matching runtime-vapor behavior):
-  // - v-else-if: the N-th branch â†’ add N anchors
-  // - v-else: if there are M preceding branches â†’ add M anchors
-  const isVapor = context.options.vapor
-  if (isVapor) {
-    ifStatement.consequent.body.push(
-      createCallExpression(`_push`, createIfAnchors(1)),
-    )
-  }
-
   let currentIf = ifStatement
   for (let i = 1; i < node.branches.length; i++) {
     const branch = node.branches[i]
@@ -61,29 +50,15 @@ export function ssrProcessIf(
         branch.condition,
         branchBlockStatement,
       )
-
-      if (isVapor) {
-        branchBlockStatement.body.push(
-          createCallExpression(`_push`, createIfAnchors(i + 1)),
-        )
-      }
     } else {
       // else
       currentIf.alternate = branchBlockStatement
-
-      if (isVapor) {
-        branchBlockStatement.body.push(
-          createCallExpression(`_push`, createIfAnchors(i)),
-        )
-      }
     }
   }
 
   if (!currentIf.alternate && !disableComment) {
     currentIf.alternate = createBlockStatement([
-      createCallExpression(`_push`, [
-        isVapor ? `\`<!--${IF_ANCHOR_LABEL}-->\`` : '`<!---->`',
-      ]),
+      createCallExpression(`_push`, ['`<!---->`']),
     ])
   }
 }
@@ -102,11 +77,3 @@ function processIfBranch(
 
   return processChildrenAsStatement(branch, context, needFragmentWrapper)
 }
-
-function createIfAnchors(count: number): string[] {
-  const anchors: string[] = []
-  for (let i = 0; i < count; i++) {
-    anchors.push(`<!--${IF_ANCHOR_LABEL}-->`)
-  }
-  return [`\`${anchors.join('')}\``]
-}
index ea0e2bbf6bbdb10701e633e3dbefb9a9c49ed2cc..41e2e776bd9de106e2b278c5433869f87d08ae2f 100644 (file)
@@ -38,7 +38,7 @@ export function render(_ctx) {
     "default": () => {
       const n0 = _createIf(() => (true), () => {
         const n3 = t0()
-        _setInsertionState(n3)
+        _setInsertionState(n3, null)
         const n2 = _createComponentWithFallback(_component_Bar)
         _withVaporDirectives(n2, [[_directive_hello, void 0, void 0, { world: true }]])
         return n3
@@ -157,9 +157,9 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
   const _component_Comp = _resolveComponent("Comp")
   const n0 = t0()
   const n3 = t1()
-  const n2 = _child(n3)
   _setInsertionState(n3, 0)
   const n1 = _createComponentWithFallback(_component_Comp)
+  const n2 = _child(n3)
   _renderEffect(() => {
     _setProp(n3, "id", _ctx.foo)
     _setText(n2, _toDisplayString(_ctx.bar))
@@ -220,9 +220,9 @@ export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n3 = t0()
   const n1 = _child(n3)
-  _setInsertionState(n1)
+  _setInsertionState(n1, null)
   const n0 = _createSlot("default", null)
-  _setInsertionState(n3, null)
+  _setInsertionState(n3, 1)
   const n2 = _createComponentWithFallback(_component_Comp)
   return n3
 }"
@@ -280,30 +280,6 @@ export function render(_ctx) {
 }"
 `;
 
-exports[`compile > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = `
-"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
-const t0 = _template("<div></div>")
-const t1 = _template("<div><div></div><!><div></div><!><div><button></button></div></div>", true)
-
-export function render(_ctx) {
-  const _component_Comp = _resolveComponent("Comp")
-  const n6 = t1()
-  const n5 = _next(_child(n6))
-  const n7 = _nthChild(n6, 3)
-  const p0 = _next(n7)
-  const n4 = _child(p0)
-  _setInsertionState(n6, n5)
-  const n0 = _createComponentWithFallback(_component_Comp)
-  _setInsertionState(n6, n7)
-  const n1 = _createIf(() => (true), () => {
-    const n3 = t0()
-    return n3
-  })
-  _renderEffect(() => _setProp(n4, "disabled", _ctx.foo))
-  return n6
-}"
-`;
-
 exports[`compile > static + dynamic root 1`] = `
 "import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
 const t0 = _template(" ")
index 7963a9e98c2c63a96ec47ee79a8ac1cfe0cfa413..ae59cc78e0c5ac1981b56f849a99a3d73675cbcb 100644 (file)
@@ -221,23 +221,6 @@ describe('compile', () => {
     })
   })
 
-  describe('setInsertionState', () => {
-    test('next, child and nthChild should be above the setInsertionState', () => {
-      const code = compile(`
-      <div>
-        <div />
-        <Comp />
-        <div />
-        <div v-if="true" />
-        <div>
-          <button :disabled="foo" />
-        </div>
-      </div>
-      `)
-      expect(code).toMatchSnapshot()
-    })
-  })
-
   describe('execution order', () => {
     test('basic', () => {
       const code = compile(`<div :id="foo">{{ bar }}</div>`)
index f73bb18b9c6e7495431545e311f0ea7bb7c0cffb..2d6def4e1276ecf119b41b956157a8aad2a443a3 100644 (file)
@@ -87,7 +87,7 @@ const t1 = _template("<div></div>", true)
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
     const n5 = t1()
-    _setInsertionState(n5)
+    _setInsertionState(n5, null)
     const n2 = _createFor(() => (_for_item0.value), (_for_item1) => {
       const n4 = t0()
       const x4 = _txt(n4)
index b6107d5a1a1c63067461c43cda989eb22e144065..4ca745ef0f575bcfefe521764cfea16fb0ddea7b 100644 (file)
@@ -42,7 +42,7 @@ const t0 = _template("<div></div>", true)
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = t0()
-  _setInsertionState(n1)
+  _setInsertionState(n1, null)
   const n0 = _createComponentWithFallback(_component_Comp, { id: () => (_ctx.foo) }, null, null, true)
   return n1
 }"
index 8250966617f42748b4dfaefd368b8b2eca5b0c9d..ff3806611addf37b4f0dc8351306c8260bf22204 100644 (file)
@@ -86,7 +86,6 @@ export class CodegenContext {
       isTS: false,
       inSSR: false,
       inline: false,
-      vapor: false,
       bindingMetadata: {},
       expressionPlugins: [],
     }
index 40fa8da6322d9cb80ce6ffe13b72efe041161943..9ad5da12139e5041a85e26dccd3aaceb560c4ce7 100644 (file)
@@ -71,7 +71,7 @@ export function genBlockContent(
   }
   for (const child of dynamic.children) {
     if (!child.hasDynamicChild) {
-      push(...genChildren(child, context, push, `n${child.id!}`))
+      push(...genChildren(child, context, `n${child.id!}`))
     }
   }
 
index bc78032380f92f350744df1962e91dbce76ccde5..beefab6cf5fc4653d50806e4bf61fe6b64d8bebf 100644 (file)
@@ -168,7 +168,7 @@ function genInsertionState(
   operation: InsertionStateTypes,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { parent, anchor } = operation
+  const { parent, anchor, append } = operation
   return [
     NEWLINE,
     ...genCall(
@@ -178,8 +178,12 @@ function genInsertionState(
         ? undefined
         : anchor === -1 // -1 indicates prepend
           ? `0` // runtime anchor value for prepend
-          : anchor === -2 // -2 indicates append
-            ? `null` // runtime anchor value for append
+          : append // -2 indicates append
+            ? // null or number > 0 for append
+              // number > 0 is used for locate the previous static node during hydration
+              anchor === 0
+              ? 'null'
+              : `${anchor}`
             : `n${anchor}`,
     ),
   ]
index f710c8869fb3c973871639b9708ed78ebc49f79d..c22d0bf987ec992984f1c83dec791363a2a15f83 100644 (file)
@@ -36,7 +36,7 @@ export function genSelf(
   }
 
   if (hasDynamicChild) {
-    push(...genChildren(dynamic, context, push, `n${id}`))
+    push(...genChildren(dynamic, context, `n${id}`))
   }
 
   return frag
@@ -45,7 +45,6 @@ export function genSelf(
 export function genChildren(
   dynamic: IRDynamicInfo,
   context: CodegenContext,
-  pushBlock: (...items: CodeFragment[]) => number,
   from: string = `n${dynamic.id}`,
 ): CodeFragment[] {
   const { helper } = context
@@ -76,17 +75,17 @@ export function genChildren(
     // p for "placeholder" variables that are meant for possible reuse by
     // other access paths
     const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
-    pushBlock(NEWLINE, `const ${variable} = `)
+    push(NEWLINE, `const ${variable} = `)
 
     if (prev) {
       if (elementIndex - prev[1] === 1) {
-        pushBlock(...genCall(helper('next'), prev[0]))
+        push(...genCall(helper('next'), prev[0]))
       } else {
-        pushBlock(...genCall(helper('nthChild'), from, String(elementIndex)))
+        push(...genCall(helper('nthChild'), from, String(elementIndex)))
       }
     } else {
       if (elementIndex === 0) {
-        pushBlock(...genCall(helper('child'), from))
+        push(...genCall(helper('child'), from))
       } else {
         // check if there's a node that we can reuse from
         let init = genCall(helper('child'), from)
@@ -95,7 +94,7 @@ export function genChildren(
         } else if (elementIndex > 1) {
           init = genCall(helper('nthChild'), from, String(elementIndex))
         }
-        pushBlock(...init)
+        push(...init)
       }
     }
 
@@ -108,7 +107,7 @@ export function genChildren(
     }
 
     prev = [variable, elementIndex]
-    push(...genChildren(child, context, pushBlock, variable))
+    push(...genChildren(child, context, variable))
   }
 
   return frag
index 96110207d5a03fe26d7b641dfcc0e814ff6f4233..e5f4a1126e33f3f93666a38310dc3cf76479e069 100644 (file)
@@ -80,6 +80,7 @@ export interface IfIRNode extends BaseIRNode {
   once?: boolean
   parent?: number
   anchor?: number
+  append?: boolean
   childIndex?: number
 }
 
@@ -100,6 +101,7 @@ export interface ForIRNode extends BaseIRNode, IRFor {
   onlyChild: boolean
   parent?: number
   anchor?: number
+  append?: boolean
   childIndex?: number
 }
 
@@ -202,6 +204,7 @@ export interface CreateComponentIRNode extends BaseIRNode {
   dynamic?: SimpleExpressionNode
   parent?: number
   anchor?: number
+  append?: boolean
   childIndex?: number
   scopeId?: string | null
 }
@@ -220,6 +223,7 @@ export interface SlotOutletIRNode extends BaseIRNode {
   forwarded?: boolean
   parent?: number
   anchor?: number
+  append?: boolean
   childIndex?: number
 }
 
index 1ad8dae5ede63f59bf655eaae41ed2646901e907..69250ab5c3beb3f332e542c38b668a81016f8743 100644 (file)
@@ -59,19 +59,17 @@ export const transformChildren: NodeTransform = (node, context) => {
 
 function processDynamicChildren(context: TransformContext<ElementNode>) {
   let prevDynamics: IRDynamicInfo[] = []
-  let hasStaticTemplate = false
-  let dynamicCount = 0
+  let staticCount = 0
   const children = context.dynamic.children
 
   for (const [index, child] of children.entries()) {
     if (child.flags & DynamicFlag.INSERT) {
       prevDynamics.push(child)
-      dynamicCount++
     }
 
     if (!(child.flags & DynamicFlag.NON_TEMPLATE)) {
       if (prevDynamics.length) {
-        if (hasStaticTemplate) {
+        if (staticCount) {
           context.childrenTemplate[index - prevDynamics.length] = `<!>`
           prevDynamics[0].flags -= DynamicFlag.NON_TEMPLATE
           const anchor = (prevDynamics[0].anchor = context.increaseId())
@@ -81,25 +79,20 @@ function processDynamicChildren(context: TransformContext<ElementNode>) {
         }
         prevDynamics = []
       }
-      hasStaticTemplate = true
+      staticCount++
     }
   }
 
   if (prevDynamics.length) {
-    registerInsertion(
-      prevDynamics,
-      context,
-      // When there is only one dynamic node, no anchor is needed,
-      // firstChild is used as the hydration node
-      dynamicCount === 1 && !hasStaticTemplate ? undefined : -2 /* append */,
-    )
+    registerInsertion(prevDynamics, context, staticCount, true)
   }
 }
 
 function registerInsertion(
   dynamics: IRDynamicInfo[],
   context: TransformContext,
-  anchor?: number,
+  anchor: number,
+  append?: boolean,
 ) {
   for (const child of dynamics) {
     if (child.template != null) {
@@ -108,12 +101,13 @@ function registerInsertion(
         type: IRNodeTypes.INSERT_NODE,
         elements: dynamics.map(child => child.id!),
         parent: context.reference(),
-        anchor: anchor === -2 ? undefined : anchor,
+        anchor: append ? undefined : anchor,
       })
     } else if (child.operation && isBlockOperation(child.operation)) {
       // block types
       child.operation.parent = context.reference()
       child.operation.anchor = anchor
+      child.operation.append = append
     }
   }
 }
index d558c690e51653c4ff0d4a05bca8e2c32575b53e..72b7e07873b5d2c1b3a76792b1e5288f02a93179 100644 (file)
@@ -169,13 +169,21 @@ describe('Vapor Mode hydration', () => {
       <template><span/>{{ data }}{{ data }}<span/></template>
     `)
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<span></span>foofoo<span></span>"`,
+        `
+        "
+        <!--[--><span></span>foofoo<span></span><!--]-->
+        "
+      `,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<span></span>barbar<span></span>"`,
+        `
+        "
+        <!--[--><span></span>barbar<span></span><!--]-->
+        "
+      `,
       )
     })
 
@@ -199,13 +207,21 @@ describe('Vapor Mode hydration', () => {
       <template><span/>{{ data }}A{{ data }}B{{ data }}<span/></template>
     `)
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<span></span>fooAfooBfoo<span></span>"`,
+        `
+        "
+        <!--[--><span></span>fooAfooBfoo<span></span><!--]-->
+        "
+      `,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<span></span>barAbarBbar<span></span>"`,
+        `
+        "
+        <!--[--><span></span>barAbarBbar<span></span><!--]-->
+        "
+      `,
       )
     })
 
@@ -240,7 +256,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--> <!--]-->
-        <!--slot-->"
+        "
       `,
       )
 
@@ -250,7 +266,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[-->foo<!--]-->
-        <!--slot-->"
+        "
       `,
       )
     })
@@ -270,13 +286,21 @@ describe('Vapor Mode hydration', () => {
       <template> A<span>{{ data }}</span>{{ data }}</template>
     `)
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `" A<span>foo</span>foo"`,
+        `
+        "
+        <!--[--> A<span>foo</span>foo<!--]-->
+        "
+      `,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `" A<span>bar</span>bar"`,
+        `
+        "
+        <!--[--> A<span>bar</span>bar<!--]-->
+        "
+      `,
       )
     })
 
@@ -350,21 +374,13 @@ describe('Vapor Mode hydration', () => {
         { Child: `<template>{{ data }}</template>` },
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->foo<!--]]-->
-        </div>"
-      `,
+        `"<div><span></span>foo</div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->bar<!--]]-->
-        </div>"
-      `,
+        `"<div><span></span>bar</div>"`,
       )
     })
 
@@ -378,7 +394,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>foo</div>-foo-<!--]]-->
+        <!--[--><div>foo</div>-foo-<!--]-->
         </div>"
       `,
       )
@@ -388,7 +404,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>bar</div>-bar-<!--]]-->
+        <!--[--><div>bar</div>-bar-<!--]-->
         </div>"
       `,
       )
@@ -404,7 +420,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[--><div>foo</div>-foo-<!--]]-->
+        <!--[--><div>foo</div>-foo-<!--]-->
         <span></span></div>"
       `,
       )
@@ -414,7 +430,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[--><div>bar</div>-bar-<!--]]-->
+        <!--[--><div>bar</div>-bar-<!--]-->
         <span></span></div>"
       `,
       )
@@ -433,7 +449,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[--><div></div><div>foo</div>-foo-<div></div><!--]]-->
+        <!--[--><div></div>
+        <!--[--><div>foo</div>-foo-<!--]-->
+        <div></div><!--]-->
         <span></span></div>"
       `,
       )
@@ -443,7 +461,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[--><div></div><div>bar</div>-bar-<div></div><!--]]-->
+        <!--[--><div></div>
+        <!--[--><div>bar</div>-bar-<!--]-->
+        <div></div><!--]-->
         <span></span></div>"
       `,
       )
@@ -464,21 +484,13 @@ describe('Vapor Mode hydration', () => {
         },
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->foo<!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span>foo<span></span></div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->bar<!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span>bar<span></span></div>"`,
       )
     })
 
@@ -493,21 +505,13 @@ describe('Vapor Mode hydration', () => {
         },
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div>foo</div><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div>foo</div><span></span></div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div>bar</div><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div>bar</div><span></span></div>"`,
       )
     })
 
@@ -522,61 +526,13 @@ describe('Vapor Mode hydration', () => {
         },
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div><span></span>
-        <!--[[--><div>foo</div><!--]]-->
-        <span></span></div><!--]]-->
-        <span></span></div>"
-      `,
-      )
-
-      data.value = 'bar'
-      await nextTick()
-      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div><span></span>
-        <!--[[--><div>bar</div><!--]]-->
-        <span></span></div><!--]]-->
-        <span></span></div>"
-      `,
-      )
-    })
-
-    test('consecutive components with insertion anchor', async () => {
-      const { container, data } = await testHydration(
-        `<template>
-        <div>
-          <span/>
-          <components.Child/>
-          <components.Child/>
-          <span/>
-        </div>
-      </template>
-      `,
-        {
-          Child: `<template>{{ data }}</template>`,
-        },
-      )
-      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->foo<!--]]-->
-        <!--[[-->foo<!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div><span></span><div>foo</div><span></span></div><span></span></div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->bar<!--]]-->
-        <!--[[-->bar<!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div><span></span><div>bar</div><span></span></div><span></span></div>"`,
       )
     })
 
@@ -597,24 +553,14 @@ describe('Vapor Mode hydration', () => {
         data,
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div>
-        <!--[[--><span>foo</span><!--]]-->
-        <!--[[--><span>bar</span><!--]]-->
-        </div>"
-      `,
+        `"<div><span>foo</span><span>bar</span></div>"`,
       )
 
       data.foo = 'foo1'
       data.bar = 'bar1'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div>
-        <!--[[--><span>foo1</span><!--]]-->
-        <!--[[--><span>bar1</span><!--]]-->
-        </div>"
-      `,
+        `"<div><span>foo1</span><span>bar1</span></div>"`,
       )
     })
 
@@ -629,23 +575,13 @@ describe('Vapor Mode hydration', () => {
         },
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div>foo</div><!--]]-->
-        <!--[[--><div>foo</div><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div>foo</div><div>foo</div><span></span></div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div>bar</div><!--]]-->
-        <!--[[--><div>bar</div><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div>bar</div><div>bar</div><span></span></div>"`,
       )
     })
 
@@ -660,27 +596,13 @@ describe('Vapor Mode hydration', () => {
         },
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div><span></span>
-        <!--[[--><div>foo</div><!--]]-->
-        <!--[[--><div>foo</div><!--]]-->
-        <span></span></div><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div><span></span><div>foo</div><div>foo</div><span></span></div><span></span></div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div><span></span>
-        <!--[[--><div>bar</div><!--]]-->
-        <!--[[--><div>bar</div><!--]]-->
-        <span></span></div><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div><span></span><div>bar</div><div>bar</div><span></span></div><span></span></div>"`,
       )
     })
 
@@ -701,64 +623,13 @@ describe('Vapor Mode hydration', () => {
         },
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->foo<!--]]-->
-        <span></span>
-        <!--[[-->foo<!--]]-->
-        <span></span></div>"
-      `,
-      )
-
-      data.value = 'bar'
-      await nextTick()
-      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->bar<!--]]-->
-        <span></span>
-        <!--[[-->bar<!--]]-->
-        <span></span></div>"
-      `,
-      )
-    })
-
-    test('mixed component and text with insertion anchor', async () => {
-      const { container, data } = await testHydration(
-        `<template>
-        <div>
-          <span/>
-          <components.Child/>
-          {{ data }}
-          <components.Child/>
-          <span/>
-        </div>
-      </template>
-      `,
-        {
-          Child: `<template>{{ data }}</template>`,
-        },
-      )
-      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->foo<!--]]-->
-         foo 
-        <!--[[-->foo<!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span>foo<span></span>foo<span></span></div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->bar<!--]]-->
-         bar 
-        <!--[[-->bar<!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span>bar<span></span>bar<span></span></div>"`,
       )
     })
 
@@ -779,7 +650,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>foo</div>-foo<!--]]-->
+        <!--[--><div>foo</div>-foo<!--]-->
         <span></span></div>"
       `,
       )
@@ -789,7 +660,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>bar</div>-bar<!--]]-->
+        <!--[--><div>bar</div>-bar<!--]-->
         <span></span></div>"
       `,
       )
@@ -808,7 +679,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>foo</div>-foo-<!--]]-->
+        <!--[--><div>foo</div>-foo-<!--]-->
         <span></span></div>"
       `,
       )
@@ -818,7 +689,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>bar</div>-bar-<!--]]-->
+        <!--[--><div>bar</div>-bar-<!--]-->
         <span></span></div>"
       `,
       )
@@ -836,11 +707,9 @@ describe('Vapor Mode hydration', () => {
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div><span></span>
-        <!--[[--><div><span></span>
-        <!--[[--><div>foo</div>-foo-<!--]]-->
-        <span></span></div><!--]]-->
-        <span></span></div>"
+        "<div><span></span><div><span></span>
+        <!--[--><div>foo</div>-foo-<!--]-->
+        <span></span></div><span></span></div>"
       `,
       )
 
@@ -848,11 +717,9 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div><span></span>
-        <!--[[--><div><span></span>
-        <!--[[--><div>bar</div>-bar-<!--]]-->
-        <span></span></div><!--]]-->
-        <span></span></div>"
+        "<div><span></span><div><span></span>
+        <!--[--><div>bar</div>-bar-<!--]-->
+        <span></span></div><span></span></div>"
       `,
       )
     })
@@ -875,8 +742,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>foo</div>-foo<!--]]-->
-        <!--[[--><div>foo</div>-foo<!--]]-->
+        <!--[--><div>foo</div>-foo<!--]-->
+        <!--[--><div>foo</div>-foo<!--]-->
         <span></span></div>"
       `,
       )
@@ -886,8 +753,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>bar</div>-bar<!--]]-->
-        <!--[[--><div>bar</div>-bar<!--]]-->
+        <!--[--><div>bar</div>-bar<!--]-->
+        <!--[--><div>bar</div>-bar<!--]-->
         <span></span></div>"
       `,
       )
@@ -906,8 +773,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>foo</div>-foo-<!--]]-->
-        <!--[[--><div>foo</div>-foo-<!--]]-->
+        <!--[--><div>foo</div>-foo-<!--]-->
+        <!--[--><div>foo</div>-foo-<!--]-->
         <span></span></div>"
       `,
       )
@@ -917,8 +784,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>bar</div>-bar-<!--]]-->
-        <!--[[--><div>bar</div>-bar-<!--]]-->
+        <!--[--><div>bar</div>-bar-<!--]-->
+        <!--[--><div>bar</div>-bar-<!--]-->
         <span></span></div>"
       `,
       )
@@ -936,12 +803,10 @@ describe('Vapor Mode hydration', () => {
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div><span></span>
-        <!--[[--><div><span></span>
-        <!--[[--><div>foo</div>-foo-<!--]]-->
-        <!--[[--><div>foo</div>-foo-<!--]]-->
-        <span></span></div><!--]]-->
-        <span></span></div>"
+        "<div><span></span><div><span></span>
+        <!--[--><div>foo</div>-foo-<!--]-->
+        <!--[--><div>foo</div>-foo-<!--]-->
+        <span></span></div><span></span></div>"
       `,
       )
 
@@ -949,12 +814,10 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div><span></span>
-        <!--[[--><div><span></span>
-        <!--[[--><div>bar</div>-bar-<!--]]-->
-        <!--[[--><div>bar</div>-bar-<!--]]-->
-        <span></span></div><!--]]-->
-        <span></span></div>"
+        "<div><span></span><div><span></span>
+        <!--[--><div>bar</div>-bar-<!--]-->
+        <!--[--><div>bar</div>-bar-<!--]-->
+        <span></span></div><span></span></div>"
       `,
       )
     })
@@ -972,7 +835,10 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>foo</div>-foo-<div>foo</div>-foo-<!--]]-->
+        <!--[-->
+        <!--[--><div>foo</div>-foo-<!--]-->
+        <!--[--><div>foo</div>-foo-<!--]-->
+        <!--]-->
         <span></span></div>"
       `,
       )
@@ -982,7 +848,10 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>bar</div>-bar-<div>bar</div>-bar-<!--]]-->
+        <!--[-->
+        <!--[--><div>bar</div>-bar-<!--]-->
+        <!--[--><div>bar</div>-bar-<!--]-->
+        <!--]-->
         <span></span></div>"
       `,
       )
@@ -1007,9 +876,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>foo</div>-foo<!--]]-->
+        <!--[--><div>foo</div>-foo<!--]-->
         <span></span>
-        <!--[[--><div>foo</div>-foo<!--]]-->
+        <!--[--><div>foo</div>-foo<!--]-->
         <span></span></div>"
       `,
       )
@@ -1019,9 +888,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>bar</div>-bar<!--]]-->
+        <!--[--><div>bar</div>-bar<!--]-->
         <span></span>
-        <!--[[--><div>bar</div>-bar<!--]]-->
+        <!--[--><div>bar</div>-bar<!--]-->
         <span></span></div>"
       `,
       )
@@ -1046,9 +915,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>foo</div>-foo<!--]]-->
+        <!--[--><div>foo</div>-foo<!--]-->
          foo 
-        <!--[[--><div>foo</div>-foo<!--]]-->
+        <!--[--><div>foo</div>-foo<!--]-->
         <span></span></div>"
       `,
       )
@@ -1058,9 +927,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>bar</div>-bar<!--]]-->
+        <!--[--><div>bar</div>-bar<!--]-->
          bar 
-        <!--[[--><div>bar</div>-bar<!--]]-->
+        <!--[--><div>bar</div>-bar<!--]-->
         <span></span></div>"
       `,
       )
@@ -1106,21 +975,13 @@ describe('Vapor Mode hydration', () => {
         ref('foo'),
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div>foo</div><!--dynamic-component--><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div>foo</div><!--dynamic-component--><span></span></div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div>bar</div><!--dynamic-component--><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div>bar</div><!--dynamic-component--><span></span></div>"`,
       )
     })
 
@@ -1141,23 +1002,13 @@ describe('Vapor Mode hydration', () => {
         ref('foo'),
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div>foo</div><!--dynamic-component--><!--]]-->
-        <!--[[--><div>foo</div><!--dynamic-component--><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div>foo</div><!--dynamic-component--><div>foo</div><!--dynamic-component--><span></span></div>"`,
       )
 
       data.value = 'bar'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><div>bar</div><!--dynamic-component--><!--]]-->
-        <!--[[--><div>bar</div><!--dynamic-component--><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><div>bar</div><!--dynamic-component--><div>bar</div><!--dynamic-component--><span></span></div>"`,
       )
     })
 
@@ -1204,7 +1055,7 @@ describe('Vapor Mode hydration', () => {
         `
         "<div>
         <!--[--><span>foo</span><!--]-->
-        <!--slot--></div><!--dynamic-component-->"
+        </div><!--dynamic-component-->"
       `,
       )
 
@@ -1214,7 +1065,7 @@ describe('Vapor Mode hydration', () => {
         `
         "<div>
         <!--[--><span>bar</span><!--]-->
-        <!--slot--></div><!--dynamic-component-->"
+        </div><!--dynamic-component-->"
       `,
       )
     })
@@ -1336,21 +1187,13 @@ describe('Vapor Mode hydration', () => {
         data,
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span>outer</span>
-        <!--[[--><div>inner</div><!--if--><!--]]-->
-        </div><!--if-->"
-      `,
+        `"<div><span>outer</span><div>inner</div><!--if--></div><!--if-->"`,
       )
 
       data.inner = false
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span>outer</span>
-        <!--[[--><!--if--><!--]]-->
-        </div><!--if-->"
-      `,
+        `"<div><span>outer</span><!--if--></div><!--if-->"`,
       )
 
       data.outer = false
@@ -1431,40 +1274,19 @@ describe('Vapor Mode hydration', () => {
         data,
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<span>
-        <!--[[--><span>foo</span><!--if--><!--]]-->
-        <!--[[--><span>bar</span><!--if--><!--]]-->
-        <span>baz</span>
-        <!--[[--><span>qux</span><!--if--><!--]]-->
-        <span>quux</span></span><!--if-->"
-      `,
+        `"<span><span>foo</span><!--if--><span>bar</span><!--if--><span>baz</span><span>qux</span><!--if--><span>quux</span></span><!--if-->"`,
       )
 
       data.qux = 'qux1'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<span>
-        <!--[[--><span>foo</span><!--if--><!--]]-->
-        <!--[[--><span>bar</span><!--if--><!--]]-->
-        <span>baz</span>
-        <!--[[--><span>qux1</span><!--if--><!--]]-->
-        <span>quux</span></span><!--if-->"
-      `,
+        `"<span><span>foo</span><!--if--><span>bar</span><!--if--><span>baz</span><span>qux1</span><!--if--><span>quux</span></span><!--if-->"`,
       )
 
       data.foo = 'foo1'
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<span>
-        <!--[[--><span>foo1</span><!--if--><!--]]-->
-        <!--[[--><span>bar</span><!--if--><!--]]-->
-        <span>baz</span>
-        <!--[[--><span>qux1</span><!--if--><!--]]-->
-        <span>quux</span></span><!--if-->"
-      `,
+        `"<span><span>foo1</span><!--if--><span>bar</span><!--if--><span>baz</span><span>qux1</span><!--if--><span>quux</span></span><!--if-->"`,
       )
     })
 
@@ -1520,21 +1342,13 @@ describe('Vapor Mode hydration', () => {
         data,
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->foo<!--if--><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span>foo<!--if--><span></span></div>"`,
       )
 
       data.value = false
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><!--if--><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><!--if--><span></span></div>"`,
       )
     })
 
@@ -1558,12 +1372,7 @@ describe('Vapor Mode hydration', () => {
         data,
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div>
-        <!--[[--><span>foo</span><!--]]-->
-        <!--[[--><span>bar</span><!--]]-->
-        </div><!--if-->"
-      `,
+        `"<div><span>foo</span><span>bar</span></div><!--if-->"`,
       )
 
       data.show = false
@@ -1586,50 +1395,6 @@ describe('Vapor Mode hydration', () => {
       )
     })
 
-    test('consecutive v-if on component with insertion anchor', async () => {
-      const data = ref(true)
-      const { container } = await testHydration(
-        `<template>
-          <div>
-            <span/>
-            <components.Child v-if="data"/>
-            <components.Child v-if="data"/>
-            <span/>
-          </div>
-        </template>`,
-        { Child: `<template>foo</template>` },
-        data,
-      )
-      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->foo<!--if--><!--]]-->
-        <!--[[-->foo<!--if--><!--]]-->
-        <span></span></div>"
-      `,
-      )
-
-      data.value = false
-      await nextTick()
-      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><!--if--><!--]]-->
-        <!--[[--><!--if--><!--]]-->
-        <span></span></div>"
-      `,
-      )
-
-      data.value = true
-      await nextTick()
-      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
-        "<div><span></span>
-        <!--[[-->foo<!--if--><!--]]-->
-        <!--[[-->foo<!--if--><!--]]-->
-        <span></span></div>"
-      `)
-    })
-
     test('on fragment component', async () => {
       const data = ref(true)
       const { container } = await testHydration(
@@ -1644,19 +1409,31 @@ describe('Vapor Mode hydration', () => {
         data,
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><div>true</div>-true-<!--if--></div>"`,
+        `
+        "<div>
+        <!--[--><div>true</div>-true-<!--if--><!--]-->
+        </div>"
+      `,
       )
 
       data.value = false
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><!--if--></div>"`,
+        `
+        "<div>
+        <!--[--><!--if--><!--]-->
+        </div>"
+      `,
       )
 
       data.value = true
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><div>true</div>-true-<!--if--></div>"`,
+        `
+        "<div>
+        <!--[--><div>true</div>-true-<!--if--><!--]-->
+        </div>"
+      `,
       )
     })
 
@@ -1678,7 +1455,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>true</div>-true-<!--if--><!--]]-->
+        <!--[--><div>true</div>-true-<!--if--><!--]-->
         <span></span></div>"
       `,
       )
@@ -1688,7 +1465,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><!--if--><!--]]-->
+        <!--[--><!--if--><!--]-->
         <span></span></div>"
       `,
       )
@@ -1697,7 +1474,7 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
         "<div><span></span>
-        <!--[[--><div>true</div>-true-<!--if--><!--]]-->
+        <!--[--><div>true</div>-true-<!--if--><!--]-->
         <span></span></div>"
       `)
     })
@@ -1721,8 +1498,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><div>true</div>-true-<!--if--><!--]]-->
-        <!--[[--><div>true</div>-true-<!--if--><!--]]-->
+        <!--[--><div>true</div>-true-<!--if--><!--]-->
+        <!--[--><div>true</div>-true-<!--if--><!--]-->
         <span></span></div>"
       `,
       )
@@ -1732,8 +1509,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><!--if--><!--]]-->
-        <!--[[--><!--if--><!--]]-->
+        <!--[--><!--if--><!--]-->
+        <!--[--><!--if--><!--]-->
         <span></span></div>"
       `,
       )
@@ -1742,8 +1519,8 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
         "<div><span></span>
-        <!--[[--><div>true</div>-true-<!--if--><!--]]-->
-        <!--[[--><div>true</div>-true-<!--if--><!--]]-->
+        <!--[--><div>true</div>-true-<!--if--><!--]-->
+        <!--[--><div>true</div>-true-<!--if--><!--]-->
         <span></span></div>"
       `)
     })
@@ -1762,30 +1539,20 @@ describe('Vapor Mode hydration', () => {
         data,
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[-->foo<!--dynamic-component--><!--if--><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span>foo<!--dynamic-component--><!--if--><span></span></div>"`,
       )
 
       data.value = false
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `
-        "<div><span></span>
-        <!--[[--><!--if--><!--]]-->
-        <span></span></div>"
-      `,
+        `"<div><span></span><!--if--><span></span></div>"`,
       )
 
       data.value = true
       await nextTick()
-      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
-        "<div><span></span>
-        <!--[[-->foo<!--dynamic-component--><!--if--><!--]]-->
-        <span></span></div>"
-      `)
+      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
+        `"<div><span></span>foo<!--dynamic-component--><!--if--><span></span></div>"`,
+      )
     })
   })
 
@@ -1799,13 +1566,21 @@ describe('Vapor Mode hydration', () => {
         ref(['a', 'b', 'c']),
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<span>a</span><span>b</span><span>c</span><!--for-->"`,
+        `
+        "
+        <!--[--><span>a</span><span>b</span><span>c</span><!--]-->
+        "
+      `,
       )
 
       data.value.push('d')
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<span>a</span><span>b</span><span>c</span><span>d</span><!--for-->"`,
+        `
+        "
+        <!--[--><span>a</span><span>b</span><span>c</span><span>d</span><!--]-->
+        "
+      `,
       )
     })
 
@@ -1823,13 +1598,25 @@ describe('Vapor Mode hydration', () => {
         ref(['a', 'b', 'c']),
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><span>a</span><span>b</span><span>c</span><!--for--></div><div>3</div>"`,
+        `
+        "
+        <!--[--><div>
+        <!--[--><span>a</span><span>b</span><span>c</span><!--]-->
+        </div><div>3</div><!--]-->
+        "
+      `,
       )
 
       data.value.push('d')
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><span>a</span><span>b</span><span>c</span><span>d</span><!--for--></div><div>4</div>"`,
+        `
+        "
+        <!--[--><div>
+        <!--[--><span>a</span><span>b</span><span>c</span><span>d</span><!--]-->
+        </div><div>4</div><!--]-->
+        "
+      `,
       )
     })
 
@@ -1848,7 +1635,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><span>a</span><span>b</span><span>c</span><!--for--><!--]]-->
+        <!--[--><span>a</span><span>b</span><span>c</span><!--]-->
         <span></span></div>"
       `,
       )
@@ -1858,7 +1645,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><span>a</span><span>b</span><span>c</span><span>d</span><!--for--><!--]]-->
+        <!--[--><span>a</span><span>b</span><span>c</span><span>d</span><!--]-->
         <span></span></div>"
       `,
       )
@@ -1868,7 +1655,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><span>b</span><span>c</span><span>d</span><!--for--><!--]]-->
+        <!--[--><span>b</span><span>c</span><span>d</span><!--]-->
         <span></span></div>"
       `,
       )
@@ -1890,8 +1677,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><span>a</span><span>b</span><span>c</span><!--for--><!--]]-->
-        <!--[[--><span>a</span><span>b</span><span>c</span><!--for--><!--]]-->
+        <!--[--><span>a</span><span>b</span><span>c</span><!--]-->
+        <!--[--><span>a</span><span>b</span><span>c</span><!--]-->
         <span></span></div>"
       `,
       )
@@ -1901,8 +1688,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><span>a</span><span>b</span><span>c</span><span>d</span><!--for--><!--]]-->
-        <!--[[--><span>a</span><span>b</span><span>c</span><span>d</span><!--for--><!--]]-->
+        <!--[--><span>a</span><span>b</span><span>c</span><span>d</span><!--]-->
+        <!--[--><span>a</span><span>b</span><span>c</span><span>d</span><!--]-->
         <span></span></div>"
       `,
       )
@@ -1912,8 +1699,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[--><span>c</span><span>d</span><!--for--><!--]]-->
-        <!--[[--><span>c</span><span>d</span><!--for--><!--]]-->
+        <!--[--><span>c</span><span>d</span><!--]-->
+        <!--[--><span>c</span><span>d</span><!--]-->
         <span></span></div>"
       `,
       )
@@ -1933,13 +1720,21 @@ describe('Vapor Mode hydration', () => {
       )
 
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><div>comp</div><div>comp</div><div>comp</div><!--for--></div>"`,
+        `
+        "<div>
+        <!--[--><div>comp</div><div>comp</div><div>comp</div><!--]-->
+        </div>"
+      `,
       )
 
       data.value.push('d')
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><div>comp</div><div>comp</div><div>comp</div><div>comp</div><!--for--></div>"`,
+        `
+        "<div>
+        <!--[--><div>comp</div><div>comp</div><div>comp</div><div>comp</div><!--]-->
+        </div>"
+      `,
       )
     })
 
@@ -1960,12 +1755,12 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
+        <!--[-->
         <!--[--><span>a</span><!--]-->
-        <!--slot-->
         <!--[--><span>b</span><!--]-->
-        <!--slot-->
         <!--[--><span>c</span><!--]-->
-        <!--slot--><!--for--></div>"
+        <!--]-->
+        </div>"
       `,
       )
 
@@ -1974,12 +1769,12 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
+        <!--[-->
         <!--[--><span>a</span><!--]-->
-        <!--slot-->
         <!--[--><span>b</span><!--]-->
-        <!--slot-->
-        <!--[--><span>c</span><!--]-->
-        <!--slot--><span>d</span><!--slot--><!--for--></div>"
+        <!--[--><span>c</span><span>d</span><!--slot--><!--]-->
+        <!--]-->
+        </div>"
       `,
       )
     })
@@ -1997,13 +1792,29 @@ describe('Vapor Mode hydration', () => {
         ref(['a', 'b', 'c']),
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><div>foo</div>-bar-<div>foo</div>-bar-<div>foo</div>-bar-<!--for--></div>"`,
+        `
+        "<div>
+        <!--[-->
+        <!--[--><div>foo</div>-bar-<!--]-->
+        <!--[--><div>foo</div>-bar-<!--]-->
+        <!--[--><div>foo</div>-bar-<!--]-->
+        <!--]-->
+        </div>"
+      `,
       )
 
       data.value.push('d')
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
-        `"<div><div>foo</div>-bar-<div>foo</div>-bar-<div>foo</div>-bar-<div>foo</div>-bar-<!--for--></div>"`,
+        `
+        "<div>
+        <!--[-->
+        <!--[--><div>foo</div>-bar-<!--]-->
+        <!--[--><div>foo</div>-bar-<!--]-->
+        <!--[--><div>foo</div>-bar-<div>foo</div>-bar-<!--]-->
+        <!--]-->
+        </div>"
+      `,
       )
     })
   })
@@ -2024,7 +1835,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--><span>foo</span><!--]-->
-        <!--slot-->"
+        "
       `,
       )
 
@@ -2034,7 +1845,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--><span>bar</span><!--]-->
-        <!--slot-->"
+        "
       `,
       )
     })
@@ -2055,10 +1866,11 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "
+        <!--[-->
         <!--[--><!--]-->
-        <!--slot-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot-->"
+        <!--]-->
+        "
       `,
       )
 
@@ -2067,10 +1879,11 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "
+        <!--[-->
         <!--[--><!--]-->
-        <!--slot-->
         <!--[--><span>bar</span><!--]-->
-        <!--slot-->"
+        <!--]-->
+        "
       `,
       )
     })
@@ -2092,7 +1905,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--><span>foo</span><!--]-->
-        <!--slot-->"
+        "
       `,
       )
 
@@ -2102,7 +1915,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--><!--]-->
-        <!--slot-->"
+        "
       `,
       )
 
@@ -2110,8 +1923,8 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
         "
-        <!--[--><!--]-->
-        <span>true</span><!--slot-->"
+        <!--[--><span>true</span><!--]-->
+        "
       `)
     })
 
@@ -2136,8 +1949,10 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "
-        <!--[--><span>a</span><span>b</span><span>c</span><!--for--><!--]-->
-        <!--slot-->"
+        <!--[-->
+        <!--[--><span>a</span><span>b</span><span>c</span><!--]-->
+        <!--]-->
+        "
       `,
       )
 
@@ -2146,8 +1961,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "
+        <!--[-->
         <!--[--><!--]-->
-        <!--slot-->"
+        "
       `,
       )
 
@@ -2156,8 +1972,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "
-        <!--[--><!--]-->
-        <span>a</span><span>b</span><span>c</span><!--for--><!--slot-->"
+        <!--[-->
+        <!--[--><span>a</span><span>b</span><span>c</span><!--for--><!--]-->
+        "
       `,
       )
     })
@@ -2179,7 +1996,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--><span></span><span>foo</span><span></span><!--]-->
-        <!--slot-->"
+        "
       `,
       )
 
@@ -2189,7 +2006,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--><span></span><span>bar</span><span></span><!--]-->
-        <!--slot-->"
+        "
       `,
       )
     })
@@ -2216,9 +2033,11 @@ describe('Vapor Mode hydration', () => {
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div></div><div></div>
+        "
+        <!--[--><div></div><div></div>
         <!--[--><span></span><span>foo</span><span></span><!--]-->
-        <!--slot--><div></div>"
+        <div></div><!--]-->
+        "
       `,
       )
 
@@ -2226,9 +2045,11 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div></div><div></div>
+        "
+        <!--[--><div></div><div></div>
         <!--[--><span></span><span>bar</span><span></span><!--]-->
-        <!--slot--><div></div>"
+        <div></div><!--]-->
+        "
       `,
       )
     })
@@ -2253,9 +2074,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
         hi</div>"
       `,
       )
@@ -2265,9 +2084,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
         bar</div>"
       `,
       )
@@ -2292,9 +2109,11 @@ describe('Vapor Mode hydration', () => {
 
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "foo
+        "
+        <!--[-->foo
         <!--[--><span>foo</span><!--]-->
-        <!--slot-->hi"
+        hi<!--]-->
+        "
       `,
       )
 
@@ -2302,9 +2121,11 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "foo
+        "
+        <!--[-->foo
         <!--[--><span>foo</span><!--]-->
-        <!--slot-->bar"
+        bar<!--]-->
+        "
       `,
       )
     })
@@ -2330,12 +2151,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[-->
         <!--[--><span>bar</span><!--]-->
-        <!--slot--><!--]]-->
         <div>hi</div></div>"
       `,
       )
@@ -2345,12 +2162,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[-->
         <!--[--><span>bar</span><!--]-->
-        <!--slot--><!--]]-->
         <div>bar</div></div>"
       `,
       )
@@ -2376,9 +2189,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
         <div>hi</div></div>"
       `,
       )
@@ -2388,9 +2199,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
         <div>bar</div></div>"
       `,
       )
@@ -2425,13 +2234,9 @@ describe('Vapor Mode hydration', () => {
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div>
-        <!--[[--><div>bar</div><!--]]-->
-        <!--[[-->
+        "<div><div>bar</div>
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[--><div>bar</div><!--]]-->
-        </div>"
+        <div>bar</div></div>"
       `,
       )
 
@@ -2439,13 +2244,9 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div>
-        <!--[[--><div>hello</div><!--]]-->
-        <!--[[-->
+        "<div><div>hello</div>
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[--><div>hello</div><!--]]-->
-        </div>"
+        <div>hello</div></div>"
       `,
       )
     })
@@ -2480,11 +2281,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[--><div>foo</div> bar<!--]]-->
-        <!--[[-->
+        <!--[--><div>foo</div> bar<!--]-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[--><div>foo</div> bar<!--]]-->
+        <!--[--><div>foo</div> bar<!--]-->
         </div>"
       `,
       )
@@ -2495,11 +2294,9 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[--><div>hello</div> vapor<!--]]-->
-        <!--[[-->
+        <!--[--><div>hello</div> vapor<!--]-->
         <!--[--><span>hello</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[--><div>hello</div> vapor<!--]]-->
+        <!--[--><div>hello</div> vapor<!--]-->
         </div>"
       `,
       )
@@ -2529,9 +2326,11 @@ describe('Vapor Mode hydration', () => {
 
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div>foo</div><!--if-->
+        "
+        <!--[--><div>foo</div><!--if-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><div>foo</div><!--if-->"
+        <div>foo</div><!--if--><!--]-->
+        "
       `,
       )
 
@@ -2539,18 +2338,22 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<!--if-->
+        "
+        <!--[--><!--if-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--if-->"
+        <!--if--><!--]-->
+        "
       `,
       )
 
       data.show = true
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
-        "<div>foo</div><!--if-->
+        "
+        <!--[--><div>foo</div><!--if-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><div>foo</div><!--if-->"
+        <div>foo</div><!--if--><!--]-->
+        "
       `)
     })
 
@@ -2578,9 +2381,13 @@ describe('Vapor Mode hydration', () => {
 
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div>a</div><div>b</div><div>c</div><!--for-->
+        "
+        <!--[-->
+        <!--[--><div>a</div><div>b</div><div>c</div><!--]-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><div>a</div><div>b</div><div>c</div><!--for-->"
+        <!--[--><div>a</div><div>b</div><div>c</div><!--]-->
+        <!--]-->
+        "
       `,
       )
 
@@ -2588,9 +2395,13 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div>a</div><div>b</div><div>c</div><div>d</div><!--for-->
+        "
+        <!--[-->
+        <!--[--><div>a</div><div>b</div><div>c</div><div>d</div><!--]-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><div>a</div><div>b</div><div>c</div><div>d</div><!--for-->"
+        <!--[--><div>a</div><div>b</div><div>c</div><div>d</div><!--]-->
+        <!--]-->
+        "
       `,
       )
     })
@@ -2619,10 +2430,11 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "
+        <!--[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot-->
         <!--[--><span>bar</span><!--]-->
-        <!--slot-->"
+        <!--]-->
+        "
       `,
       )
 
@@ -2632,10 +2444,11 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "
+        <!--[-->
         <!--[--><span>hello</span><!--]-->
-        <!--slot-->
         <!--[--><span>vapor</span><!--]-->
-        <!--slot-->"
+        <!--]-->
+        "
       `,
       )
     })
@@ -2671,12 +2484,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[-->
         <!--[--><span>bar</span><!--]-->
-        <!--slot--><!--]]-->
         <span></span></div>"
       `,
       )
@@ -2687,12 +2496,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div><span></span>
-        <!--[[-->
         <!--[--><span>hello</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[-->
         <!--[--><span>vapor</span><!--]-->
-        <!--slot--><!--]]-->
         <span></span></div>"
       `,
       )
@@ -2731,12 +2536,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[-->
         <!--[--><span>bar</span><!--]-->
-        <!--slot--><!--]]-->
         <div>baz</div></div>"
       `,
       )
@@ -2747,12 +2548,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
         <!--[--><span>hello</span><!--]-->
-        <!--slot--><!--]]-->
-        <!--[[-->
         <!--[--><span>vapor</span><!--]-->
-        <!--slot--><!--]]-->
         <div>baz</div></div>"
       `,
       )
@@ -2776,7 +2573,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--><span>foo</span><!--]-->
-        <!--slot-->"
+        "
       `,
       )
 
@@ -2786,7 +2583,7 @@ describe('Vapor Mode hydration', () => {
         `
         "
         <!--[--><span>bar</span><!--]-->
-        <!--slot-->"
+        "
       `,
       )
     })
@@ -2813,13 +2610,11 @@ describe('Vapor Mode hydration', () => {
       )
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div>
-        <!--[[--><div><div>
+        "<div><div><div>
         <!--[-->
         <!--[--><span>foo</span><!--]-->
-        <!--slot--><!--]-->
-        <!--slot--></div></div><!--]]-->
-        <div>bar</div></div>"
+        <!--]-->
+        </div></div><div>bar</div></div>"
       `,
       )
 
@@ -2828,13 +2623,11 @@ describe('Vapor Mode hydration', () => {
       await nextTick()
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
-        "<div>
-        <!--[[--><div><div>
+        "<div><div><div>
         <!--[-->
         <!--[--><span>foo1</span><!--]-->
-        <!--slot--><!--]-->
-        <!--slot--></div></div><!--]]-->
-        <div>bar1</div></div>"
+        <!--]-->
+        </div></div><div>bar1</div></div>"
       `,
       )
     })
@@ -2856,8 +2649,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[--><!--slot-->foo<!--]-->
-        <!--slot--></div>"
+        <!--[-->foo<!--]-->
+        </div>"
       `,
       )
 
@@ -2866,8 +2659,8 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[--><!--slot-->foo1<!--]-->
-        <!--slot--></div>"
+        <!--[-->foo1<!--]-->
+        </div>"
       `,
       )
     })
@@ -2915,9 +2708,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
-        <!--[--><!--slot--><!--slot--><!--slot--><!--]-->
-        <!--slot--><!--]]-->
+        <!--[--><!--]-->
         <div>foo</div></div>"
       `,
       )
@@ -2927,9 +2718,7 @@ describe('Vapor Mode hydration', () => {
       expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
         `
         "<div>
-        <!--[[-->
-        <!--[--><!--slot--><!--slot--><!--slot--><!--]-->
-        <!--slot--><!--]]-->
+        <!--[--><!--]-->
         <div>bar</div></div>"
       `,
       )
@@ -3155,7 +2944,7 @@ describe('VDOM interop', () => {
       `
       "
       <!--[--><span>true vapor fallback</span><!--]-->
-      <!--slot-->"
+      "
     `,
     )
 
@@ -3165,7 +2954,7 @@ describe('VDOM interop', () => {
       `
       "
       <!--[--><span>false vapor fallback</span><!--]-->
-      <!--slot-->"
+      "
     `,
     )
   })
@@ -3197,7 +2986,7 @@ describe('VDOM interop', () => {
       `
       "<div>
       <!--[-->true<!--]-->
-      <!--slot--></div>"
+      </div>"
     `,
     )
 
@@ -3207,7 +2996,7 @@ describe('VDOM interop', () => {
       `
       "<div>
       <!--[-->false<!--]-->
-      <!--slot--></div>"
+      </div>"
     `,
     )
   })
@@ -3242,7 +3031,7 @@ describe('VDOM interop', () => {
       `
       "<div>
       <!--[--><div><div>true</div></div><!--]-->
-      <!--slot--></div>"
+      </div>"
     `,
     )
 
@@ -3254,7 +3043,7 @@ describe('VDOM interop', () => {
       `
       "<div>
       <!--[--><div><div>false</div></div><!--]-->
-      <!--slot--></div>"
+      </div>"
     `,
     )
   })
index 659f8487a0f0f760a4a1c33666aaa6efba38aeca..cc7bd6aaf2fc778eb58bbb95752058efeaae17c5 100644 (file)
@@ -9,7 +9,6 @@ import {
   insertionParent,
   resetInsertionState,
 } from './insertionState'
-import { DYNAMIC_COMPONENT_ANCHOR_LABEL } from '@vue/shared'
 import { advanceHydrationNode, isHydrating } from './dom/hydration'
 import { DynamicFragment, type VaporFragment } from './fragment'
 
@@ -27,7 +26,7 @@ export function createDynamicComponent(
 
   const frag =
     isHydrating || __DEV__
-      ? new DynamicFragment(DYNAMIC_COMPONENT_ANCHOR_LABEL)
+      ? new DynamicFragment('dynamic-component')
       : new DynamicFragment()
 
   renderEffect(() => {
index 7a9342e909887fdb29dd9778b16e0afa11520df4..7954dcb05609b0ef6104ab262f81cf41c5715237 100644 (file)
@@ -11,7 +11,7 @@ import {
   toReadonly,
   watch,
 } from '@vue/reactivity'
-import { FOR_ANCHOR_LABEL, isArray, isObject, isString } from '@vue/shared'
+import { isArray, isObject, isString } from '@vue/shared'
 import { createComment, createTextNode } from './dom/node'
 import {
   type Block,
@@ -27,9 +27,8 @@ import { renderEffect } from './renderEffect'
 import { VaporVForFlags } from '../../shared/src/vaporFlags'
 import {
   advanceHydrationNode,
-  currentHydrationNode,
   isHydrating,
-  locateFragmentAnchor,
+  locateFragmentEndAnchor,
   locateHydrationNode,
 } from './dom/hydration'
 import { ForFragment, VaporFragment } from './fragment'
@@ -135,10 +134,8 @@ export const createFor = (
       }
 
       if (isHydrating) {
-        parentAnchor = locateFragmentAnchor(
-          currentHydrationNode!,
-          FOR_ANCHOR_LABEL,
-        )!
+        parentAnchor = locateFragmentEndAnchor()!
+        // TODO: special handling vFor not render as a fragment. (inside Transition/TransitionGroup)
         if (__DEV__ && !parentAnchor) {
           throw new Error(`v-for fragment anchor node was not found.`)
         }
index d9432b497e9c958b1b1fa56b98cc00bfff977742..2183cb9677f3a78acdf90a9210cb0ddd10b074c6 100644 (file)
@@ -1,4 +1,3 @@
-import { IF_ANCHOR_LABEL } from '@vue/shared'
 import { type Block, type BlockFn, insert } from './block'
 import { advanceHydrationNode, isHydrating } from './dom/hydration'
 import {
@@ -24,9 +23,7 @@ export function createIf(
     frag = condition() ? b1() : b2 ? b2() : []
   } else {
     frag =
-      isHydrating || __DEV__
-        ? new DynamicFragment(IF_ANCHOR_LABEL)
-        : new DynamicFragment()
+      isHydrating || __DEV__ ? new DynamicFragment('if') : new DynamicFragment()
     renderEffect(() => (frag as DynamicFragment).update(condition() ? b1 : b2))
   }
 
index 04b0a4881cbff58337b95aed838bf5d80ad7c350..75679004c38e236069d3394f8c45e6ac9ecf1299 100644 (file)
@@ -19,6 +19,7 @@ import {
   type VaporFragment,
   isFragment,
 } from './fragment'
+import { child } from './dom/node'
 
 export interface TransitionOptions {
   $key?: any
@@ -71,7 +72,7 @@ export function insert(
   anchor: Node | null | 0 = null, // 0 means prepend
   parentSuspense?: any, // TODO Suspense
 ): void {
-  anchor = anchor === 0 ? parent.firstChild : anchor
+  anchor = anchor === 0 ? child(parent) : anchor
   if (block instanceof Node) {
     if (!isHydrating) {
       // only apply transition on Element nodes
index 1e8b30128e1e49058dedc8b38d6550c562847887..52ccf18cae79b429df8bdc24100eedc1593c8a16 100644 (file)
@@ -1,11 +1,4 @@
-import {
-  EMPTY_OBJ,
-  NO,
-  SLOT_ANCHOR_LABEL,
-  hasOwn,
-  isArray,
-  isFunction,
-} from '@vue/shared'
+import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
 import { type Block, type BlockFn, insert, setScopeId } from './block'
 import { rawPropsProxyHandlers } from './componentProps'
 import { currentInstance, isRef } from '@vue/runtime-dom'
@@ -142,7 +135,7 @@ export function createSlot(
   } else {
     fragment =
       isHydrating || __DEV__
-        ? new DynamicFragment(SLOT_ANCHOR_LABEL)
+        ? new DynamicFragment('slot')
         : new DynamicFragment()
     const isDynamicName = isFunction(name)
     const renderSlot = () => {
index eeb5c8590f7fdce415a6fddd563b9ff2de9013d0..aab0e76e6c38a799094063add92a8f784f5988a2 100644 (file)
@@ -1,18 +1,17 @@
 import { warn } from '@vue/runtime-dom'
 import {
+  type ChildItem,
+  getHydrationState,
   insertionAnchor,
   insertionParent,
   resetInsertionState,
   setInsertionState,
 } from '../insertionState'
 import {
-  _child,
-  _next,
   createTextNode,
   disableHydrationNodeLookup,
   enableHydrationNodeLookup,
 } from './node'
-import { BLOCK_ANCHOR_END_LABEL, BLOCK_ANCHOR_START_LABEL } from '@vue/shared'
 
 const isHydratingStack = [] as boolean[]
 export let isHydrating = false
@@ -30,7 +29,7 @@ function performHydration<T>(
     locateHydrationNode = locateHydrationNodeImpl
     // optimize anchor cache lookup
     ;(Comment.prototype as any).$fe = undefined
-    ;(Node.prototype as any).$lbn = undefined
+    ;(Node.prototype as any).$idx = undefined
     isOptimized = true
   }
   enableHydrationNodeLookup()
@@ -135,14 +134,48 @@ function adoptTemplateImpl(node: Node, template: string): Node | null {
 function locateHydrationNodeImpl(): void {
   let node: Node | null
   if (insertionAnchor !== undefined) {
-    // prepend / insert / append
-    node = insertionParent!.$lbn = locateNextBlockNode(
-      insertionParent!.$lbn || _child(insertionParent!),
-    )!
+    const hydrationState = getHydrationState(insertionParent!)!
+    const {
+      prevDynamicCount,
+      logicalChildren,
+      appendAnchor,
+      insertionAnchors,
+    } = hydrationState
+    // prepend
+    if (insertionAnchor === 0) {
+      node = logicalChildren[prevDynamicCount]
+    }
+    // insert
+    else if (insertionAnchor instanceof Node) {
+      const seen =
+        (insertionAnchors && insertionAnchors.get(insertionAnchor)) || 0
+      node = seen
+        ? logicalChildren[(insertionAnchor as ChildItem).$idx + seen]
+        : insertionAnchor
+
+      hydrationState.insertionAnchors = (
+        hydrationState.insertionAnchors || new Map()
+      ).set(insertionAnchor, seen + 1)
+    }
+    // append
+    else {
+      if (appendAnchor) {
+        node = logicalChildren[(appendAnchor as ChildItem).$idx + 1]
+      } else {
+        node =
+          insertionAnchor === null
+            ? logicalChildren[0]
+            : // insertionAnchor is a number > 0
+              // indicates how many static nodes precede the node to append
+              logicalChildren[prevDynamicCount + insertionAnchor]
+      }
+      hydrationState.appendAnchor = node
+    }
+    hydrationState.prevDynamicCount++
   } else {
     node = currentHydrationNode
     if (insertionParent && (!node || node.parentNode !== insertionParent)) {
-      node = _child(insertionParent)
+      node = insertionParent.firstChild
     }
   }
 
@@ -181,43 +214,11 @@ export function locateEndAnchor(
   return null
 }
 
-export function locateFragmentAnchor(
-  node: Node,
-  label: string,
-): Comment | null {
-  while (node && node.nodeType === 8) {
+export function locateFragmentEndAnchor(label: string = ']'): Comment | null {
+  let node = currentHydrationNode!
+  while (node) {
     if (isComment(node, label)) return node
     node = node.nextSibling!
   }
   return null
 }
-
-function locateNextBlockNode(node: Node): Node | null {
-  while (node) {
-    if (isComment(node, BLOCK_ANCHOR_START_LABEL)) return node.nextSibling
-    node = node.nextSibling!
-  }
-
-  if (__DEV__) {
-    throw new Error(
-      `Could not locate hydration node with anchor label: ${BLOCK_ANCHOR_START_LABEL}`,
-    )
-  }
-  return null
-}
-
-export function advanceToNonBlockNode(node: Node): Node {
-  while (node) {
-    if (isComment(node, BLOCK_ANCHOR_START_LABEL)) {
-      node = locateEndAnchor(
-        node,
-        BLOCK_ANCHOR_START_LABEL,
-        BLOCK_ANCHOR_END_LABEL,
-      )!
-      continue
-    }
-
-    break
-  }
-  return node
-}
index fe267f27b95dd41b8ce546c9963b44621d99cf93..6a99e95bac5433741730da1ac1bdf7ad2aac74f9 100644 (file)
@@ -1,33 +1,37 @@
-import { advanceToNonBlockNode } from './hydration'
-import { isBlockStartAnchor } from '@vue/shared'
+/* @__NO_SIDE_EFFECTS__ */
+
+import {
+  type ChildItem,
+  getHydrationState,
+  getTemplateChildren,
+} from '../insertionState'
 
-/*! #__NO_SIDE_EFFECTS__ */
 export function createElement(tagName: string): HTMLElement {
   return document.createElement(tagName)
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function createTextNode(value = ''): Text {
   return document.createTextNode(value)
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function createComment(data: string): Comment {
   return document.createComment(data)
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function querySelector(selectors: string): Element | null {
   return document.querySelector(selectors)
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 const _txt: typeof _child = _child
 
 /**
  * Hydration-specific version of `child`.
  */
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 const __txt: typeof __child = (node: ParentNode): Node => {
   let n = node.firstChild!
 
@@ -41,54 +45,72 @@ const __txt: typeof __child = (node: ParentNode): Node => {
   return n
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function _child(node: ParentNode): Node {
-  return node.firstChild!
+  const templateChildren = getTemplateChildren(node)
+  return templateChildren ? templateChildren[0] : node.firstChild!
 }
 
 /**
  * Hydration-specific version of `child`.
  */
-/*! #__NO_SIDE_EFFECTS__ */
-export function __child(node: ParentNode): Node {
-  let n: Node = node.firstChild!
-  while (n && isBlockStartAnchor(n)) {
-    n = advanceToNonBlockNode(n)
-    n = n.nextSibling!
-  }
-
-  return n
+/* @__NO_SIDE_EFFECTS__ */
+export function __child(node: ParentNode & { $lpn?: Node }): Node {
+  return __nthChild(node, 0)!
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function _nthChild(node: Node, i: number): Node {
-  return node.childNodes[i]
+  const templateChildren = getTemplateChildren(node as ParentNode)
+  return templateChildren ? templateChildren[i] : node.childNodes[i]
 }
 
 /**
  * Hydration-specific version of `nthChild`.
  */
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function __nthChild(node: Node, i: number): Node {
-  let n = __child(node as ParentNode)
-  for (let start = 0; start < i; start++) {
-    n = __next(n) as ChildNode
+  const hydrationState = getHydrationState(node as ParentNode)
+  if (hydrationState) {
+    const { prevDynamicCount, insertionAnchors, logicalChildren } =
+      hydrationState
+    // prevDynamicCount tracks how many dynamic nodes have been processed
+    // so far (prepend/insert/append).
+    // For anchor-based insert, the first time an anchor is used we adopt the
+    // anchor node itself and do NOT consume the next child in `logicalChildren`,
+    // yet prevDynamicCount is still incremented. This overcounts the base
+    // offset by 1 per unique anchor that has appeared.
+    // insertionAnchors.size equals the number of unique anchors seen, so we
+    // subtract it to neutralize those "first-use doesn't consume" cases:
+    //   base = prevDynamicCount - insertionAnchors.size
+    // Then index from this base: logicalChildren[base + i].
+    const size = insertionAnchors ? insertionAnchors.size : 0
+    return logicalChildren[prevDynamicCount - size + i]
   }
-  return n
+  return node.childNodes[i]
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function _next(node: Node): Node {
-  return node.nextSibling!
+  const templateChildren = getTemplateChildren(node.parentNode!)
+  return templateChildren
+    ? templateChildren[(node as ChildItem).$idx + 1]
+    : node.nextSibling!
 }
 
 /**
  * Hydration-specific version of `next`.
  */
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function __next(node: Node): Node {
-  if (isBlockStartAnchor(node)) {
-    node = advanceToNonBlockNode(node)
+  const hydrationState = getHydrationState(node.parentNode!)
+  if (hydrationState) {
+    const { logicalChildren, insertionAnchors } = hydrationState
+    const seenCount = (insertionAnchors && insertionAnchors.get(node)) || 0
+    // If node is used as an anchor, the first hydration uses node itself,
+    // but seenCount increases, so here needs -1
+    const insertedNodesCount = seenCount === 0 ? 0 : seenCount - 1
+    return logicalChildren[(node as ChildItem).$idx + insertedNodesCount + 1]
   }
   return node.nextSibling!
 }
@@ -97,25 +119,25 @@ type DelegatedFunction<T extends (...args: any[]) => any> = T & {
   impl: T
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export const txt: DelegatedFunction<typeof _txt> = node => {
   return txt.impl(node)
 }
 txt.impl = _child
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export const child: DelegatedFunction<typeof _child> = node => {
   return child.impl(node)
 }
 child.impl = _child
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export const next: DelegatedFunction<typeof _next> = node => {
   return next.impl(node)
 }
 next.impl = _next
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export const nthChild: DelegatedFunction<typeof _nthChild> = (node, i) => {
   return nthChild.impl(node, i)
 }
index e00e9d213f4d70d49082e4777407f4aa43b5082e..e2acbc96e66837cc8e6a1ad02829892f134dfdd1 100644 (file)
@@ -18,6 +18,7 @@ export function template(html: string, root?: boolean) {
       if (root) (adopted as any).$root = true
       return adopted
     }
+
     // fast path for text nodes
     if (html[0] !== '<') {
       return createTextNode(html)
index e349196e700d6ce8af062a9c3594106d28eaf1fe..da92f6b4eca260cc16d0a7a0a206ee90d6fbc24b 100644 (file)
@@ -12,16 +12,16 @@ import {
 import type { TransitionHooks } from '@vue/runtime-dom'
 import {
   advanceHydrationNode,
-  currentHydrationNode,
   isHydrating,
-  locateFragmentAnchor,
+  locateFragmentEndAnchor,
   locateHydrationNode,
 } from './dom/hydration'
 import {
   applyTransitionHooks,
   applyTransitionLeaveHooks,
 } from './components/Transition'
-import type { VaporComponentInstance } from './component'
+import { type VaporComponentInstance, isVaporComponent } from './component'
+import { isArray } from '@vue/shared'
 
 export class VaporFragment<T extends Block = Block>
   implements TransitionOptions
@@ -74,7 +74,7 @@ export class DynamicFragment extends VaporFragment {
 
   update(render?: BlockFn, key: any = render): void {
     if (key === this.current) {
-      if (isHydrating) this.hydrate(this.anchorLabel!)
+      if (isHydrating) this.hydrate(true)
       return
     }
     this.current = key
@@ -139,19 +139,41 @@ export class DynamicFragment extends VaporFragment {
 
     setActiveSub(prevSub)
 
-    if (isHydrating) this.hydrate(this.anchorLabel!)
+    if (isHydrating) this.hydrate()
   }
 
-  hydrate = (label: string): void => {
-    // avoid repeated hydration during rendering fallback
+  hydrate = (isEmpty = false): void => {
+    // avoid repeated hydration during fallback rendering
     if (this.anchor) return
 
-    this.anchor = locateFragmentAnchor(currentHydrationNode!, label)!
-    if (this.anchor) {
-      advanceHydrationNode(this.anchor)
-    } else if (__DEV__) {
-      throw new Error(`${label} fragment anchor node was not found.`)
+    // reuse the empty comment node as the anchor for empty if
+    if (this.anchorLabel === 'if' && isEmpty) {
+      this.anchor = locateFragmentEndAnchor('')!
+      if (!this.anchor) {
+        throw new Error('Failed to locate if anchor')
+      } else {
+        ;(this.anchor as Comment).data = this.anchorLabel
+        return
+      }
+    }
+
+    // reuse the vdom fragment end anchor for slots
+    if (this.anchorLabel === 'slot') {
+      this.anchor = locateFragmentEndAnchor()!
+      if (!this.anchor) {
+        throw new Error('Failed to locate slot anchor')
+      } else {
+        return
+      }
     }
+
+    // create an anchor
+    const { parentNode, nextSibling } = findLastChild(this)!
+    parentNode!.insertBefore(
+      (this.anchor = createComment(this.anchorLabel!)),
+      nextSibling,
+    )
+    advanceHydrationNode(this.anchor)
   }
 }
 
@@ -195,3 +217,16 @@ function findInvalidFragment(fragment: VaporFragment): VaporFragment | null {
     ? findInvalidFragment(fragment.nodes) || fragment
     : fragment
 }
+
+export function findLastChild(node: Block): Node | undefined | null {
+  if (node && node instanceof Node) {
+    return node
+  } else if (isArray(node)) {
+    return findLastChild(node[node.length - 1])
+  } else if (isVaporComponent(node)) {
+    return findLastChild(node.block!)
+  } else {
+    if (node instanceof DynamicFragment && node.anchor) return node.anchor
+    return findLastChild(node.nodes!)
+  }
+}
index f703efffd9b0cfb07ac3a07aee12e63ec597c133..bcfd900a6c2aa37dd1da70772d6a6d8a017bafbb 100644 (file)
@@ -1,11 +1,20 @@
-export let insertionParent:
-  | (ParentNode & {
-      // the last hydrated block node
-      $lbn?: Node
-    })
-  | undefined
+import { isHydrating } from './dom/hydration'
+export interface ChildItem extends ChildNode {
+  $idx: number
+}
+type HydrationState = {
+  logicalChildren: ChildItem[]
+  prevDynamicCount: number
+  insertionAnchors: Map<Node, number> | null
+  appendAnchor: Node | null
+}
+export let insertionParent: ParentNode | undefined
 export let insertionAnchor: Node | 0 | undefined | null
 
+const templateChildrenCache = new WeakMap<ParentNode, ChildItem[]>()
+
+const hydrationStateCache = new WeakMap<ParentNode, HydrationState>()
+
 /**
  * This function is called before a block type that requires insertion
  * (component, slot outlet, if, for) is created. The state is used for actual
@@ -13,12 +22,102 @@ export let insertionAnchor: Node | 0 | undefined | null
  */
 export function setInsertionState(
   parent: ParentNode,
-  anchor?: Node | 0 | null,
+  anchor?: Node | 0 | null | number,
 ): void {
   insertionParent = parent
-  insertionAnchor = anchor
+  if (isHydrating) {
+    initializeHydrationState(anchor, parent)
+  } else {
+    cacheTemplateChildren(anchor, parent)
+  }
+}
+
+function initializeHydrationState(
+  anchor: number | Node | null | undefined,
+  parent: ParentNode,
+) {
+  insertionAnchor = anchor as Node
+  if (!hydrationStateCache.has(parent)) {
+    const childNodes = parent.childNodes
+    const len = childNodes.length
+    const logicalChildren = new Array(len) as ChildItem[]
+    // Build logical children:
+    // - static node: keep the node as a child
+    // - fragment: keep only the start anchor ('<!--[-->') as a child
+    let index = 0
+    for (let i = 0; i < len; i++) {
+      const n = childNodes[i] as ChildItem
+      if (n.nodeType === 8) {
+        const data = (n as any as Comment).data
+        // vdom fragment
+        if (data === '[') {
+          n.$idx = index
+          logicalChildren[index++] = n
+          // find matching end anchor, accounting for nested fragments
+          let depth = 1
+          let j = i + 1
+          for (; j < len; j++) {
+            const c = childNodes[j] as Comment
+            if (c.nodeType === 8) {
+              const d = c.data
+              if (d === '[') depth++
+              else if (d === ']') {
+                depth--
+                if (depth === 0) break
+              }
+            }
+          }
+          // jump i to the end anchor
+          i = j
+          continue
+        }
+      }
+      n.$idx = index
+      logicalChildren[index++] = n
+    }
+    logicalChildren.length = index
+    hydrationStateCache.set(parent, {
+      logicalChildren,
+      prevDynamicCount: 0,
+      insertionAnchors: null,
+      appendAnchor: null,
+    })
+  }
+}
+
+function cacheTemplateChildren(
+  anchor: number | Node | null | undefined,
+  parent: ParentNode,
+) {
+  // special handling append anchor value to null
+  insertionAnchor =
+    typeof anchor === 'number' && anchor > 0 ? null : (anchor as Node)
+
+  if (!templateChildrenCache.has(parent)) {
+    const nodes = parent.childNodes
+    const len = nodes.length
+    const children = new Array(len)
+    for (let i = 0; i < len; i++) {
+      const node = nodes[i] as ChildItem
+      node.$idx = i
+      children[i] = node
+    }
+    templateChildrenCache.set(parent, children)
+  }
 }
 
 export function resetInsertionState(): void {
   insertionParent = insertionAnchor = undefined
 }
+
+export function getTemplateChildren(
+  parent: ParentNode,
+): ChildItem[] | undefined {
+  return templateChildrenCache.get(parent)
+}
+
+export function getHydrationState(
+  parent: ParentNode,
+): HydrationState | undefined {
+  return hydrationStateCache.get(parent)
+}
index 04a288118552130431d958f55b13ef127286c4b5..9a979c4e4c7129cef2ef6545ff08bdd7592f88d6 100644 (file)
@@ -59,7 +59,7 @@ import {
   currentHydrationNode,
   isComment,
   isHydrating,
-  locateFragmentAnchor,
+  locateFragmentEndAnchor,
   locateHydrationNode,
   setCurrentHydrationNode,
   hydrateNode as vaporHydrateNode,
@@ -198,18 +198,14 @@ const vaporInteropImpl: Omit<
     const propsRef = (vnode.vs!.ref = shallowRef(vnode.props))
     vaporHydrateNode(node, () => {
       vnode.vb = slot(new Proxy(propsRef, vaporSlotPropsProxyHandler))
-      vnode.el = vnode.anchor = locateFragmentAnchor(
-        currentHydrationNode!,
-        // locate the vdom fragment end anchor (<!--]-->), since no vapor slot
-        // anchor (<!--slot-->) is injected in vdom component
-        ']',
-      )
+      vnode.el = currentHydrationNode!
+      vnode.anchor = locateFragmentEndAnchor()
 
       if (__DEV__ && !vnode.anchor) {
-        throw new Error(`vapor slot anchor node was not found.`)
+        throw new Error(`Failed to locate slot anchor`)
       }
     })
-    return _next(node)
+    return _next(vnode.anchor as Node)
   },
 }
 
index 0e2ee5d6c213ea0addbe757f5b4a1513370f7ebe..19aa4ce63b76c1a1118fd13a4dc32c6bae35533f 100644 (file)
@@ -5,7 +5,7 @@ import {
   type SSRBufferItem,
   renderVNodeChildren,
 } from '../render'
-import { isArray, isString } from '@vue/shared'
+import { isArray } from '@vue/shared'
 
 const { ensureValidVNode } = ssrUtils
 
@@ -83,20 +83,7 @@ export function ssrRenderSlotInner(
         isEmptySlot = false
       } else {
         for (let i = 0; i < slotBuffer.length; i++) {
-          const buffer = slotBuffer[i]
-
-          // preserve empty slot anchor in vapor components
-          // DynamicFragment requires this anchor
-          if (
-            parentComponent.type.__vapor &&
-            isString(buffer) &&
-            buffer === '<!--slot-->'
-          ) {
-            push(buffer)
-            continue
-          }
-
-          if (!isComment(buffer)) {
+          if (!isComment(slotBuffer[i])) {
             isEmptySlot = false
             break
           }
index 2ff0c499f2794db1fffdfbcee710b703532f6712..221d3895e2288f80699d918f333d3631498825f2 100644 (file)
@@ -236,17 +236,11 @@ export function renderVNode(
       push(escapeHtml(children as string))
       break
     case Comment:
-      if (children) {
-        const content = children as string
-        // avoid escaping comments
-        if (content.startsWith('<!--') && content.endsWith('-->')) {
-          push(content)
-        } else {
-          push(`<!--${escapeHtmlComment(content)}-->`)
-        }
-      } else {
-        push(`<!---->`)
-      }
+      push(
+        children
+          ? `<!--${escapeHtmlComment(children as string)}-->`
+          : `<!---->`,
+      )
       break
     case Static:
       push(children as string)
diff --git a/packages/shared/src/domAnchors.ts b/packages/shared/src/domAnchors.ts
deleted file mode 100644 (file)
index 691aebd..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-export const BLOCK_ANCHOR_START_LABEL = '[['
-export const BLOCK_ANCHOR_END_LABEL = ']]'
-export const IF_ANCHOR_LABEL: string = 'if'
-export const DYNAMIC_COMPONENT_ANCHOR_LABEL: string = 'dynamic-component'
-export const FOR_ANCHOR_LABEL: string = 'for'
-export const SLOT_ANCHOR_LABEL: string = 'slot'
-
-export function isBlockStartAnchor(node: Node): node is Comment {
-  if (node.nodeType !== 8) return false
-  const data = (node as Comment).data
-  return data === `${BLOCK_ANCHOR_START_LABEL}`
-}
index 9372b8e1a96ab3f6acdf89c968bf61f18d994152..0c38d640ba0982f7a8439279624ca9f71018042a 100644 (file)
@@ -13,5 +13,4 @@ export * from './looseEqual'
 export * from './toDisplayString'
 export * from './typeUtils'
 export * from './subSequence'
-export * from './domAnchors'
 export * from './cssVars'