feat(compiler-vapor): add support for forwarded slots

This commit is contained in:
daiwei 2025-05-29 17:04:49 +08:00
parent 1ef6e6edb7
commit dde7076120
7 changed files with 57 additions and 4 deletions

View File

@ -18,6 +18,7 @@ import {
genCall,
} from './generators/utils'
import { setTemplateRefIdent } from './generators/templateRef'
import { createForwardedSlotIdent } from './generators/slotOutlet'
export type CodegenOptions = Omit<BaseCodegenOptions, 'optimizeImports'>
@ -129,6 +130,12 @@ export function generate(
`const ${setTemplateRefIdent} = ${context.helper('createTemplateRefSetter')}()`,
)
}
if (ir.hasForwardedSlot) {
push(
NEWLINE,
`const ${createForwardedSlotIdent} = ${context.helper('forwardedSlotCreator')}()`,
)
}
push(...genBlockContent(ir.block, context, true))
push(INDENT_END, NEWLINE)

View File

@ -5,12 +5,14 @@ import { genExpression } from './expression'
import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils'
import { genRawProps } from './component'
export const createForwardedSlotIdent = `_createForwardedSlot`
export function genSlotOutlet(
oper: SlotOutletIRNode,
context: CodegenContext,
): CodeFragment[] {
const { helper } = context
const { id, name, fallback } = oper
const { id, name, fallback, forwarded } = oper
const [frag, push] = buildCodeFragment()
const nameExpr = name.isStatic
@ -26,7 +28,7 @@ export function genSlotOutlet(
NEWLINE,
`const n${id} = `,
...genCall(
helper('createSlot'),
forwarded ? createForwardedSlotIdent : helper('createSlot'),
nameExpr,
genRawProps(oper.props, context) || 'null',
fallbackArg,

View File

@ -66,6 +66,7 @@ export interface RootIRNode {
directive: Set<string>
block: BlockIRNode
hasTemplateRef: boolean
hasForwardedSlot: boolean
}
export interface IfIRNode extends BaseIRNode {
@ -209,6 +210,7 @@ export interface SlotOutletIRNode extends BaseIRNode {
name: SimpleExpressionNode
props: IRProps[]
fallback?: BlockIRNode
forwarded?: boolean
parent?: number
anchor?: number
}

View File

@ -230,6 +230,7 @@ export function transform(
directive: new Set(),
block: newBlock(node),
hasTemplateRef: false,
hasForwardedSlot: false,
}
const context = new TransformContext(ir, node, options)

View File

@ -5,6 +5,7 @@ import {
ErrorCodes,
NodeTypes,
type SimpleExpressionNode,
type TemplateChildNode,
createCompilerError,
createSimpleExpression,
isStaticArgOf,
@ -99,6 +100,13 @@ export const transformSlotOutlet: NodeTransform = (node, context) => {
}
return () => {
let forwarded = false
const slotNode = context.block.node
if (slotNode.type === NodeTypes.ELEMENT) {
forwarded = hasForwardedSlots(slotNode.children)
}
if (forwarded) context.ir.hasForwardedSlot = true
exitBlock && exitBlock()
context.dynamic.operation = {
type: IRNodeTypes.SLOT_OUTLET_NODE,
@ -106,6 +114,7 @@ export const transformSlotOutlet: NodeTransform = (node, context) => {
name: slotName,
props: irProps,
fallback,
forwarded,
}
}
}
@ -131,3 +140,21 @@ function createFallback(
context.reference()
return [fallback, exitBlock]
}
// TODO
function hasForwardedSlots(children: TemplateChildNode[]): boolean {
for (let i = 0; i < children.length; i++) {
const child = children[i]
switch (child.type) {
case NodeTypes.ELEMENT:
if (
child.tagType === ElementTypes.SLOT ||
hasForwardedSlots(child.children)
) {
return true
}
break
}
}
return false
}

View File

@ -87,10 +87,24 @@ export function getSlot(
}
}
export function forwardedSlotCreator(): (
name: string | (() => string),
rawProps?: LooseRawProps | null,
fallback?: VaporSlot,
) => Block {
const instance = currentInstance as VaporComponentInstance
return (
name: string | (() => string),
rawProps?: LooseRawProps | null,
fallback?: VaporSlot,
) => createSlot(name, rawProps, fallback, instance)
}
export function createSlot(
name: string | (() => string),
rawProps?: LooseRawProps | null,
fallback?: VaporSlot,
i?: VaporComponentInstance,
): Block {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
@ -98,7 +112,7 @@ export function createSlot(
locateHydrationNode()
}
const instance = currentInstance as VaporComponentInstance
const instance = i || (currentInstance as VaporComponentInstance)
const rawSlots = instance.rawSlots
const slotProps = rawProps
? new Proxy(rawProps, rawPropsProxyHandlers)

View File

@ -9,7 +9,7 @@ export { insert, prepend, remove, isFragment, VaporFragment } from './block'
export { setInsertionState } from './insertionState'
export { createComponent, createComponentWithFallback } from './component'
export { renderEffect } from './renderEffect'
export { createSlot } from './componentSlots'
export { createSlot, forwardedSlotCreator } from './componentSlots'
export { template } from './dom/template'
export { createTextNode, child, nthChild, next } from './dom/node'
export {