}"
`;
+exports[`compiler: transform slot > forwarded slots > <slot w/ nested component> 1`] = `
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+ const _createForwardedSlot = _forwardedSlotCreator()
+ const _component_Comp = _resolveComponent("Comp")
+ const n2 = _createComponentWithFallback(_component_Comp, null, {
+ "default": () => {
+ const n1 = _createComponentWithFallback(_component_Comp, null, {
+ "default": () => {
+ const n0 = _createForwardedSlot("default", null)
+ return n0
+ }
+ })
+ return n1
+ }
+ }, true)
+ return n2
+}"
+`;
+
+exports[`compiler: transform slot > forwarded slots > <slot> tag only 1`] = `
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+ const _createForwardedSlot = _forwardedSlotCreator()
+ const _component_Comp = _resolveComponent("Comp")
+ const n1 = _createComponentWithFallback(_component_Comp, null, {
+ "default": () => {
+ const n0 = _createForwardedSlot("default", null)
+ return n0
+ }
+ }, true)
+ return n1
+}"
+`;
+
+exports[`compiler: transform slot > forwarded slots > <slot> tag w/ template 1`] = `
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+ const _createForwardedSlot = _forwardedSlotCreator()
+ const _component_Comp = _resolveComponent("Comp")
+ const n2 = _createComponentWithFallback(_component_Comp, null, {
+ "default": () => {
+ const n0 = _createForwardedSlot("default", null)
+ return n0
+ }
+ }, true)
+ return n2
+}"
+`;
+
+exports[`compiler: transform slot > forwarded slots > <slot> tag w/ v-for 1`] = `
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createFor as _createFor, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+ const _createForwardedSlot = _forwardedSlotCreator()
+ const _component_Comp = _resolveComponent("Comp")
+ const n3 = _createComponentWithFallback(_component_Comp, null, {
+ "default": () => {
+ const n0 = _createFor(() => (_ctx.b), (_for_item0) => {
+ const n2 = _createForwardedSlot("default", null)
+ return n2
+ })
+ return n0
+ }
+ }, true)
+ return n3
+}"
+`;
+
+exports[`compiler: transform slot > forwarded slots > <slot> tag w/ v-if 1`] = `
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createIf as _createIf, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+ const _createForwardedSlot = _forwardedSlotCreator()
+ const _component_Comp = _resolveComponent("Comp")
+ const n3 = _createComponentWithFallback(_component_Comp, null, {
+ "default": () => {
+ const n0 = _createIf(() => (_ctx.ok), () => {
+ const n2 = _createForwardedSlot("default", null)
+ return n2
+ })
+ return n0
+ }
+ }, true)
+ return n3
+}"
+`;
+
exports[`compiler: transform slot > implicit default slot 1`] = `
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
const t0 = _template("<div></div>")
})
})
+ describe('forwarded slots', () => {
+ test('<slot> tag only', () => {
+ const { code } = compileWithSlots(`<Comp><slot/></Comp>`)
+ expect(code).toMatchSnapshot()
+ })
+
+ test('<slot> tag w/ v-if', () => {
+ const { code } = compileWithSlots(`<Comp><slot v-if="ok"/></Comp>`)
+ expect(code).toMatchSnapshot()
+ })
+
+ test('<slot> tag w/ v-for', () => {
+ const { code } = compileWithSlots(`<Comp><slot v-for="a in b"/></Comp>`)
+ expect(code).toMatchSnapshot()
+ })
+
+ test('<slot> tag w/ template', () => {
+ const { code } = compileWithSlots(
+ `<Comp><template #default><slot/></template></Comp>`,
+ )
+ expect(code).toMatchSnapshot()
+ })
+
+ test('<slot w/ nested component>', () => {
+ const { code } = compileWithSlots(`<Comp><Comp><slot/></Comp></Comp>`)
+ expect(code).toMatchSnapshot()
+ })
+ })
+
describe('errors', () => {
test('error on extraneous children w/ named default slot', () => {
const onError = vi.fn()
inVOnce: boolean = false
inVFor: number = 0
+ inSlot: number = 0
comment: CommentNode[] = []
component: Set<string> = this.ir.component
}
return () => {
- let forwarded = false
- const slotNode = context.block.node
- if (slotNode.type === NodeTypes.ELEMENT) {
- forwarded = hasForwardedSlots(slotNode.children)
- }
+ const {
+ block: { node: slotNode },
+ inSlot,
+ } = context
+ const forwarded =
+ inSlot !== 0 &&
+ slotNode.type === NodeTypes.ELEMENT &&
+ hasForwardedSlots(slotNode.children)
if (forwarded) context.ir.hasForwardedSlot = true
exitBlock && exitBlock()
return [fallback, exitBlock]
}
-// TODO
function hasForwardedSlots(children: TemplateChildNode[]): boolean {
for (let i = 0; i < children.length; i++) {
const child = children[i]
const block: SlotBlockIRNode = newBlock(slotNode)
block.props = dir && dir.exp
const exitBlock = context.enterBlock(block)
- return [block, exitBlock]
+ context.inSlot++
+ return [
+ block,
+ () => {
+ context.inSlot--
+ exitBlock()
+ },
+ ]
}
function isNonWhitespaceContent(node: TemplateChildNode): boolean {
createSlot,
createVaporApp,
defineVaporComponent,
+ forwardedSlotCreator,
insert,
prepend,
renderEffect,
import { currentInstance, nextTick, ref } from '@vue/runtime-dom'
import { makeRender } from './_utils'
import type { DynamicSlot } from '../src/componentSlots'
-import { setElementText } from '../src/dom/prop'
+import { setElementText, setText } from '../src/dom/prop'
const define = makeRender<any>()
expect(host.innerHTML).toBe('<div><h1></h1><!--slot--></div>')
})
})
+
+ describe('forwarded slot', () => {
+ test('should work', async () => {
+ const Child = defineVaporComponent({
+ setup() {
+ return createSlot('foo', null)
+ },
+ })
+ const Parent = defineVaporComponent({
+ setup() {
+ const createForwardedSlot = forwardedSlotCreator()
+ const n2 = createComponent(
+ Child,
+ null,
+ {
+ foo: () => {
+ return createForwardedSlot('foo', null)
+ },
+ },
+ true,
+ )
+ return n2
+ },
+ })
+
+ const foo = ref('foo')
+ const { host } = define({
+ setup() {
+ const n2 = createComponent(
+ Parent,
+ null,
+ {
+ foo: () => {
+ const n0 = template(' ')() as any
+ renderEffect(() => setText(n0, foo.value))
+ return n0
+ },
+ },
+ true,
+ )
+ return n2
+ },
+ }).render()
+
+ expect(host.innerHTML).toBe('foo<!--slot--><!--slot-->')
+
+ foo.value = 'bar'
+ await nextTick()
+ expect(host.innerHTML).toBe('bar<!--slot--><!--slot-->')
+ })
+ })
})