diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
index e4084a66fba..670ed8d17a8 100644
--- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
@@ -38,7 +38,7 @@ export function render(_ctx) {
"default": _withVaporCtx(() => {
const n0 = _createIf(() => (true), () => {
const n3 = t0()
- _setInsertionState(n3, null, 0, true)
+ _setInsertionState(n3, null, 0)
const n2 = _createComponentWithFallback(_component_Bar)
_withVaporDirectives(n2, [[_directive_hello, void 0, void 0, { world: true }]])
return n3
@@ -159,7 +159,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
const n3 = t1()
const n2 = _child(n3, 1)
_renderEffect(() => _setProp(n3, "id", _ctx.foo))
- _setInsertionState(n3, 0, 0, true)
+ _setInsertionState(n3, 0, 0)
const n1 = _createComponentWithFallback(_component_Comp)
_renderEffect(() => _setText(n2, _toDisplayString(_ctx.bar)))
return [n0, n3]
@@ -223,7 +223,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
const n5 = _next(n2, 1)
const n4 = _next(n5, 2)
const x2 = _txt(n2)
- _setInsertionState(n6, n5, 1, true)
+ _setInsertionState(n6, n5, 1)
const n3 = _createComponentWithFallback(_component_Child)
const x4 = _txt(n4)
_renderEffect(() => _setText(x4, _toDisplayString(_ctx.useId())))
@@ -246,7 +246,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
const _component_Child = _resolveComponent("Child")
const n1 = t0()
_renderEffect(() => _setProp(n1, "id", _ctx.useId()))
- _setInsertionState(n1, null, 0, true)
+ _setInsertionState(n1, null, 0)
const n0 = _createComponentWithFallback(_component_Child)
return n1
}"
@@ -280,7 +280,7 @@ export function render(_ctx) {
const n4 = _child(p0)
_setInsertionState(n6, n5, 1)
const n0 = _createComponentWithFallback(_component_Comp)
- _setInsertionState(n6, n7, 3, true)
+ _setInsertionState(n6, n7, 3)
const n1 = _createIf(() => (true), () => {
const n3 = t0()
return n3
@@ -298,9 +298,9 @@ export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n3 = t0()
const n1 = _child(n3)
- _setInsertionState(n1, null, 0, true)
+ _setInsertionState(n1, null, 0)
const n0 = _createSlot("default", null)
- _setInsertionState(n3, null, 1, true)
+ _setInsertionState(n3, null, 1)
const n2 = _createComponentWithFallback(_component_Comp)
return n3
}"
diff --git a/packages/compiler-vapor/__tests__/compile.spec.ts b/packages/compiler-vapor/__tests__/compile.spec.ts
index 4cf9ad286f1..58d852af1fd 100644
--- a/packages/compiler-vapor/__tests__/compile.spec.ts
+++ b/packages/compiler-vapor/__tests__/compile.spec.ts
@@ -266,7 +266,7 @@ describe('compile', () => {
})
expect(code).contains(
`_renderEffect(() => _setProp(n1, "id", _ctx.useId()))
- _setInsertionState(n1, null, 0, true)
+ _setInsertionState(n1, null, 0)
const n0 = _createComponentWithFallback(_component_Child)`,
)
expect(code).matchSnapshot()
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap
index ed349cf93a8..960a99fe990 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap
@@ -49,7 +49,7 @@ export function render(_ctx) {
return n5
}, () => {
const n14 = t2()
- _setInsertionState(n14, 0, 0, true)
+ _setInsertionState(n14, 0, 0)
const n9 = _createIf(() => (_ctx.c), () => {
const n11 = t1()
return n11
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/logicalIndex.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/logicalIndex.spec.ts.snap
index 201dd5e8a3f..c34ea1aa123 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/logicalIndex.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/logicalIndex.spec.ts.snap
@@ -11,7 +11,7 @@ export function render(_ctx) {
const n3 = _next(_child(n2), 2)
_setInsertionState(n2, 0, 0)
const n0 = _createComponentWithFallback(_component_Comp1)
- _setInsertionState(n2, n3, 2, true)
+ _setInsertionState(n2, n3, 2)
const n1 = _createComponentWithFallback(_component_Comp2)
return n2
}"
@@ -31,7 +31,7 @@ export function render(_ctx) {
const n0 = _createComponentWithFallback(_component_Comp1)
_setInsertionState(n3, 0, 1)
const n1 = _createComponentWithFallback(_component_Comp2)
- _setInsertionState(n3, n4, 3, true)
+ _setInsertionState(n3, n4, 3)
const n2 = _createComponentWithFallback(_component_Comp3)
return n3
}"
@@ -48,7 +48,7 @@ export function render(_ctx) {
const n4 = _next(_child(n5), 1)
_setInsertionState(n5, n4, 1)
const n0 = _createComponentWithFallback(_component_Comp)
- _setInsertionState(n5, null, 3, true)
+ _setInsertionState(n5, null, 3)
const n1 = _createIf(() => (true), () => {
const n3 = t0()
return n3
@@ -69,7 +69,7 @@ export function render(_ctx) {
const n6 = _nthChild(n5, 3, 3)
_setInsertionState(n5, n4, 1)
const n0 = _createComponentWithFallback(_component_Comp)
- _setInsertionState(n5, n6, 3, true)
+ _setInsertionState(n5, n6, 3)
const n1 = _createIf(() => (true), () => {
const n3 = t0()
return n3
@@ -88,7 +88,7 @@ export function render(_ctx) {
const n2 = t0()
_setInsertionState(n2, null, 1)
const n0 = _createComponentWithFallback(_component_Comp1)
- _setInsertionState(n2, null, 2, true)
+ _setInsertionState(n2, null, 2)
const n1 = _createComponentWithFallback(_component_Comp2)
return n2
}"
@@ -101,7 +101,7 @@ const t0 = _template("
", true)
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n1 = t0()
- _setInsertionState(n1, null, 0, true)
+ _setInsertionState(n1, null, 0)
const n0 = _createComponentWithFallback(_component_Comp)
return n1
}"
@@ -114,7 +114,7 @@ const t0 = _template("
A", true)
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n1 = t0()
- _setInsertionState(n1, null, 1, true)
+ _setInsertionState(n1, null, 1)
const n0 = _createComponentWithFallback(_component_Comp)
return n1
}"
@@ -131,7 +131,7 @@ export function render(_ctx) {
const n2 = _next(_child(n3), 1)
_setInsertionState(n3, n2, 1)
const n0 = _createComponentWithFallback(_component_Comp1)
- _setInsertionState(n3, n2, 2, true)
+ _setInsertionState(n3, n2, 2)
const n1 = _createComponentWithFallback(_component_Comp2)
return n3
}"
@@ -145,7 +145,7 @@ export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n2 = t0()
const n1 = _next(_child(n2), 1)
- _setInsertionState(n2, n1, 1, true)
+ _setInsertionState(n2, n1, 1)
const n0 = _createComponentWithFallback(_component_Comp)
return n2
}"
@@ -158,7 +158,7 @@ const t1 = _template("A", true)
export function render(_ctx) {
const n2 = t1()
- _setInsertionState(n2, null, 1, true)
+ _setInsertionState(n2, null, 1)
const n0 = _createKeyedFragment(() => (_ctx.id), () => {
const n1 = t0()
return n1
@@ -175,7 +175,7 @@ const t1 = _template("
AB", true)
export function render(_ctx) {
const n3 = t1()
const n2 = _next(_child(n3), 1)
- _setInsertionState(n3, n2, 1, true)
+ _setInsertionState(n3, n2, 1)
const n0 = _createKeyedFragment(() => (_ctx.id), () => {
const n1 = t0()
return n1
@@ -191,7 +191,7 @@ const t1 = _template("A", true)
export function render(_ctx) {
const n2 = t1()
- _setInsertionState(n2, 0, 0, true)
+ _setInsertionState(n2, 0, 0)
const n0 = _createKeyedFragment(() => (_ctx.id), () => {
const n1 = t0()
return n1
@@ -210,7 +210,7 @@ export function render(_ctx) {
const n2 = t0()
_setInsertionState(n2, 0, 0)
const n0 = _createComponentWithFallback(_component_Comp1)
- _setInsertionState(n2, null, 2, true)
+ _setInsertionState(n2, null, 2)
const n1 = _createComponentWithFallback(_component_Comp2)
return n2
}"
@@ -230,7 +230,7 @@ export function render(_ctx) {
const n0 = _createComponentWithFallback(_component_Comp1)
_setInsertionState(n3, n4, 2)
const n1 = _createComponentWithFallback(_component_Comp2)
- _setInsertionState(n3, null, 4, true)
+ _setInsertionState(n3, null, 4)
const n2 = _createComponentWithFallback(_component_Comp3)
return n3
}"
@@ -246,7 +246,7 @@ export function render(_ctx) {
const n2 = t0()
_setInsertionState(n2, 0, 0)
const n0 = _createComponentWithFallback(_component_Comp1)
- _setInsertionState(n2, 0, 1, true)
+ _setInsertionState(n2, 0, 1)
const n1 = _createComponentWithFallback(_component_Comp2)
return n2
}"
@@ -259,7 +259,7 @@ const t0 = _template("A", true)
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n1 = t0()
- _setInsertionState(n1, 0, 0, true)
+ _setInsertionState(n1, 0, 0)
const n0 = _createComponentWithFallback(_component_Comp)
return n1
}"
@@ -271,7 +271,7 @@ const t0 = _template("A", true)
export function render(_ctx) {
const n1 = t0()
- _setInsertionState(n1, null, 1, true)
+ _setInsertionState(n1, null, 1)
const n0 = _createSlot("default", null)
return n1
}"
@@ -283,7 +283,7 @@ const t0 = _template("
A", true)
export function render(_ctx) {
const n1 = t0()
- _setInsertionState(n1, 0, 0, true)
+ _setInsertionState(n1, 0, 0)
const n0 = _createSlot("default", null)
return n1
}"
@@ -296,7 +296,7 @@ const t1 = _template("A", true)
export function render(_ctx) {
const n3 = t1()
- _setInsertionState(n3, null, 1, true)
+ _setInsertionState(n3, null, 1)
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n2 = t0()
return n2
@@ -312,7 +312,7 @@ const t1 = _template("
A", true)
export function render(_ctx) {
const n3 = t1()
- _setInsertionState(n3, 0, 0, true)
+ _setInsertionState(n3, 0, 0)
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n2 = t0()
return n2
@@ -328,7 +328,7 @@ const t1 = _template("A", true)
export function render(_ctx) {
const n3 = t1()
- _setInsertionState(n3, null, 1, true)
+ _setInsertionState(n3, null, 1)
const n0 = _createIf(() => (_ctx.show), () => {
const n2 = t0()
return n2
@@ -345,7 +345,7 @@ const t1 = _template("
AB", true)
export function render(_ctx) {
const n4 = t1()
const n3 = _next(_child(n4), 1)
- _setInsertionState(n4, n3, 1, true)
+ _setInsertionState(n4, n3, 1)
const n0 = _createIf(() => (_ctx.show), () => {
const n2 = t0()
return n2
@@ -361,7 +361,7 @@ const t1 = _template("
A", true)
export function render(_ctx) {
const n3 = t1()
- _setInsertionState(n3, 0, 0, true)
+ _setInsertionState(n3, 0, 0)
const n0 = _createIf(() => (_ctx.show), () => {
const n2 = t0()
return n2
@@ -390,7 +390,7 @@ export function render(_ctx) {
const n5 = t1()
return n5
}, 5, null, 0)
- _setInsertionState(n7, null, 2, true)
+ _setInsertionState(n7, null, 2)
const n6 = _createComponentWithFallback(_component_Comp2)
return n7
}"
@@ -407,7 +407,7 @@ export function render(_ctx) {
const n6 = t2()
_setInsertionState(n6, 0, 0)
const n0 = _createComponentWithFallback(_component_Comp)
- _setInsertionState(n6, 0, 1, true)
+ _setInsertionState(n6, 0, 1)
const n1 = _createIf(() => (_ctx.show), () => {
const n3 = t0()
return n3
@@ -428,7 +428,7 @@ const t2 = _template("AB", true)
export function render(_ctx) {
const n6 = t2()
const n5 = _next(_child(n6), 1)
- _setInsertionState(n6, n5, 1, true)
+ _setInsertionState(n6, n5, 1)
const n0 = _createIf(() => (_ctx.show), () => {
const n2 = t0()
return n2
@@ -450,7 +450,7 @@ const t3 = _template("
AB", true)
export function render(_ctx) {
const n8 = t3()
const n7 = _next(_child(n8), 1)
- _setInsertionState(n8, n7, 1, true)
+ _setInsertionState(n8, n7, 1)
const n0 = _createIf(() => (_ctx.a), () => {
const n2 = t0()
return n2
@@ -473,7 +473,7 @@ const t2 = _template("
A", true)
export function render(_ctx) {
const n5 = t2()
- _setInsertionState(n5, null, 1, true)
+ _setInsertionState(n5, null, 1)
const n0 = _createIf(() => (_ctx.show), () => {
const n2 = t0()
return n2
@@ -502,7 +502,7 @@ export function render(_ctx) {
const n4 = t1()
return n4
}, 5, null, 0)
- _setInsertionState(n6, null, 2, true)
+ _setInsertionState(n6, null, 2)
const n5 = _createComponentWithFallback(_component_Comp)
return n6
}"
@@ -516,7 +516,7 @@ const t2 = _template("
A", true)
export function render(_ctx) {
const n5 = t2()
- _setInsertionState(n5, 0, 0, true)
+ _setInsertionState(n5, 0, 0)
const n0 = _createIf(() => (_ctx.show), () => {
const n2 = t0()
return n2
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap
index 2653467a6d5..ad31de3f867 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap
@@ -8,7 +8,7 @@ const t1 = _template("", true)
export function render(_ctx) {
const n4 = t1()
const n3 = _next(_child(n4), 1)
- _setInsertionState(n4, n3, 1, true)
+ _setInsertionState(n4, n3, 1)
const n0 = _createIf(() => (1), () => {
const n2 = t0()
return n2
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformKey.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformKey.spec.ts.snap
index d5f01e5c27e..1292ef742f4 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformKey.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformKey.spec.ts.snap
@@ -92,7 +92,7 @@ const t0 = _template("
", true)
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n2 = t0()
- _setInsertionState(n2, null, 0, true)
+ _setInsertionState(n2, null, 0)
const n0 = _createKeyedFragment(() => (_ctx.id), () => {
const n1 = _createComponentWithFallback(_component_Foo)
return n1
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
index e7304e39203..6146d74f762 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
@@ -87,7 +87,7 @@ const t1 = _template("
")
export function render(_ctx) {
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n5 = t1()
- _setInsertionState(n5, null, 0, true)
+ _setInsertionState(n5, null, 0)
const n2 = _createFor(() => (_for_item0.value), (_for_item1) => {
const n4 = t0()
const x4 = _txt(n4)
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap
index 00f3b1d5edd..e9c721bff4b 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap
@@ -281,7 +281,7 @@ export function render(_ctx) {
const n2 = t0()
return n2
}, null, 1)
- _setInsertionState(n8, null, 1, true)
+ _setInsertionState(n8, null, 1)
const n3 = _createIf(() => (_ctx.bar), () => {
const n5 = t1()
return n5
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
index e57903b60ac..a994ba5cb76 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
@@ -42,7 +42,7 @@ const t0 = _template("
", true)
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n1 = t0()
- _setInsertionState(n1, null, 0, true)
+ _setInsertionState(n1, null, 0)
const n0 = _createComponentWithFallback(_component_Comp, { id: () => (_ctx.foo) }, null, null, true)
return n1
}"
@@ -66,7 +66,7 @@ const t0 = _template("
", true)
export function render(_ctx) {
const n1 = t0()
- _setInsertionState(n1, null, 0, true)
+ _setInsertionState(n1, null, 0)
const n0 = _createSlot("default", null, null, null, true)
return n1
}"
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
index 0b162b38817..6d3b33ce165 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
@@ -411,7 +411,7 @@ export function render(_ctx) {
const n6 = t0()
_setInsertionState(n6, null, 0)
const n0 = _createSlot("foo", null)
- _setInsertionState(n6, null, 1, true)
+ _setInsertionState(n6, null, 1)
const n1 = _createIf(() => (true), () => {
const n3 = _createComponentWithFallback(_component_Foo)
return n3
@@ -635,7 +635,7 @@ export function render(_ctx) {
"default": _withVaporCtx(() => {
const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
const n3 = t0()
- _setInsertionState(n3, null, 0, true)
+ _setInsertionState(n3, null, 0)
const n2 = _createComponentWithFallback(_component_ChildComp)
return n3
})
@@ -657,7 +657,7 @@ export function render(_ctx) {
"default": _withVaporCtx(() => {
const n0 = _createIf(() => (_ctx.show), () => {
const n3 = t0()
- _setInsertionState(n3, null, 0, true)
+ _setInsertionState(n3, null, 0)
const n2 = _createComponentWithFallback(_component_ChildComp)
return n3
}, null, 1)
@@ -694,7 +694,7 @@ export function render(_ctx) {
"default": _withVaporCtx(() => {
const n0 = _createIf(() => (_ctx.show), () => {
const n3 = t0()
- _setInsertionState(n3, null, 0, true)
+ _setInsertionState(n3, null, 0)
const n2 = _createPlainElement("my-element")
return n3
}, null, 1)
@@ -732,10 +732,10 @@ export function render(_ctx) {
"default": _withVaporCtx(() => {
const n0 = _createIf(() => (_ctx.a), () => {
const n6 = t1()
- _setInsertionState(n6, null, 0, true)
+ _setInsertionState(n6, null, 0)
const n2 = _createIf(() => (_ctx.b), () => {
const n5 = t0()
- _setInsertionState(n5, null, 0, true)
+ _setInsertionState(n5, null, 0)
const n4 = _createComponentWithFallback(_component_ChildComp)
return n5
}, null, 1)
diff --git a/packages/compiler-vapor/__tests__/transforms/logicalIndex.spec.ts b/packages/compiler-vapor/__tests__/transforms/logicalIndex.spec.ts
index 5374707e608..9abc007563e 100644
--- a/packages/compiler-vapor/__tests__/transforms/logicalIndex.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/logicalIndex.spec.ts
@@ -106,8 +106,8 @@ describe('compiler: logicalIndex', () => {
A
`)
- // setInsertionState(parent, anchor=0(prepend), logicalIndex=0, last=true)
- expect(code).toContain('_setInsertionState(n1, 0, 0, true)')
+ // setInsertionState(parent, anchor=0(prepend), logicalIndex=0)
+ expect(code).toContain('_setInsertionState(n1, 0, 0)')
expect(code).toMatchSnapshot()
})
@@ -122,7 +122,7 @@ describe('compiler: logicalIndex', () => {
// Comp1
expect(code).toContain('_setInsertionState(n2, 0, 0)')
// Comp2
- expect(code).toContain('_setInsertionState(n2, 0, 1, true)')
+ expect(code).toContain('_setInsertionState(n2, 0, 1)')
expect(code).toMatchSnapshot()
})
})
@@ -136,8 +136,8 @@ describe('compiler: logicalIndex', () => {
B
`)
- // setInsertionState(parent, anchor=n${id}, logicalIndex=1, last=true)
- expect(code).toContain('_setInsertionState(n2, n1, 1, true)')
+ // setInsertionState(parent, anchor=n${id}, logicalIndex=1)
+ expect(code).toContain('_setInsertionState(n2, n1, 1)')
expect(code).toMatchSnapshot()
})
@@ -153,7 +153,7 @@ describe('compiler: logicalIndex', () => {
// Comp1
expect(code).toContain('_setInsertionState(n3, n2, 1)')
// Comp2
- expect(code).toContain('_setInsertionState(n3, n2, 2, true)')
+ expect(code).toContain('_setInsertionState(n3, n2, 2)')
expect(code).toMatchSnapshot()
})
})
@@ -166,8 +166,8 @@ describe('compiler: logicalIndex', () => {
`)
- // setInsertionState(parent, null(append), logicalIndex=1, last=true)
- expect(code).toContain('_setInsertionState(n1, null, 1, true)')
+ // setInsertionState(parent, null(append), logicalIndex=1)
+ expect(code).toContain('_setInsertionState(n1, null, 1)')
expect(code).toMatchSnapshot()
})
@@ -182,7 +182,7 @@ describe('compiler: logicalIndex', () => {
// Comp1
expect(code).toContain('_setInsertionState(n2, null, 1)')
// Comp2
- expect(code).toContain('_setInsertionState(n2, null, 2, true)')
+ expect(code).toContain('_setInsertionState(n2, null, 2)')
expect(code).toMatchSnapshot()
})
@@ -192,7 +192,7 @@ describe('compiler: logicalIndex', () => {
`)
- expect(code).toContain('_setInsertionState(n1, null, 0, true)')
+ expect(code).toContain('_setInsertionState(n1, null, 0)')
expect(code).toMatchSnapshot()
})
})
@@ -209,7 +209,7 @@ describe('compiler: logicalIndex', () => {
// Comp1: prepend, logicalIndex=0
expect(code).toContain('_setInsertionState(n2, 0, 0)')
// Comp2: append, logicalIndex=2
- expect(code).toContain('_setInsertionState(n2, null, 2, true)')
+ expect(code).toContain('_setInsertionState(n2, null, 2)')
expect(code).toMatchSnapshot()
})
@@ -228,7 +228,7 @@ describe('compiler: logicalIndex', () => {
// Comp2: insert, logicalIndex=2
expect(code).toContain('_setInsertionState(n3, n4, 2)')
// Comp3: append, logicalIndex=4
- expect(code).toContain('_setInsertionState(n3, null, 4, true)')
+ expect(code).toContain('_setInsertionState(n3, null, 4)')
expect(code).toMatchSnapshot()
})
})
@@ -241,7 +241,7 @@ describe('compiler: logicalIndex', () => {
A
`)
- expect(code).toContain('_setInsertionState(n3, 0, 0, true)')
+ expect(code).toContain('_setInsertionState(n3, 0, 0)')
expect(code).toMatchSnapshot()
})
@@ -253,7 +253,7 @@ describe('compiler: logicalIndex', () => {
B
`)
- expect(code).toContain('_setInsertionState(n4, n3, 1, true)')
+ expect(code).toContain('_setInsertionState(n4, n3, 1)')
expect(code).toMatchSnapshot()
})
@@ -264,7 +264,7 @@ describe('compiler: logicalIndex', () => {
`)
- expect(code).toContain('_setInsertionState(n3, null, 1, true)')
+ expect(code).toContain('_setInsertionState(n3, null, 1)')
expect(code).toMatchSnapshot()
})
})
@@ -277,7 +277,7 @@ describe('compiler: logicalIndex', () => {
A
`)
- expect(code).toContain('_setInsertionState(n3, 0, 0, true)')
+ expect(code).toContain('_setInsertionState(n3, 0, 0)')
expect(code).toMatchSnapshot()
})
@@ -288,7 +288,7 @@ describe('compiler: logicalIndex', () => {
`)
- expect(code).toContain('_setInsertionState(n3, null, 1, true)')
+ expect(code).toContain('_setInsertionState(n3, null, 1)')
expect(code).toMatchSnapshot()
})
})
@@ -301,7 +301,7 @@ describe('compiler: logicalIndex', () => {
A
`)
- expect(code).toContain('_setInsertionState(n1, 0, 0, true)')
+ expect(code).toContain('_setInsertionState(n1, 0, 0)')
expect(code).toMatchSnapshot()
})
@@ -312,7 +312,7 @@ describe('compiler: logicalIndex', () => {
`)
- expect(code).toContain('_setInsertionState(n1, null, 1, true)')
+ expect(code).toContain('_setInsertionState(n1, null, 1)')
expect(code).toMatchSnapshot()
})
})
@@ -329,7 +329,7 @@ describe('compiler: logicalIndex', () => {
`)
// The entire if/else block is logicalIndex = 1
- expect(code).toContain('_setInsertionState(n6, n5, 1, true)')
+ expect(code).toContain('_setInsertionState(n6, n5, 1)')
expect(code).toMatchSnapshot()
})
@@ -344,7 +344,7 @@ describe('compiler: logicalIndex', () => {
`)
// The entire if/else-if/else block is logicalIndex = 1
- expect(code).toContain('_setInsertionState(n8, n7, 1, true)')
+ expect(code).toContain('_setInsertionState(n8, n7, 1)')
expect(code).toMatchSnapshot()
})
@@ -357,7 +357,7 @@ describe('compiler: logicalIndex', () => {
`)
// logicalIndex = 0 for prepend
- expect(code).toContain('_setInsertionState(n5, 0, 0, true)')
+ expect(code).toContain('_setInsertionState(n5, 0, 0)')
expect(code).toMatchSnapshot()
})
@@ -370,7 +370,7 @@ describe('compiler: logicalIndex', () => {
`)
// logicalIndex = 1 for append
- expect(code).toContain('_setInsertionState(n5, null, 1, true)')
+ expect(code).toContain('_setInsertionState(n5, null, 1)')
expect(code).toMatchSnapshot()
})
@@ -386,7 +386,7 @@ describe('compiler: logicalIndex', () => {
// v-if/v-else: logicalIndex = 1
// Comp: logicalIndex = 2
expect(code).toContain('_setInsertionState(n6, null, 1)')
- expect(code).toContain('_setInsertionState(n6, null, 2, true)')
+ expect(code).toContain('_setInsertionState(n6, null, 2)')
expect(code).toMatchSnapshot()
})
@@ -403,7 +403,7 @@ describe('compiler: logicalIndex', () => {
// v-if/v-else: logicalIndex = 1
// span: logicalIndex = 2
expect(code).toContain('_setInsertionState(n6, 0, 0)')
- expect(code).toContain('_setInsertionState(n6, 0, 1, true)')
+ expect(code).toContain('_setInsertionState(n6, 0, 1)')
expect(code).toMatchSnapshot()
})
@@ -421,7 +421,7 @@ describe('compiler: logicalIndex', () => {
// Comp2: logicalIndex = 2
expect(code).toContain('_setInsertionState(n7, null, 0)')
expect(code).toContain('_setInsertionState(n7, null, 1)')
- expect(code).toContain('_setInsertionState(n7, null, 2, true)')
+ expect(code).toContain('_setInsertionState(n7, null, 2)')
expect(code).toMatchSnapshot()
})
})
@@ -434,7 +434,7 @@ describe('compiler: logicalIndex', () => {
A
`)
- expect(code).toContain('_setInsertionState(n2, 0, 0, true)')
+ expect(code).toContain('_setInsertionState(n2, 0, 0)')
expect(code).toMatchSnapshot()
})
@@ -446,7 +446,7 @@ describe('compiler: logicalIndex', () => {
`)
expect(code).toMatchSnapshot()
- expect(code).toContain('_setInsertionState(n2, null, 1, true)')
+ expect(code).toContain('_setInsertionState(n2, null, 1)')
})
test('key in middle', () => {
@@ -457,7 +457,7 @@ describe('compiler: logicalIndex', () => {
B
`)
- expect(code).toContain('_setInsertionState(n3, n2, 1, true)')
+ expect(code).toContain('_setInsertionState(n3, n2, 1)')
expect(code).toMatchSnapshot()
})
})
diff --git a/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts
index 36df074dd99..7a72975a985 100644
--- a/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts
@@ -70,7 +70,7 @@ describe('compiler: children transform', () => {
)
// ensure the insertion anchor is generated before the insertion statement
expect(code).toMatch(`const n3 = _next(_child(n4), 1)`)
- expect(code).toMatch(`_setInsertionState(n4, n3, 1, true)`)
+ expect(code).toMatch(`_setInsertionState(n4, n3, 1)`)
expect(code).toMatchSnapshot()
})
})
diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts
index 036816360bd..884865153c9 100644
--- a/packages/compiler-vapor/src/generators/operation.ts
+++ b/packages/compiler-vapor/src/generators/operation.ts
@@ -171,7 +171,7 @@ function genInsertionState(
operation: InsertionStateTypes,
context: CodegenContext,
): CodeFragment[] {
- const { parent, anchor, logicalIndex, append, last } = operation
+ const { parent, anchor, logicalIndex, append } = operation
return [
NEWLINE,
...genCall(
@@ -186,7 +186,6 @@ function genInsertionState(
'null'
: `n${anchor}`,
logicalIndex !== undefined ? String(logicalIndex) : undefined,
- last && 'true',
),
]
}
diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts
index 31ef721a92c..8ca98360fd6 100644
--- a/packages/compiler-vapor/src/ir/index.ts
+++ b/packages/compiler-vapor/src/ir/index.ts
@@ -100,7 +100,6 @@ export interface IfIRNode extends BaseIRNode, EffectBoundary {
anchor?: number
logicalIndex?: number
append?: boolean
- last?: boolean
}
export interface IRFor {
@@ -122,7 +121,6 @@ export interface ForIRNode extends BaseIRNode, IRFor, EffectBoundary {
anchor?: number
logicalIndex?: number
append?: boolean
- last?: boolean
}
export interface KeyIRNode extends BaseIRNode, EffectBoundary {
@@ -134,7 +132,6 @@ export interface KeyIRNode extends BaseIRNode, EffectBoundary {
anchor?: number
logicalIndex?: number
append?: boolean
- last?: boolean
}
export interface SetBlockKeyIRNode extends BaseIRNode {
@@ -244,7 +241,6 @@ export interface CreateComponentIRNode extends BaseIRNode, EffectBoundary {
anchor?: number
logicalIndex?: number
append?: boolean
- last?: boolean
}
export interface SlotOutletIRNode extends BaseIRNode, EffectBoundary {
@@ -259,7 +255,6 @@ export interface SlotOutletIRNode extends BaseIRNode, EffectBoundary {
anchor?: number
logicalIndex?: number
append?: boolean
- last?: boolean
}
export interface GetTextChildIRNode extends BaseIRNode {
diff --git a/packages/compiler-vapor/src/transforms/transformChildren.ts b/packages/compiler-vapor/src/transforms/transformChildren.ts
index ff48a6ee6e5..58612494017 100644
--- a/packages/compiler-vapor/src/transforms/transformChildren.ts
+++ b/packages/compiler-vapor/src/transforms/transformChildren.ts
@@ -8,7 +8,6 @@ import {
DynamicFlag,
type IRDynamicInfo,
IRNodeTypes,
- type InsertionStateTypes,
isBlockOperation,
} from '../ir'
import { shouldUseCreateElement } from './transformElement'
@@ -81,8 +80,6 @@ export const transformChildren: NodeTransform = (node, context) => {
function processDynamicChildren(context: TransformContext) {
let prevDynamics: IRDynamicInfo[] = []
let staticCount = 0
- let dynamicCount = 0
- let lastInsertionChild: IRDynamicInfo | undefined
const children = context.dynamic.children
// Track logical index for each child.
@@ -94,7 +91,7 @@ function processDynamicChildren(context: TransformContext) {
for (const [index, child] of children.entries()) {
if (child.flags & DynamicFlag.INSERT) {
child.logicalIndex = logicalIndex
- prevDynamics.push((lastInsertionChild = child))
+ prevDynamics.push(child)
logicalIndex++
}
@@ -109,7 +106,6 @@ function processDynamicChildren(context: TransformContext) {
} else {
registerInsertion(prevDynamics, context, -1 /* prepend */)
}
- dynamicCount += prevDynamics.length
prevDynamics = []
}
staticCount++
@@ -122,14 +118,10 @@ function processDynamicChildren(context: TransformContext) {
prevDynamics,
context,
// the logical index of append child
- dynamicCount + staticCount,
+ prevDynamics[0].logicalIndex!,
true,
)
}
-
- if (lastInsertionChild && lastInsertionChild.operation) {
- ;(lastInsertionChild.operation! as InsertionStateTypes).last = true
- }
}
function registerInsertion(
diff --git a/packages/compiler-vapor/src/utils.ts b/packages/compiler-vapor/src/utils.ts
index 172bb281ed4..e0221ebdb4b 100644
--- a/packages/compiler-vapor/src/utils.ts
+++ b/packages/compiler-vapor/src/utils.ts
@@ -153,6 +153,9 @@ export function isTeleportTag(tag: string): boolean {
export function isBuiltInComponent(tag: string): string | undefined {
if (isTeleportTag(tag)) {
return 'VaporTeleport'
+ } else if (tag === 'Suspense' || tag === 'suspense') {
+ // TODO: replace with VaporSuspense once it's implemented
+ return 'Suspense'
} else if (isKeepAliveTag(tag)) {
return 'VaporKeepAlive'
} else if (isTransitionTag(tag)) {
diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts
index 2bc4b6ffd36..6f1c1c217a1 100644
--- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts
+++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts
@@ -1676,7 +1676,7 @@ describe('component: slots', () => {
() => props.show,
() => {
const n5 = template('')() as any
- setInsertionState(n5, null, 0, true)
+ setInsertionState(n5, null, 0)
createSlot('header', null, () => {
const n4 = template('default header')()
return n4
diff --git a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts
index bdc784b9f03..e1547b51531 100644
--- a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts
+++ b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts
@@ -1388,7 +1388,7 @@ function runSharedTests(deferMode: boolean): void {
() => show.value,
() => {
const n0 = template('')()
- setInsertionState(n0 as any, null, 0, true)
+ setInsertionState(n0 as any, null, 0)
createComponent(
VaporTeleport,
{
@@ -1585,7 +1585,7 @@ function runSharedTests(deferMode: boolean): void {
setup() {
const n0 = template('')()
const n4 = template('')() as any
- setInsertionState(n4, null, 0, true)
+ setInsertionState(n4, null, 0)
createComponent(
VaporTeleport,
{ to: () => '#tt' },
diff --git a/packages/runtime-vapor/__tests__/customElement.spec.ts b/packages/runtime-vapor/__tests__/customElement.spec.ts
index cce030013fd..a407c4369fe 100644
--- a/packages/runtime-vapor/__tests__/customElement.spec.ts
+++ b/packages/runtime-vapor/__tests__/customElement.spec.ts
@@ -125,7 +125,7 @@ describe('defineVaporCustomElement', () => {
const containerComp = defineVaporComponent({
setup() {
const n1 = template('', true)() as any
- setInsertionState(n1, 0, 0, true)
+ setInsertionState(n1, 0, 0)
createPlainElement('my-el-input', {
value: () => num.value,
onInput: () => ($event: CustomEvent) => {
@@ -781,13 +781,13 @@ describe('defineVaporCustomElement', () => {
const t0 = template('fallback
')
const t1 = template('')
const n3 = t1() as any
- setInsertionState(n3, null, 0, true)
+ setInsertionState(n3, null, 0)
createSlot('default', null, () => {
const n2 = t0()
return n2
})
const n5 = t1() as any
- setInsertionState(n5, null, 0, true)
+ setInsertionState(n5, null, 0)
createSlot('named', null)
return [n3, n5]
},
diff --git a/packages/runtime-vapor/__tests__/hydration.spec.ts b/packages/runtime-vapor/__tests__/hydration.spec.ts
index 8a026ac4fe7..f820c9c5a81 100644
--- a/packages/runtime-vapor/__tests__/hydration.spec.ts
+++ b/packages/runtime-vapor/__tests__/hydration.spec.ts
@@ -5060,6 +5060,38 @@ describe('Vapor Mode hydration', () => {
)
})
+ test('enabled teleport hydration with empty v-if should preserve target anchors', async () => {
+ const data = ref({ ok: false, msg: 'foo' })
+
+ const teleportContainer = document.createElement('div')
+ teleportContainer.id = 'teleport-empty-if'
+ teleportContainer.innerHTML =
+ `` + ``
+ document.body.appendChild(teleportContainer)
+
+ const { container } = await mountWithHydration(
+ '',
+ `
+ {{data.msg}}
+ `,
+ data,
+ )
+ await nextTick()
+
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+ expect(teleportContainer.innerHTML).toBe(
+ ``,
+ )
+
+ data.value.ok = true
+ await nextTick()
+ expect(teleportContainer.innerHTML).toBe(
+ `foo`,
+ )
+ })
+
test('disabled teleport hydration over empty main-view range should preserve teleport end anchor', async () => {
const data = ref({ msg: 'foo' })
@@ -5882,7 +5914,6 @@ describe('Vapor Mode hydration', () => {
`
const appCode = `
@@ -5964,7 +5995,6 @@ describe('Vapor Mode hydration', () => {
`
const appCode = `
@@ -6047,7 +6077,6 @@ describe('Vapor Mode hydration', () => {
`
const appCode = `
@@ -6145,7 +6174,6 @@ describe('Vapor Mode hydration', () => {
`
const appCode = `
@@ -6288,7 +6316,6 @@ describe('Vapor Mode hydration', () => {
`
const appCode = `
@@ -7359,6 +7386,164 @@ describe('mismatch handling', () => {
expect(cloned.outerHTML).toBe(`claimed
`)
})
})
+
+ test('nested insertion hydration preserves outer sibling cursor', async () => {
+ const { container, data } = await testWithVaporApp(
+ `
+
+
+
+
+ `,
+ {
+ Child: 'child',
+ },
+ )
+
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+
+ data.value = 'bar'
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+ })
+
+ test('nested v-if hydration preserves outer sibling cursor', async () => {
+ const { container, data } = await testWithVaporApp(`
+
+
+
+
+ `)
+
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+
+ data.value = 'bar'
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+ })
+
+ test('nested v-if hydration preserves same-root dynamic sibling and outer cursor', async () => {
+ const { container, data } = await testWithVaporApp(`
+
+
+
+
+ `)
+
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+
+ data.value = 'bar'
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+ })
+
+ test('nthChild hydration uses logical index after inserted sibling', async () => {
+ const { container, data } = await testWithVaporApp(
+ `
+
+
+
+ `,
+ {
+ Child: 'child',
+ },
+ )
+
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+
+ data.value = 'bar'
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+ })
+
+ test('single-root nested v-if hydration keeps static siblings', async () => {
+ const { container, data } = await testWithVaporApp(`
+
+
+
+
+
+
+ `)
+
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+
+ data.value = 'bar'
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+ })
+
+ test('nested v-for hydration preserves outer sibling cursor', async () => {
+ const { container, data } = await testWithVaporApp(`
+
+
+
+
+ `)
+
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+
+ data.value = 'bar'
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+ })
+
+ test('nested slot hydration preserves outer sibling cursor', async () => {
+ const { container, data } = await testWithVaporApp(
+ `
+
+ child
+
+ `,
+ {
+ Wrapper: `
+
+
+
+
+ `,
+ },
+ )
+
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+
+ data.value = 'bar'
+ await nextTick()
+ expect(container.innerHTML).toBe(
+ ``,
+ )
+ })
})
describe('data-allow-mismatch', () => {
@@ -9056,6 +9241,219 @@ describe('VDOM interop', () => {
`)
})
+ test('hydrate VDOM Teleport slot content and switch vapor branch', async () => {
+ const targetId = 'interop-vdom-teleport-slot-hydration-target'
+ const data = ref({
+ show: true,
+ target: targetId,
+ tail: 'tail',
+ })
+ const portalCode = `
+
+
+
+
+ `
+ const rootCode = `
+
+
+ teleported
+
+ next
+ {{ data.tail }}
+ `
+
+ const ssrComponents = {
+ Portal: compile(portalCode, data, {}, { vapor: false, ssr: true }),
+ }
+ const clientComponents = {
+ Portal: compile(portalCode, data, {}, { vapor: false, ssr: false }),
+ }
+ const serverComp = compile(rootCode, data, ssrComponents, {
+ vapor: true,
+ ssr: true,
+ })
+ const ssrCtx: Record = {}
+ const html = await VueServerRenderer.renderToString(
+ runtimeDom.createSSRApp(serverComp),
+ ssrCtx,
+ )
+
+ const target = document.createElement('div')
+ target.id = targetId
+ target.innerHTML = ssrCtx.teleports[`#${targetId}`]
+ const nextTarget = document.createElement('div')
+ nextTarget.id = `${targetId}-next`
+ document.body.appendChild(target)
+ document.body.appendChild(nextTarget)
+
+ const container = document.createElement('div')
+ container.innerHTML = html
+ document.body.appendChild(container)
+
+ const clientComp = compile(rootCode, data, clientComponents, {
+ vapor: true,
+ ssr: false,
+ })
+ const app = createVaporSSRApp(clientComp)
+ app.use(runtimeVapor.vaporInteropPlugin)
+ try {
+ app.mount(container)
+
+ expect(`Hydration node mismatch`).not.toHaveBeenWarned()
+ expect(formatHtml(target.innerHTML)).toBe(
+ '\n' +
+ 'teleported\n' +
+ '',
+ )
+
+ data.value.target = `${targetId}-next`
+ await nextTick()
+
+ expect(target.innerHTML).not.toContain('')
+ expect(nextTarget.innerHTML).toContain(
+ 'teleported',
+ )
+
+ data.value.show = false
+ await nextTick()
+
+ expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+ "
+ next
tail
+ "
+ `)
+ expect(target.innerHTML).toBe('')
+ expect(nextTarget.innerHTML).toBe('')
+ } finally {
+ app.unmount()
+ container.remove()
+ target.remove()
+ nextTarget.remove()
+ }
+ })
+
+ test('hydrate client-mounted VDOM Teleport slot content and switch vapor branch', async () => {
+ const targetId = 'interop-vdom-mounted-teleport-slot-hydration-target'
+ const data = ref({
+ show: true,
+ tail: 'tail',
+ })
+ const mountedPortalCode = `
+
+
+
+
+ `
+ const portalCode = `
+
+
+
+
+ `
+ const rootCode = `
+
+
+ teleported
+
+ next
+ {{ data.tail }}
+ `
+
+ const ssrComponents: any = {}
+ ssrComponents.MountedPortal = compile(
+ mountedPortalCode,
+ data,
+ ssrComponents,
+ {
+ vapor: false,
+ ssr: true,
+ },
+ )
+ ssrComponents.Portal = compile(portalCode, data, ssrComponents, {
+ vapor: false,
+ ssr: true,
+ })
+ const clientComponents: any = {}
+ clientComponents.MountedPortal = compile(
+ mountedPortalCode,
+ data,
+ clientComponents,
+ {
+ vapor: false,
+ ssr: false,
+ },
+ )
+ clientComponents.Portal = compile(portalCode, data, clientComponents, {
+ vapor: false,
+ ssr: false,
+ })
+ const serverComp = compile(rootCode, data, ssrComponents, {
+ vapor: true,
+ ssr: true,
+ })
+ const html = await VueServerRenderer.renderToString(
+ runtimeDom.createSSRApp(serverComp),
+ )
+
+ const target = document.createElement('div')
+ target.id = targetId
+ document.body.appendChild(target)
+
+ const container = document.createElement('div')
+ container.innerHTML = html
+ document.body.appendChild(container)
+
+ const clientComp = compile(rootCode, data, clientComponents, {
+ vapor: true,
+ ssr: false,
+ })
+ const app = createVaporSSRApp(clientComp)
+ app.use(runtimeVapor.vaporInteropPlugin)
+ try {
+ app.mount(container)
+ await nextTick()
+
+ expect(`Hydration node mismatch`).not.toHaveBeenWarned()
+ expect(formatHtml(target.innerHTML)).toBe('teleported')
+
+ data.value.show = false
+ await nextTick()
+
+ expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+ "
+ next
tail
+ "
+ `)
+ expect(target.innerHTML).toBe('')
+ } finally {
+ app.unmount()
+ container.remove()
+ target.remove()
+ }
+ })
+
test('hydrate Suspense VNode via createDynamicComponent and switch branch', async () => {
const data = ref({
showSuspense: true,
@@ -9131,6 +9529,75 @@ describe('VDOM interop', () => {
`)
})
+ test('hydrate static Suspense with vapor child before trailing sibling', async () => {
+ const serverData = ref({
+ tail: 'tail',
+ })
+ const clientData = ref({
+ html: '',
+ tail: 'tail',
+ })
+ const appCode = `
+
+
+
before
+
+
+
+
+
{{ data.tail }}
+
+ `
+ const serverChildCode = ``
+ const clientChildCode = `
+ `
+
+ const SSRChild = compileVaporComponent(
+ serverChildCode,
+ serverData,
+ undefined,
+ true,
+ )
+ const SSRApp = compileVaporComponent(
+ appCode,
+ serverData,
+ { Child: SSRChild },
+ true,
+ )
+ const html = await VueServerRenderer.renderToString(
+ runtimeDom.createSSRApp(SSRApp),
+ )
+
+ const ClientChild = compileVaporComponent(clientChildCode, clientData)
+ const ClientApp = compileVaporComponent(appCode, clientData, {
+ Child: ClientChild,
+ })
+ const container = document.createElement('div')
+ container.innerHTML = html
+ document.body.appendChild(container)
+ const app = createVaporSSRApp(ClientApp)
+ app.use(runtimeVapor.vaporInteropPlugin)
+ app.mount(container)
+
+ expect(`Hydration node mismatch`).not.toHaveBeenWarned()
+ expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+ ""
+ `)
+
+ clientData.value.html = ''
+ clientData.value.tail = 'tail-updated'
+ await nextTick()
+
+ expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+ ""
+ `)
+ })
+
test('hydrate async Suspense VNode via createDynamicComponent and show fallback', async () => {
const data = ref({
showSuspense: true,
diff --git a/packages/runtime-vapor/__tests__/scopeId.spec.ts b/packages/runtime-vapor/__tests__/scopeId.spec.ts
index 6a976296e19..0d78835eca7 100644
--- a/packages/runtime-vapor/__tests__/scopeId.spec.ts
+++ b/packages/runtime-vapor/__tests__/scopeId.spec.ts
@@ -316,7 +316,7 @@ describe('scopeId', () => {
const Child = defineVaporComponent({
setup() {
const n0 = template('')() as any
- setInsertionState(n0, null, 0, true)
+ setInsertionState(n0, null, 0)
createSlot('default')
return n0
},
@@ -380,7 +380,7 @@ describe('scopeId', () => {
const Parent = defineVaporComponent({
setup() {
const n1 = template('
', true)() as any
- setInsertionState(n1, null, 0, true)
+ setInsertionState(n1, null, 0)
createSlot('default', null)
return n1
},
@@ -389,7 +389,7 @@ describe('scopeId', () => {
const Child = defineVaporComponent({
setup() {
const n1 = template('
', true)() as any
- setInsertionState(n1, null, 0, true)
+ setInsertionState(n1, null, 0)
createSlot('default', null)
return n1
},
diff --git a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts
index 25eccd8fbcc..e610838e0e4 100644
--- a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts
+++ b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts
@@ -1505,6 +1505,40 @@ describe('vdomInterop', () => {
expect(vdomRef.value.name).toBe('vdomChild')
})
+ it('dynamic component includes vdom component should unmount with vapor branch', async () => {
+ const show = ref(true)
+ const unmounted = vi.fn()
+ const VdomChild = defineComponent({
+ setup() {
+ onUnmounted(unmounted)
+ return () => h('div', 'vdom child')
+ },
+ })
+
+ const VaporChild = defineVaporComponent({
+ setup() {
+ return createIf(
+ () => show.value,
+ () => createDynamicComponent(() => VdomChild),
+ )
+ },
+ })
+
+ const { html } = define({
+ setup() {
+ return () => h(VaporChild as any)
+ },
+ }).render()
+
+ expect(html()).toContain('
vdom child
')
+
+ show.value = false
+ await nextTick()
+
+ expect(unmounted).toHaveBeenCalledTimes(1)
+ expect(html()).not.toContain('vdom child')
+ })
+
it('dynamic component includes vdom component should cleanup old ref', async () => {
const VdomChild = defineComponent({
setup(_, { expose }) {
@@ -1906,6 +1940,121 @@ describe('vdomInterop', () => {
expect(hooksA.activated).toHaveBeenCalledTimes(2)
})
+ it('unmounts cached inner VDOM components', async () => {
+ const hooksA = {
+ unmounted: vi.fn(),
+ }
+ const hooksB = {
+ unmounted: vi.fn(),
+ }
+
+ const VDOMCompA = defineComponent({
+ setup() {
+ onUnmounted(() => hooksA.unmounted())
+ return () => h('div', 'vdom A')
+ },
+ })
+
+ const VDOMCompB = defineComponent({
+ setup() {
+ onUnmounted(() => hooksB.unmounted())
+ return () => h('div', 'vdom B')
+ },
+ })
+
+ const current = shallowRef
(VDOMCompA)
+
+ const App = defineVaporComponent({
+ setup() {
+ return createComponent(VaporKeepAlive, null, {
+ default: () => createDynamicComponent(() => current.value),
+ })
+ },
+ })
+
+ const root = document.createElement('div')
+ const app = createApp({
+ setup() {
+ return () => h(App as any)
+ },
+ })
+ app.use(vaporInteropPlugin)
+ app.mount(root)
+
+ expect(root.innerHTML).toBe(
+ 'vdom A
',
+ )
+
+ current.value = VDOMCompB
+ await nextTick()
+ expect(root.innerHTML).toBe(
+ 'vdom B
',
+ )
+ expect(hooksA.unmounted).toHaveBeenCalledTimes(0)
+ expect(hooksB.unmounted).toHaveBeenCalledTimes(0)
+
+ app.unmount()
+ await nextTick()
+ expect(hooksA.unmounted).toHaveBeenCalledTimes(1)
+ expect(hooksB.unmounted).toHaveBeenCalledTimes(1)
+ })
+
+ it('unmounts inactive cached inner VDOM components during KeepAlive hmr rerender', async () => {
+ const hooksA = {
+ unmounted: vi.fn(),
+ }
+ const hooksB = {
+ unmounted: vi.fn(),
+ }
+
+ const VDOMCompA = defineComponent({
+ setup() {
+ onUnmounted(() => hooksA.unmounted())
+ return () => h('div', 'vdom A')
+ },
+ })
+
+ const VDOMCompB = defineComponent({
+ setup() {
+ onUnmounted(() => hooksB.unmounted())
+ return () => h('div', 'vdom B')
+ },
+ })
+
+ const current = shallowRef(VDOMCompA)
+ let keepAlive: any
+
+ const App = defineVaporComponent({
+ setup() {
+ keepAlive = createComponent(VaporKeepAlive, null, {
+ default: () => createDynamicComponent(() => current.value),
+ })
+ return keepAlive
+ },
+ })
+
+ const { html } = define({
+ setup() {
+ return () => h(App as any)
+ },
+ }).render()
+
+ expect(html()).toBe('vdom A
')
+
+ current.value = VDOMCompB
+ await nextTick()
+ expect(html()).toBe('vdom B
')
+ expect(hooksA.unmounted).toHaveBeenCalledTimes(0)
+ expect(hooksB.unmounted).toHaveBeenCalledTimes(0)
+
+ keepAlive.hmrRerender()
+ await nextTick()
+
+ expect(hooksA.unmounted).toHaveBeenCalledTimes(1)
+ expect(hooksB.unmounted).toHaveBeenCalledTimes(1)
+ expect(html()).toBe('vdom B
')
+ })
+
it('switch VNode with inner mixed vapor/VDOM components', async () => {
const hooksA = {
mounted: vi.fn(),
diff --git a/packages/runtime-vapor/src/apiCreateDynamicComponent.ts b/packages/runtime-vapor/src/apiCreateDynamicComponent.ts
index 661e72be931..535a8bb0c04 100644
--- a/packages/runtime-vapor/src/apiCreateDynamicComponent.ts
+++ b/packages/runtime-vapor/src/apiCreateDynamicComponent.ts
@@ -21,11 +21,12 @@ import { type RawSlots, getScopeOwner } from './componentSlots'
import {
insertionAnchor,
insertionParent,
- isLastInsertion,
resetInsertionState,
} from './insertionState'
import {
- advanceHydrationNode,
+ type HydrationCursor,
+ captureHydrationCursor,
+ exitHydrationCursor,
isHydrating,
locateHydrationNode,
} from './dom/hydration'
@@ -43,8 +44,10 @@ export function createDynamicComponent(
): VaporFragment {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
- const _isLastInsertion = isLastInsertion
if (!isHydrating) resetInsertionState()
+ const hydrationCursor: HydrationCursor | null = isHydrating
+ ? captureHydrationCursor()
+ : null
const frag =
isHydrating || __DEV__
@@ -74,9 +77,6 @@ export function createDynamicComponent(
if (isHydrating) {
locateHydrationNode(shouldConsumeFragmentStart(value))
frag.hydrate()
- if (_isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
- }
}
return frag
}
@@ -98,9 +98,7 @@ export function createDynamicComponent(
if (!isHydrating) {
if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
} else {
- if (_isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
- }
+ exitHydrationCursor(hydrationCursor)
}
return frag
}
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index b114ee18181..6936e036b1c 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -24,13 +24,15 @@ import {
import { renderEffect } from './renderEffect'
import { VaporVForFlags } from '@vue/shared'
import {
+ type HydrationCursor,
advanceHydrationNode,
currentHydrationNode,
enterHydrationBoundary,
+ enterHydrationCursor,
+ exitHydrationCursor,
isComment,
isHydrating,
locateHydrationBoundaryClose,
- locateHydrationNode,
locateNextNode,
markHydrationAnchor,
setCurrentHydrationNode,
@@ -46,7 +48,6 @@ import {
insertionAnchor,
insertionIndex,
insertionParent,
- isLastInsertion,
resetInsertionState,
} from './insertionState'
import { applyTransitionHooks, isTransitionEnabled } from './transition'
@@ -89,9 +90,9 @@ export const createFor = (
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
const _insertionIndex = insertionIndex
- const _isLastInsertion = isLastInsertion
+ let hydrationCursor: HydrationCursor | null = null
if (isHydrating) {
- locateHydrationNode(true)
+ hydrationCursor = enterHydrationCursor(true)
} else {
resetInsertionState()
}
@@ -556,8 +557,11 @@ export const createFor = (
if (!isHydrating) {
if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
- } else if (!pendingHydrationAnchor) {
- advanceHydrationNode(_isLastInsertion ? _insertionParent! : parentAnchor!)
+ } else {
+ if (!pendingHydrationAnchor && currentHydrationNode === parentAnchor!) {
+ advanceHydrationNode(parentAnchor!)
+ }
+ exitHydrationCursor(hydrationCursor)
}
return frag
diff --git a/packages/runtime-vapor/src/apiCreateFragment.ts b/packages/runtime-vapor/src/apiCreateFragment.ts
index 08bb297df9a..193f2871982 100644
--- a/packages/runtime-vapor/src/apiCreateFragment.ts
+++ b/packages/runtime-vapor/src/apiCreateFragment.ts
@@ -1,10 +1,14 @@
import { type Block, type BlockFn, insert } from './block'
-import { advanceHydrationNode, isHydrating } from './dom/hydration'
+import {
+ type HydrationCursor,
+ captureHydrationCursor,
+ exitHydrationCursor,
+ isHydrating,
+} from './dom/hydration'
import { DynamicFragment } from './fragment'
import {
insertionAnchor,
insertionParent,
- isLastInsertion,
resetInsertionState,
} from './insertionState'
import { renderEffect } from './renderEffect'
@@ -22,8 +26,10 @@ import { renderEffect } from './renderEffect'
export function createKeyedFragment(key: () => any, render: BlockFn): Block {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
- const _isLastInsertion = isLastInsertion
if (!isHydrating) resetInsertionState()
+ const hydrationCursor: HydrationCursor | null = isHydrating
+ ? captureHydrationCursor()
+ : null
const frag = __DEV__
? new DynamicFragment('keyed', true)
@@ -34,9 +40,7 @@ export function createKeyedFragment(key: () => any, render: BlockFn): Block {
if (!isHydrating) {
if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
} else {
- if (_isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
- }
+ exitHydrationCursor(hydrationCursor)
}
return frag
}
diff --git a/packages/runtime-vapor/src/apiCreateIf.ts b/packages/runtime-vapor/src/apiCreateIf.ts
index b8dba51ce59..9e64a051e14 100644
--- a/packages/runtime-vapor/src/apiCreateIf.ts
+++ b/packages/runtime-vapor/src/apiCreateIf.ts
@@ -1,13 +1,15 @@
import { type Block, type BlockFn, insert } from './block'
import {
+ type HydrationCursor,
advanceHydrationNode,
+ currentHydrationNode,
+ enterHydrationCursor,
+ exitHydrationCursor,
isHydrating,
- locateHydrationNode,
} from './dom/hydration'
import {
insertionAnchor,
insertionParent,
- isLastInsertion,
resetInsertionState,
} from './insertionState'
import { renderEffect } from './renderEffect'
@@ -25,15 +27,17 @@ export function createIf(
): Block {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
- const _isLastInsertion = isLastInsertion
if (!isHydrating) resetInsertionState()
+ let hydrationCursor: HydrationCursor | null = null
+ let branchShape: VaporBlockShape | undefined
let frag: Block
if (once) {
const ok = condition()
if (isHydrating) {
- locateHydrationNode(
- decodeIfShape(blockShape!, ok) === VaporBlockShape.MULTI_ROOT,
+ branchShape = decodeIfShape(blockShape!, ok)
+ hydrationCursor = enterHydrationCursor(
+ branchShape === VaporBlockShape.MULTI_ROOT,
)
}
frag = ok
@@ -51,8 +55,9 @@ export function createIf(
renderEffect(() => {
const ok = condition()
if (isHydrating) {
- locateHydrationNode(
- decodeIfShape(blockShape!, ok) === VaporBlockShape.MULTI_ROOT,
+ branchShape = decodeIfShape(blockShape!, ok)
+ hydrationCursor = enterHydrationCursor(
+ branchShape === VaporBlockShape.MULTI_ROOT,
)
}
;(frag as DynamicFragment).update(
@@ -65,9 +70,20 @@ export function createIf(
if (!isHydrating) {
if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
} else {
- if (_isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
+ // SSR empty branches render as , and no template adoption consumes
+ // that comment. Claim it before restoring the outer cursor.
+ if (branchShape === VaporBlockShape.EMPTY && hydrationCursor) {
+ const start = hydrationCursor.start
+ if (
+ start &&
+ currentHydrationNode === start &&
+ start.nodeType === 8 &&
+ (start as Comment).data === ''
+ ) {
+ advanceHydrationNode(start)
+ }
}
+ exitHydrationCursor(hydrationCursor)
}
return frag
diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts
index 7a65933e3ef..118ada66b13 100644
--- a/packages/runtime-vapor/src/component.ts
+++ b/packages/runtime-vapor/src/component.ts
@@ -86,14 +86,16 @@ import {
} from './componentSlots'
import { hmrReload, hmrRerender } from './hmr'
import {
+ type HydrationCursor,
adoptTemplate,
advanceHydrationNode,
currentHydrationNode,
enterHydrationBoundary,
+ enterHydrationCursor,
+ exitHydrationCursor,
isComment,
isHydrating,
locateEndAnchor,
- locateHydrationNode,
locateNextNode,
markHydrationAnchor,
setCurrentHydrationNode,
@@ -114,7 +116,6 @@ import {
import {
insertionAnchor,
insertionParent,
- isLastInsertion,
resetInsertionState,
} from './insertionState'
import type {
@@ -256,8 +257,8 @@ export function createComponent(
): VaporComponentInstance {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
- const _isLastInsertion = isLastInsertion
let hydrationClose: Node | null = null
+ let hydrationCursor: HydrationCursor | null = null
let exitHydrationBoundary: (() => void) | undefined
let deferHydrationBoundary = false
const finalizeHydrationBoundary = () => {
@@ -267,7 +268,7 @@ export function createComponent(
}
}
if (isHydrating) {
- locateHydrationNode()
+ hydrationCursor = enterHydrationCursor()
if (component.__multiRoot && isComment(currentHydrationNode!, '[')) {
hydrationClose = locateEndAnchor(currentHydrationNode!)
exitHydrationBoundary = enterHydrationBoundary(
@@ -338,9 +339,6 @@ export function createComponent(
if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
} else {
frag.hydrate()
- if (_isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
- }
}
return frag
}
@@ -357,9 +355,6 @@ export function createComponent(
if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
} else {
frag.hydrate()
- if (_isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
- }
}
return frag as any
@@ -441,10 +436,6 @@ export function createComponent(
mountComponent(instance, _insertionParent!, _insertionAnchor)
}
- if (isHydrating && _isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
- }
-
if (
__FEATURE_SUSPENSE__ &&
isSuspenseEnabled &&
@@ -466,12 +457,16 @@ export function createComponent(
}
finalizeHydrationBoundary()
}
+ exitHydrationCursor(hydrationCursor)
}
return instance
} finally {
if (isHydrating && !deferHydrationBoundary) {
+ // Boundary cleanup still needs the component-local cursor. Only after
+ // that do we restore the outer cursor's resume point.
finalizeHydrationBoundary()
+ exitHydrationCursor(hydrationCursor)
}
}
}
@@ -912,9 +907,9 @@ export function createPlainElement(
): HTMLElement {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
- const _isLastInsertion = isLastInsertion
+ let hydrationCursor: HydrationCursor | null = null
if (isHydrating) {
- locateHydrationNode()
+ hydrationCursor = enterHydrationCursor()
} else {
resetInsertionState()
}
@@ -968,9 +963,7 @@ export function createPlainElement(
if (!isHydrating) {
if (_insertionParent) insert(el, _insertionParent, _insertionAnchor)
} else {
- if (_isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
- }
+ exitHydrationCursor(hydrationCursor)
}
return el
diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts
index 8914d13e10d..9a3585b8de5 100644
--- a/packages/runtime-vapor/src/componentSlots.ts
+++ b/packages/runtime-vapor/src/componentSlots.ts
@@ -12,13 +12,14 @@ import { renderEffect } from './renderEffect'
import {
insertionAnchor,
insertionParent,
- isLastInsertion,
resetInsertionState,
} from './insertionState'
import {
- advanceHydrationNode,
+ type HydrationCursor,
+ captureHydrationCursor,
+ enterHydrationCursor,
+ exitHydrationCursor,
isHydrating,
- locateHydrationNode,
} from './dom/hydration'
import {
type DynamicFragment,
@@ -188,8 +189,8 @@ export function createSlot(
): Block {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
- const _isLastInsertion = isLastInsertion
if (!isHydrating) resetInsertionState()
+ let hydrationCursor: HydrationCursor | null = null
const instance = getScopeOwner()!
const rawSlots = instance.rawSlots
@@ -199,7 +200,7 @@ export function createSlot(
let fragment: VaporFragment
if (isRef(rawSlots._) && isInteropEnabled) {
- if (isHydrating) locateHydrationNode()
+ if (isHydrating) hydrationCursor = enterHydrationCursor()
fragment = instance.appContext.vapor!.vdomSlot(
rawSlots._,
name,
@@ -208,6 +209,7 @@ export function createSlot(
fallback,
)
} else {
+ if (isHydrating) hydrationCursor = captureHydrationCursor()
const slotFragment = (fragment = new SlotFragment())
// mark the slot as forwarded
slotFragment.forwarded =
@@ -307,9 +309,7 @@ export function createSlot(
if (fragment.insert) {
;(fragment as VaporFragment).hydrate!()
}
- if (_isLastInsertion) {
- advanceHydrationNode(_insertionParent!)
- }
+ exitHydrationCursor(hydrationCursor)
}
return fragment
diff --git a/packages/runtime-vapor/src/components/KeepAlive.ts b/packages/runtime-vapor/src/components/KeepAlive.ts
index a137ad17804..656a346af1f 100644
--- a/packages/runtime-vapor/src/components/KeepAlive.ts
+++ b/packages/runtime-vapor/src/components/KeepAlive.ts
@@ -125,7 +125,15 @@ const VaporKeepAliveImpl = defineVaporComponent({
const rerender = keepAliveInstance.hmrRerender
keepAliveInstance.hmrRerender = () => {
keepAliveInstance.exposed = null
- cache.forEach(cached => unsetShapeFlag(cached))
+ cache.forEach(cached => {
+ unsetShapeFlag(cached)
+ if (cached !== current) {
+ // Cached blocks may contain interop children whose VDOM teardown
+ // is owned by remove(), not scope.stop().
+ const parentNode = findBlockNode(cached).parentNode
+ if (parentNode) remove(cached, parentNode as ParentNode)
+ }
+ })
cache.clear()
keys.clear()
keptAliveScopes.forEach(scope => scope.stop())
diff --git a/packages/runtime-vapor/src/dom/hydration.ts b/packages/runtime-vapor/src/dom/hydration.ts
index 9c7429f6d62..12e75841258 100644
--- a/packages/runtime-vapor/src/dom/hydration.ts
+++ b/packages/runtime-vapor/src/dom/hydration.ts
@@ -148,6 +148,42 @@ export function advanceHydrationNode(node: Node): void {
}
}
+export type HydrationCursor = {
+ start: Node | null
+ // `undefined` means this scope follows the cursor advanced by its body.
+ // `null` is a real resume point: the outer scope has no next node.
+ resume: Node | null | undefined
+}
+
+export function enterHydrationCursor(
+ consumeFragmentStart = false,
+): HydrationCursor {
+ const resume = insertionParent ? currentHydrationNode : undefined
+ locateHydrationNode(consumeFragmentStart)
+ return {
+ start: currentHydrationNode,
+ resume,
+ }
+}
+
+/**
+ * Capture only the outer resume cursor for dynamic wrappers whose inner owner
+ * locates the local start later, after the selected inner path is known.
+ * This avoids consuming insertion state too early.
+ */
+export function captureHydrationCursor(): HydrationCursor {
+ return {
+ start: null,
+ resume: insertionParent ? currentHydrationNode : undefined,
+ }
+}
+
+export function exitHydrationCursor(cursor: HydrationCursor | null): void {
+ if (cursor && cursor.resume !== undefined) {
+ setCurrentHydrationNode(cursor.resume)
+ }
+}
+
/**
* Locate the first non-fragment-comment node and locate the next node
* while handling potential fragments.
diff --git a/packages/runtime-vapor/src/dom/node.ts b/packages/runtime-vapor/src/dom/node.ts
index 6c36ff1aec8..2f131c8ce70 100644
--- a/packages/runtime-vapor/src/dom/node.ts
+++ b/packages/runtime-vapor/src/dom/node.ts
@@ -49,9 +49,13 @@ export function child(node: InsertionParent, logicalIndex?: number): Node {
}
/*@__NO_SIDE_EFFECTS__*/
-export function nthChild(node: InsertionParent, i: number): Node {
+export function nthChild(
+ node: InsertionParent,
+ i: number,
+ logicalIndex: number = i,
+): Node {
if (isHydrating) {
- return locateChildByLogicalIndex(node, i)!
+ return locateChildByLogicalIndex(node, logicalIndex)!
}
return node.childNodes[i]
}
diff --git a/packages/runtime-vapor/src/fragment.ts b/packages/runtime-vapor/src/fragment.ts
index fc394b638d9..cd849c9610b 100644
--- a/packages/runtime-vapor/src/fragment.ts
+++ b/packages/runtime-vapor/src/fragment.ts
@@ -431,6 +431,28 @@ export class DynamicFragment extends VaporFragment {
advanceHydrationNode(currentHydrationNode)
return
}
+ if (
+ this.anchorLabel &&
+ currentHydrationNode &&
+ isComment(currentHydrationNode, 'teleport anchor')
+ ) {
+ const parentNode = getParentNode(currentHydrationNode)
+ const anchor = markHydrationAnchor(currentHydrationNode)
+ if (parentNode) {
+ // Target-side teleport anchors are structural. Empty dynamic
+ // fragments insert their own anchor before the target anchor
+ // instead of consuming it as mismatched SSR content.
+ queuePostFlushCb(() => {
+ parentNode.insertBefore(
+ (this.anchor = markHydrationAnchor(
+ __DEV__ ? createComment(this.anchorLabel!) : createTextNode(),
+ )),
+ anchor.parentNode === parentNode ? anchor : null,
+ )
+ })
+ return
+ }
+ }
if (
!isSlot &&
this.anchorLabel &&
diff --git a/packages/runtime-vapor/src/insertionState.ts b/packages/runtime-vapor/src/insertionState.ts
index b005e508c69..c5d1fe97e87 100644
--- a/packages/runtime-vapor/src/insertionState.ts
+++ b/packages/runtime-vapor/src/insertionState.ts
@@ -16,11 +16,6 @@ export let insertionAnchor: Node | 0 | undefined | null
// logical index for hydration
export let insertionIndex: number | undefined
-// indicates whether the insertion is the last one in the parent.
-// if true, means no more nodes need to be hydrated after this insertion,
-// advancing current hydration node to parent nextSibling
-export let isLastInsertion: boolean | undefined
-
/**
* This function is called before a block type that requires insertion
* (component, slot outlet, if, for) is created. The state is used for actual
@@ -30,10 +25,8 @@ export function setInsertionState(
parent: ParentNode & { $fc?: Node | null },
anchor?: Node | 0 | null,
logicalIndex?: number,
- last?: boolean,
): void {
insertionParent = parent
- isLastInsertion = last
insertionIndex = logicalIndex
if (anchor !== undefined) {
@@ -52,9 +45,5 @@ export function setInsertionState(
}
export function resetInsertionState(): void {
- insertionParent =
- insertionAnchor =
- insertionIndex =
- isLastInsertion =
- undefined
+ insertionParent = insertionAnchor = insertionIndex = undefined
}
diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts
index 9da5d2d9907..147121d0f08 100644
--- a/packages/runtime-vapor/src/vdomInterop.ts
+++ b/packages/runtime-vapor/src/vdomInterop.ts
@@ -286,6 +286,7 @@ const vaporInteropImpl: Omit<
unmount(vnode, doRemove) {
const container = doRemove ? vnode.anchor!.parentNode : undefined
const instance = vnode.component as any as VaporComponentInstance
+ let slotStartAnchor: Node | null = null
if (instance) {
// the async component may not be resolved yet, block is null
if (instance.block) {
@@ -304,6 +305,11 @@ const vaporInteropImpl: Omit<
}
} else if (vnode.vb) {
const anchor = vnode.anchor as Node | null
+ // `hydrateSlot()` records the opening marker for VDOM SSR slot fragments
+ // on vnode.el while vnode.anchor points at the closing marker.
+ if (vnode.el && vnode.el !== anchor && isComment(vnode.el as Node, '[')) {
+ slotStartAnchor = vnode.el as Node
+ }
// Fragment child unmounts invoke VaporSlot with doRemove = false, so the
// renderer does not pass us a container. Most slot blocks can still
// clean themselves up without it, but KeepAlive needs the host container
@@ -317,6 +323,12 @@ const vaporInteropImpl: Omit<
stopVaporSlotScope(vnode)
}
if (doRemove) {
+ if (slotStartAnchor) {
+ const parent = slotStartAnchor.parentNode
+ if (parent) {
+ remove(slotStartAnchor, parent)
+ }
+ }
const anchor = vnode.anchor as Node
// `container` is captured before unmount starts, but the unmount above
// may already remove or move this anchor. Only remove it if it is still
@@ -362,6 +374,10 @@ const vaporInteropImpl: Omit<
const selfAnchor = n1.anchor as Node
const parent = selfAnchor.parentNode as ParentNode
const nextSibling = selfAnchor.nextSibling
+ const rangeStartAnchor =
+ n1.el && n1.el !== selfAnchor && isComment(n1.el as Node, '[')
+ ? (n1.el as Node)
+ : undefined
const oldBlockOwnsAnchor =
isFragment(n1.vb!) && n1.vb!.anchor === selfAnchor
// remove old vapor block
@@ -380,12 +396,14 @@ const vaporInteropImpl: Omit<
newAnchor = selfAnchor
insertAnchor = selfAnchor
}
- insert((n2.el = n2.anchor = newAnchor), parent, insertAnchor)
+ insert((n2.anchor = newAnchor), parent, insertAnchor)
+ n2.el = rangeStartAnchor || newAnchor
insert((n2.vb = slotBlock), parent, newAnchor)
} else {
const vs1 = n1.vs!
const vs2 = n2.vs!
- n2.el = n2.anchor = n1.anchor
+ n2.el = n1.el
+ n2.anchor = n1.anchor
n2.vb = n1.vb
;(vs2.ref = vs1.ref)!.value = n2.props
vs2.scope = vs1.scope
@@ -395,6 +413,13 @@ const vaporInteropImpl: Omit<
},
move(vnode, container, anchor, moveType) {
+ if (
+ vnode.el &&
+ vnode.el !== vnode.anchor &&
+ isComment(vnode.el as Node, '[')
+ ) {
+ move(vnode.el as any, container, anchor, moveType)
+ }
move(vnode.vb || (vnode.component as any), container, anchor, moveType)
move(vnode.anchor as any, container, anchor, moveType)
},
@@ -432,11 +457,18 @@ const vaporInteropImpl: Omit<
if (!isHydrating && !isVdomHydrating) return node
vaporHydrateNode(node, () => {
vnode.vb = renderVaporSlot(vnode, parentComponent, parentSuspense)
- vnode.anchor = vnode.el =
+ const anchor =
isFragment(vnode.vb) && vnode.vb.anchor
? vnode.vb.anchor
: currentHydrationNode!
-
+ // VDOM SSR wraps slot output in fragment anchors. Keep that range on the
+ // VaporSlot vnode so enabled Teleport removal can dispose both anchors.
+ if (isComment(node, '[') && isComment(anchor, ']')) {
+ vnode.el = node
+ vnode.anchor = anchor
+ } else {
+ vnode.anchor = vnode.el = anchor
+ }
if (__DEV__ && !vnode.anchor) {
throw new Error(
`Failed to locate slot anchor. this is likely a Vue internal bug.`,
@@ -604,9 +636,8 @@ const vaporSlotsProxyHandler: ProxyHandler = {
let vdomHydrateNode: HydrationRenderer['hydrateNode'] | undefined
-// Static/Fragment vnodes always represent a contiguous range [el..anchor].
-// For component vnodes, only treat them as a range when their hydrated subTree
-// is Static/Fragment (multi-root component case).
+// Static/Fragment/Teleport vnodes represent a root range [el..anchor].
+// Component roots can update internally, so resolve through the current subtree.
function resolveVNodeRange(vnode: VNode): [Node, Node] | undefined {
const { type, shapeFlag, el, anchor } = vnode
if (shapeFlag & ShapeFlags.TELEPORT && el && anchor && anchor !== el) {
@@ -616,21 +647,11 @@ function resolveVNodeRange(vnode: VNode): [Node, Node] | undefined {
if ((type === Static || type === Fragment) && el && anchor && anchor !== el) {
return [el as Node, anchor as Node]
}
- if (!(shapeFlag & ShapeFlags.COMPONENT)) {
- return
- }
-
- const subTree = vnode.component && vnode.component.subTree
- const subEl = subTree && subTree.el
- const subAnchor = subTree && subTree.anchor
- if (
- subTree &&
- (subTree.type === Static || subTree.type === Fragment) &&
- subEl &&
- subAnchor &&
- subAnchor !== subEl
- ) {
- return [subEl as Node, subAnchor as Node]
+ if (shapeFlag & ShapeFlags.COMPONENT) {
+ const subTree = vnode.component && vnode.component.subTree
+ if (subTree) {
+ return resolveVNodeRange(subTree)
+ }
}
}
@@ -655,9 +676,27 @@ function resolveVNodeNodes(vnode: VNode): Block {
}
return nodeRange
}
+ if (vnode.shapeFlag & ShapeFlags.COMPONENT) {
+ const subTree = vnode.component && vnode.component.subTree
+ if (subTree) {
+ return resolveVNodeNodes(subTree)
+ }
+ }
return vnode.el as Block
}
+function removeAttachedNodes(block: Block, parent: ParentNode): void {
+ if (block instanceof Node) {
+ if (block.parentNode === parent) {
+ remove(block, parent)
+ }
+ } else if (isArray(block)) {
+ for (let i = 0; i < block.length; i++) {
+ removeAttachedNodes(block[i], parent)
+ }
+ }
+}
+
function appendVnodeUpdatedHook(vnode: VNode, hook: () => void): void {
const props = (vnode.props ||= {})
const existing = props.onVnodeUpdated
@@ -869,7 +908,20 @@ function createVDOMComponent(
let rawRef: VNodeNormalizedRef | null = null
let isMounted = false
+ let isUnmounted = false
+ let isDomRemoved = false
+ const removeDom = (parentNode?: ParentNode): void => {
+ if (!parentNode || isDomRemoved) {
+ return
+ }
+ removeAttachedNodes(resolveVNodeNodes(vnode), parentNode)
+ isDomRemoved = true
+ }
const unmount = (parentNode?: ParentNode, transition?: TransitionHooks) => {
+ if (isUnmounted) {
+ if (!transition) removeDom(parentNode)
+ return
+ }
// unset ref
if (rawRef) vdomSetRef(rawRef, null, null, vnode, true)
if (transition) setVNodeTransitionHooks(vnode, transition)
@@ -883,13 +935,16 @@ function createVDOMComponent(
)
return
}
+ isUnmounted = true
+ isMounted = false
internals.umt(vnode.component!, null, !!parentNode)
+ // VDOM transitions own their leaving DOM until the leave finishes.
+ if (!transition) removeDom(parentNode)
}
frag.hydrate = () => {
if (!isHydrating) return
hydrateVNode(vnode, parentComponent as any)
- onScopeDispose(unmount, true)
isMounted = true
frag.nodes = resolveVNodeNodes(vnode)
frag.validityPending = false
@@ -927,7 +982,6 @@ function createVDOMComponent(
)
// set ref
if (rawRef) vdomSetRef(rawRef, null, null, vnode)
- onScopeDispose(unmount, true)
isMounted = true
} else {
// move