Skip to content

Commit

Permalink
feat(compiler-vapor, runtime-vapor): impl comp slot content
Browse files Browse the repository at this point in the history
related #154
  • Loading branch information
Doctor-wu committed May 7, 2024
1 parent b58d6a9 commit 1343093
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 5 deletions.
24 changes: 24 additions & 0 deletions packages/compiler-vapor/src/generators/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type IRProp,
type IRProps,
type IRPropsStatic,
type SlotContent,
} from '../ir'
import {
type CodeFragment,
Expand All @@ -22,6 +23,7 @@ import { createSimpleExpression } from '@vue/compiler-dom'
import { genEventHandler } from './event'
import { genDirectiveModifiers, genDirectivesForElement } from './directive'
import { genModelHandler } from './modelValue'
import { genBlock } from './block'

// TODO: generate component slots
export function genCreateComponent(
Expand All @@ -32,6 +34,7 @@ export function genCreateComponent(

const tag = genTag()
const isRoot = oper.root
const { slots, dynamicSlots } = oper
const rawProps = genRawProps(oper.props, context)

return [
Expand All @@ -41,6 +44,8 @@ export function genCreateComponent(
vaporHelper('createComponent'),
tag,
rawProps || (isRoot ? 'null' : false),
slots ? genSlots(slots, context) : isRoot ? 'null' : false,
dynamicSlots ? genSlots(dynamicSlots, context) : isRoot ? 'null' : false,
isRoot && 'true',
),
...genDirectivesForElement(oper.id, context),
Expand Down Expand Up @@ -134,3 +139,22 @@ function genModelModifiers(
const modifiersVal = genDirectiveModifiers(modelModifiers)
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
}

function genSlots(
slots: SlotContent[],
context: CodegenContext,
): CodeFragment[] {
const frags = genMulti(
SEGMENTS_OBJECT_NEWLINE,
...slots.map(({ name: key, block }) => {
return [
...(key.isStatic
? [key.content]
: ['[', ...genExpression(key, context), ']']),
':',
...genBlock(block, context),
]
}),
)
return frags
}
13 changes: 13 additions & 0 deletions packages/compiler-vapor/src/ir.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DynamicSlot } from './../../runtime-vapor/src/componentSlots'

Check failure on line 1 in packages/compiler-vapor/src/ir.ts

View workflow job for this annotation

GitHub Actions / lint-and-test-dts

'DynamicSlot' is declared but its value is never read.
import type {
BindingTypes,
CompoundExpressionNode,
Expand Down Expand Up @@ -31,6 +32,7 @@ export enum IRNodeTypes {
CREATE_TEXT_NODE,
CREATE_COMPONENT_NODE,
SLOT_OUTLET_NODE,
DYNAMIC_SLOTS,

WITH_DIRECTIVE,
DECLARE_OLD_REF, // consider make it more general
Expand Down Expand Up @@ -199,12 +201,23 @@ export interface WithDirectiveIRNode extends BaseIRNode {
builtin?: VaporHelper
}

export interface SlotContent {
name: SimpleExpressionNode
block: BlockIRNode
}

export interface DynamicSlotContent extends SlotContent {
key?: SimpleExpressionNode
}

export interface CreateComponentIRNode extends BaseIRNode {
type: IRNodeTypes.CREATE_COMPONENT_NODE
id: number
tag: string
props: IRProps[]
// TODO slots
slots?: SlotContent[]
dynamicSlots?: DynamicSlotContent[]

resolve: boolean
root: boolean
Expand Down
5 changes: 5 additions & 0 deletions packages/compiler-vapor/src/transforms/transformChildren.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export const transformChildren: NodeTransform = (node, context) => {
(node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.TEMPLATE)

if (!isFragment && node.type !== NodeTypes.ELEMENT) return
if (
node.type === NodeTypes.ELEMENT &&
node.tagType === ElementTypes.COMPONENT
)
return

for (const [i, child] of node.children.entries()) {
const childContext = createContext(
Expand Down
148 changes: 143 additions & 5 deletions packages/compiler-vapor/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ErrorCodes,
NodeTypes,
type SimpleExpressionNode,
type TemplateChildNode,
createCompilerError,
createSimpleExpression,
} from '@vue/compiler-dom'
Expand All @@ -17,22 +18,26 @@ import {
isVoidTag,
makeMap,
} from '@vue/shared'
import type {
DirectiveTransformResult,
NodeTransform,
TransformContext,
import {
type DirectiveTransformResult,
type NodeTransform,
type TransformContext,
transformNode,
} from '../transform'
import {
type BlockIRNode,
DynamicFlag,
type DynamicSlotContent,
IRDynamicPropsKind,
IRNodeTypes,
type IRProp,
type IRProps,
type IRPropsDynamicAttribute,
type IRPropsStatic,
type SlotContent,
type VaporDirectiveNode,
} from '../ir'
import { EMPTY_EXPRESSION } from './utils'
import { EMPTY_EXPRESSION, newBlock } from './utils'

export const isReservedProp = /*#__PURE__*/ makeMap(
// the leading comma is intentional so empty string "" is also included
Expand Down Expand Up @@ -97,13 +102,19 @@ function transformComponentElement(
const root =
context.root === context.parent && context.parent.node.children.length === 1

const [slots, dynamicSlots] = buildSlotContent(
context as TransformContext<ElementNode>,
)

context.registerOperation({
type: IRNodeTypes.CREATE_COMPONENT_NODE,
id: context.reference(),
tag,
props: propsResult[0] ? propsResult[1] : [propsResult[1]],
resolve,
root,
slots,
dynamicSlots,
})
}

Expand Down Expand Up @@ -350,3 +361,130 @@ function mergePropValues(existing: IRProp, incoming: IRProp) {
const newValues = incoming.values
existing.values.push(...newValues)
}

function buildSlotContent(
context: TransformContext<ElementNode>,
): [slots?: SlotContent[], dynamicSlots?: DynamicSlotContent[]] {
const node = context.node
const slots: SlotContent[] = []
const dynamicSlots: DynamicSlotContent[] = []
if (!node.children.length) return []
let explictlyNamedDefaultSlot = false
const defaultTemplateNodes: TemplateChildNode[] = []

for (let i = 0; i < node.children.length; i++) {
const child = node.children[i]
if (
child.type === NodeTypes.ELEMENT &&
child.tagType === ElementTypes.TEMPLATE
) {
let slotDirective: VaporDirectiveNode | undefined
let slotKey: SimpleExpressionNode | undefined
let isVIf = false
let isVFor = false
for (const prop of child.props as (
| AttributeNode
| VaporDirectiveNode
)[]) {
if (
!slotDirective &&
prop.type === NodeTypes.DIRECTIVE &&
prop.name === 'slot'
) {
slotDirective = prop
} else if (
prop.type === NodeTypes.DIRECTIVE &&
prop.name === 'if' &&
prop.exp
) {
isVIf = true
} else if (
prop.type === NodeTypes.DIRECTIVE &&
prop.name === 'for' &&
prop.exp
) {
isVFor = true
} else if (
prop.type === NodeTypes.ATTRIBUTE &&
prop.name === 'key' &&
prop.value
) {
slotKey = createSimpleExpression(prop.value?.content, true)
} else if (
prop.type === NodeTypes.DIRECTIVE &&
prop.name === 'bind' &&
prop.arg &&
prop.arg.content === 'key' &&
prop.exp
) {
slotKey = prop.exp
}
}
const isDynamicSlot = isVIf || (isVFor && !slotDirective?.arg?.isStatic)
const slotArg = slotDirective?.arg
if (slotArg?.content === 'default') explictlyNamedDefaultSlot = true
if (slotArg?.content) {
const slotNode = isDynamicSlot
? child
: extend({}, child, {
type: NodeTypes.ELEMENT,
tag: 'template',
props: [],
tagType: ElementTypes.TEMPLATE,
children: child.children,
})
const slotBlock = buildSlotBlock(context, slotNode)
if (isDynamicSlot) {
dynamicSlots.push(
extend(
{
name: slotArg,
block: slotBlock,
},
slotKey ? { key: slotKey } : {},
),
)
} else {
slots.push({
name: slotArg,
block: slotBlock,
})
}
continue
} else if (!explictlyNamedDefaultSlot) {
!isDynamicSlot && defaultTemplateNodes.push(...child.children)
}
} else if (!explictlyNamedDefaultSlot) {
defaultTemplateNodes.push(child)
}
}

if (!explictlyNamedDefaultSlot) {
const defaultSlotNode = extend({}, node, {
type: NodeTypes.ELEMENT,
tag: 'template',
props: [],
tagType: ElementTypes.TEMPLATE,
children: defaultTemplateNodes,
})
slots.push({
name: createSimpleExpression('default', true),
block: buildSlotBlock(context, defaultSlotNode),
})
}

return [slots, dynamicSlots]
}

function buildSlotBlock(
context: TransformContext<ElementNode>,
slotNode: ElementNode,
): BlockIRNode {
const block = newBlock(slotNode)
const exit = context.enterBlock(block)
context.node = slotNode
context.reference()
transformNode(context)
exit()
return block
}

0 comments on commit 1343093

Please sign in to comment.