]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-vapor): improve fallback handling support chaining edison/feat/fowardedSlots 13408/head
authordaiwei <daiwei521@126.com>
Fri, 22 Aug 2025 07:39:15 +0000 (15:39 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 22 Aug 2025 07:39:15 +0000 (15:39 +0800)
packages/runtime-vapor/__tests__/componentSlots.spec.ts
packages/runtime-vapor/src/block.ts

index ea9c07dc025163f7e5cdf3a72792cd31ba4088bc..16022d51502f55585adc59a2433bc54ef5e62067 100644 (file)
@@ -870,971 +870,137 @@ describe('component: slots', () => {
       expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->bar<!--slot-->')
     })
 
-    describe('vdom interop', () => {
-      const createVaporSlot = (fallbackText = 'fallback') => {
-        return defineVaporComponent({
-          setup() {
-            const n0 = createSlot('foo', null, () => {
-              const n2 = template(`<div>${fallbackText}</div>`)()
-              return n2
-            })
-            return n0
-          },
-        })
-      }
-
-      const createVdomSlot = (fallbackText = 'fallback') => {
-        return {
-          render(this: any) {
-            return renderSlot(this.$slots, 'foo', {}, () => [
-              h('div', fallbackText),
-            ])
-          },
-        }
-      }
-
-      const createVaporForwardedSlot = (
-        targetComponent: any,
-        fallbackText?: string,
-      ) => {
-        return defineVaporComponent({
-          setup() {
-            const createForwardedSlot = forwardedSlotCreator()
-            const n2 = createComponent(
-              targetComponent,
-              null,
-              {
-                foo: () => {
-                  return fallbackText
-                    ? createForwardedSlot('foo', null, () => {
-                        const n2 = template(`<div>${fallbackText}</div>`)()
-                        return n2
-                      })
-                    : createForwardedSlot('foo', null)
-                },
-              },
-              true,
-            )
-            return n2
-          },
-        })
-      }
-
-      const createVdomForwardedSlot = (
-        targetComponent: any,
-        fallbackText?: string,
-      ) => {
-        return {
-          render(this: any) {
-            return h(targetComponent, null, {
-              foo: () => [
-                fallbackText
-                  ? renderSlot(this.$slots, 'foo', {}, () => [
-                      h('div', fallbackText),
-                    ])
-                  : renderSlot(this.$slots, 'foo'),
-              ],
-              _: 3 /* FORWARDED */,
-            })
-          },
-        }
-      }
-
-      const createMultipleVaporForwardedSlots = (
-        targetComponent: any,
-        count: number,
-      ) => {
-        let current = targetComponent
-        for (let i = 0; i < count; i++) {
-          current = createVaporForwardedSlot(current)
-        }
-        return current
-      }
-
-      const createMultipleVdomForwardedSlots = (
-        targetComponent: any,
-        count: number,
-      ) => {
-        let current = targetComponent
-        for (let i = 0; i < count; i++) {
-          current = createVdomForwardedSlot(current)
-        }
-        return current
-      }
-
-      const createTestApp = (
-        rootComponent: any,
-        foo: Ref<string>,
-        show: Ref<Boolean>,
-      ) => {
-        return {
-          setup() {
-            return () =>
-              h(
-                rootComponent,
-                null,
-                createSlots({ _: 2 /* DYNAMIC */ } as any, [
-                  show.value
-                    ? {
-                        name: 'foo',
-                        fn: () => [h('span', foo.value)],
-                        key: '0',
-                      }
-                    : undefined,
-                ]),
-              )
-          },
-        }
-      }
-
-      const createEmptyTestApp = (rootComponent: any) => {
-        return {
-          setup() {
-            return () => h(rootComponent)
-          },
-        }
-      }
-
-      test('vdom slot > vapor forwarded slot > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
-        const App = createTestApp(VaporForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot(with fallback) > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
-          VaporSlot,
-          'forwarded fallback',
-        )
-        const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>forwarded fallback</div><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VaporForwardedSlot = createVaporForwardedSlot(VdomSlot)
-        const App = createTestApp(VaporForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
-
-      test('vdom slot > vapor forwarded slot(with fallback) > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
-          VdomSlot,
-          'forwarded fallback',
-        )
-        const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>forwarded fallback</div>')
-      })
-
-      test('vdom slot > vapor forwarded slot > vdom forwarded slot > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
-        const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot)
-        const App = createTestApp(VaporForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
-
-      test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
-        const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
-          VdomForwardedSlot,
-          'forwarded fallback',
-        )
-        const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>forwarded fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
-
-      test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
-          VaporSlot,
-          'vdom fallback',
-        )
-        const VaporForwardedSlot = createVaporForwardedSlot(
-          VdomForwardedSlotWithFallback,
-        )
-        const App = createTestApp(VaporForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vdom fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
-
-      test('vdom slot(empty) > vapor forwarded slot > vdom forwarded slot(with fallback) > vapor slot', async () => {
-        const VaporSlot = createVaporSlot()
-        const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
-          VaporSlot,
-          'vdom fallback',
-        )
-        const VaporForwardedSlot = createVaporForwardedSlot(
-          VdomForwardedSlotWithFallback,
-        )
-        const App = createEmptyTestApp(VaporForwardedSlot)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<div>vdom fallback</div>')
-      })
-
-      test('vdom slot > vapor forwarded slot > vdom forwarded slot > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
-        const VaporForwardedSlot = createVaporForwardedSlot(VdomForwardedSlot)
-        const App = createTestApp(VaporForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
-
-      test('vdom slot > vapor forwarded slot(with fallback) > vdom forwarded slot > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
-        const VaporForwardedSlotWithFallback = createVaporForwardedSlot(
-          VdomForwardedSlot,
-          'vapor fallback',
-        )
-        const App = createTestApp(VaporForwardedSlotWithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vapor fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
-
-      test('vdom slot > vapor forwarded slot > vdom forwarded slot(with fallback) > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-
-        const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
-          VdomSlot,
-          'vdom fallback',
-        )
-        const VaporForwardedSlot = createVaporForwardedSlot(
-          VdomForwardedSlotWithFallback,
-        )
-        const App = createTestApp(VaporForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vdom fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
-
-      test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VdomForwardedSlot = createVdomForwardedSlot(VdomSlot)
-        const VaporForwardedSlot = createMultipleVaporForwardedSlots(
-          VdomForwardedSlot,
-          3,
-        )
-        const App = createTestApp(VaporForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div><!--slot--><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot (multiple) > vdom forwarded slot(with fallback) > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VdomForwardedSlotWithFallback = createVdomForwardedSlot(
-          VdomSlot,
-          'vdom fallback',
-        )
-        const VaporForwardedSlot = createMultipleVaporForwardedSlots(
-          VdomForwardedSlotWithFallback,
-          3,
-        )
-        const App = createTestApp(VaporForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe(
-          '<div>vdom fallback</div><!--slot--><!--slot-->',
-        )
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
-      })
-
-      test('vdom slot > vdom forwarded slot > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VdomForwardedSlot = createVdomForwardedSlot(VaporSlot)
-        const App = createTestApp(VdomForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
-
-      test('vdom slot > vdom forwarded slot > vapor forwarded slot > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
-        const VdomForwardedSlot = createVdomForwardedSlot(VaporForwardedSlot)
-        const App = createTestApp(VdomForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-      })
-
-      test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VaporForwardedSlot = createVaporForwardedSlot(VaporSlot)
-        const VdomForwardedSlot = createMultipleVdomForwardedSlots(
-          VaporForwardedSlot,
-          3,
-        )
-        const App = createTestApp(VdomForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-      })
-
-      test('vdom slot > vdom forwarded slot (multiple) > vapor forwarded slot(with fallback) > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VaporForwardedSlot = createVaporForwardedSlot(
-          VaporSlot,
-          'vapor fallback',
-        )
-        const VdomForwardedSlot = createMultipleVdomForwardedSlots(
-          VaporForwardedSlot,
-          3,
-        )
-        const App = createTestApp(VdomForwardedSlot, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vapor fallback</div><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot > vapor forwarded slot > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VaporForwardedSlot1 = createMultipleVaporForwardedSlots(
-          VdomSlot,
-          2,
-        )
-        const App = createTestApp(VaporForwardedSlot1, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VaporForwardedSlot2 = createVaporForwardedSlot(VdomSlot)
-        const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
-          VaporForwardedSlot2,
-          'vapor1 fallback',
-        )
-        const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vapor1 fallback</div><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot > vapor forwarded slot(with fallback) > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
-          VdomSlot,
-          'vapor2 fallback',
-        )
-        const VaporForwardedSlot1 = createVaporForwardedSlot(
-          VaporForwardedSlot2WithFallback,
-        )
-        const App = createTestApp(VaporForwardedSlot1, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vapor2 fallback</div><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot > vapor forwarded slot > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VaporForwardedSlot2 = createVaporForwardedSlot(VaporSlot)
-        const VaporForwardedSlot1 =
-          createVaporForwardedSlot(VaporForwardedSlot2)
-        const App = createTestApp(VaporForwardedSlot1, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>fallback</div><!--slot--><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
-          VdomSlot,
-          'vapor2 fallback',
-        )
-        const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
-          VaporForwardedSlot2WithFallback,
-          'vapor1 fallback',
-        )
-        const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vapor1 fallback</div><!--slot-->')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot-->')
-      })
-
-      test('vdom slot > vapor forwarded slot(with fallback) > vapor forwarded slot(with fallback) > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VaporForwardedSlot2WithFallback = createVaporForwardedSlot(
-          VaporSlot,
-          'vapor2 fallback',
-        )
-        const VaporForwardedSlot1WithFallback = createVaporForwardedSlot(
-          VaporForwardedSlot2WithFallback,
-          'vapor1 fallback',
-        )
-        const App = createTestApp(VaporForwardedSlot1WithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span><!--slot--><!--slot-->')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe(
-          '<div>vapor1 fallback</div><!--slot--><!--slot-->',
-        )
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span><!--slot--><!--slot-->')
-      })
-
-      test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
-          VaporSlot,
-          'vdom2 fallback',
-        )
-        const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
-          VdomForwardedSlot2WithFallback,
-          'vdom1 fallback',
-        )
-        const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
+    test('forwarded slot with fallback', async () => {
+      const Child = defineVaporComponent({
+        setup() {
+          return createSlot('default', null, () => template('child fallback')())
+        },
       })
 
-      test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) > vdom slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VdomSlot = createVdomSlot()
-        const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
-          VdomSlot,
-          'vdom2 fallback',
-        )
-        const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
-          VdomForwardedSlot2WithFallback,
-          'vdom1 fallback',
-        )
-        const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
-
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
+      const Parent = defineVaporComponent({
+        setup() {
+          const createForwardedSlot = forwardedSlotCreator()
+          const n2 = createComponent(Child, null, {
+            default: () => {
+              const n0 = createForwardedSlot('default', null, () => {
+                return template('<!-- <div></div> -->')()
+              })
+              return n0
+            },
+          })
+          return n2
+        },
       })
 
-      test('vdom slot > vdom forwarded slot(with fallback) > vdom forwarded slot(with fallback) (multiple) > vapor slot', async () => {
-        const foo = ref('foo')
-        const show = ref(true)
-
-        const VaporSlot = createVaporSlot()
-        const VdomForwardedSlot3WithFallback = createVdomForwardedSlot(
-          VaporSlot,
-          'vdom3 fallback',
-        )
-        const VdomForwardedSlot2WithFallback = createVdomForwardedSlot(
-          VdomForwardedSlot3WithFallback,
-          'vdom2 fallback',
-        )
-        const VdomForwardedSlot1WithFallback = createVdomForwardedSlot(
-          VdomForwardedSlot2WithFallback,
-          'vdom1 fallback',
-        )
-        const App = createTestApp(VdomForwardedSlot1WithFallback, foo, show)
-
-        const root = document.createElement('div')
-        createApp(App).use(vaporInteropPlugin).mount(root)
-        expect(root.innerHTML).toBe('<span>foo</span>')
-
-        foo.value = 'bar'
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-
-        show.value = false
-        await nextTick()
-        expect(root.innerHTML).toBe('<div>vdom1 fallback</div>')
+      const { html } = define({
+        setup() {
+          return createComponent(Parent, null, {
+            default: () => template('<!-- <div>App</div> -->')(),
+          })
+        },
+      }).render()
 
-        show.value = true
-        await nextTick()
-        expect(root.innerHTML).toBe('<span>bar</span>')
-      })
+      expect(html()).toBe('child fallback<!--slot--><!--slot-->')
     })
-  })
 
-  describe('forwarded slot', () => {
-    test('should work', async () => {
+    test('forwarded slot with fallback (v-if)', async () => {
       const Child = defineVaporComponent({
         setup() {
-          return createSlot('foo', null)
+          return createSlot('default', null, () => template('child fallback')())
         },
       })
+
+      const show = ref(false)
       const Parent = defineVaporComponent({
         setup() {
           const createForwardedSlot = forwardedSlotCreator()
-          const n2 = createComponent(
-            Child,
-            null,
-            {
-              foo: () => {
-                return createForwardedSlot('foo', null)
-              },
+          const n2 = createComponent(Child, null, {
+            default: () => {
+              const n0 = createForwardedSlot('default', null, () => {
+                const n2 = createIf(
+                  () => show.value,
+                  () => {
+                    const n4 = template('<div>if content</div>')()
+                    return n4
+                  },
+                )
+                return n2
+              })
+              return n0
             },
-            true,
-          )
+          })
           return n2
         },
       })
 
-      const foo = ref('foo')
-      const { host } = define({
+      const { html } = define({
         setup() {
-          const n2 = createComponent(
-            Parent,
-            null,
-            {
-              foo: () => {
-                const n0 = template(' ')() as any
-                renderEffect(() => setText(n0, foo.value))
-                return n0
-              },
-            },
-            true,
-          )
-          return n2
+          return createComponent(Parent, null, {
+            default: () => template('<!-- <div>App</div> -->')(),
+          })
         },
       }).render()
 
-      expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->')
+      expect(html()).toBe('child fallback<!--if--><!--slot--><!--slot-->')
 
-      foo.value = 'bar'
+      show.value = true
       await nextTick()
-      expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->')
+      expect(html()).toBe(
+        '<div>if content</div><!--if--><!--slot--><!--slot-->',
+      )
     })
 
-    test('mixed with non-forwarded slot', async () => {
+    test('forwarded slot with fallback (v-for)', async () => {
       const Child = defineVaporComponent({
         setup() {
-          return [createSlot('foo', null)]
+          return createSlot('default', null, () => template('child fallback')())
         },
       })
+
+      const items = ref<number[]>([])
       const Parent = defineVaporComponent({
         setup() {
           const createForwardedSlot = forwardedSlotCreator()
           const n2 = createComponent(Child, null, {
-            foo: () => {
-              const n0 = createForwardedSlot('foo', null)
+            default: () => {
+              const n0 = createForwardedSlot('default', null, () => {
+                const n2 = createFor(
+                  () => items.value,
+                  for_item0 => {
+                    const n4 = template('<span> </span>')() as any
+                    const x4 = child(n4) as any
+                    renderEffect(() =>
+                      setText(x4, toDisplayString(for_item0.value)),
+                    )
+                    return n4
+                  },
+                )
+                return n2
+              })
               return n0
             },
           })
-          const n3 = createSlot('default', null)
-          return [n2, n3]
+          return n2
         },
       })
 
-      const foo = ref('foo')
-      const { host } = define({
+      const { html } = define({
         setup() {
-          const n2 = createComponent(
-            Parent,
-            null,
-            {
-              foo: () => {
-                const n0 = template(' ')() as any
-                renderEffect(() => setText(n0, foo.value))
-                return n0
-              },
-              default: () => {
-                const n3 = template(' ')() as any
-                renderEffect(() => setText(n3, foo.value))
-                return n3
-              },
-            },
-            true,
-          )
-          return n2
+          return createComponent(Parent, null, {
+            default: () => template('<!-- <div>App</div> -->')(),
+          })
         },
       }).render()
 
-      expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->foo<!--slot-->')
+      expect(html()).toBe('child fallback<!--for--><!--slot--><!--slot-->')
 
-      foo.value = 'bar'
+      items.value.push(1)
       await nextTick()
-      expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->bar<!--slot-->')
+      expect(html()).toBe('<span>1</span><!--for--><!--slot--><!--slot-->')
+
+      items.value.pop()
+      await nextTick()
+      expect(html()).toBe('child fallback<!--for--><!--slot--><!--slot-->')
     })
 
     describe('vdom interop', () => {
index 9ccb0fb26a58fc365f89d79c0a14ed273d0d7f1d..87ba58527a9d6f842936d9b886797913c0b3b674 100644 (file)
@@ -102,12 +102,23 @@ export function setFragmentFallback(
   fragment: VaporFragment,
   fallback: BlockFn,
 ): void {
-  // stop recursion if fragment has its own fallback
-  if (fragment.fallback) return
+  if (fragment.fallback) {
+    const originalFallback = fragment.fallback
+    // if the original fallback also renders invalid blocks,
+    // this ensures proper fallback chaining
+    fragment.fallback = () => {
+      const fallbackNodes = originalFallback()
+      if (isValidBlock(fallbackNodes)) {
+        return fallbackNodes
+      }
+      return fallback()
+    }
+  } else {
+    fragment.fallback = fallback
+  }
 
-  fragment.fallback = fallback
   if (isFragment(fragment.nodes)) {
-    setFragmentFallback(fragment.nodes, fallback)
+    setFragmentFallback(fragment.nodes, fragment.fallback)
   }
 }