Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1343093

Browse files
committedMay 7, 2024··
feat(compiler-vapor, runtime-vapor): impl comp slot content
related #154
1 parent b58d6a9 commit 1343093

File tree

4 files changed

+185
-5
lines changed

4 files changed

+185
-5
lines changed
 

‎packages/compiler-vapor/src/generators/component.ts

+24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
type IRProp,
77
type IRProps,
88
type IRPropsStatic,
9+
type SlotContent,
910
} from '../ir'
1011
import {
1112
type CodeFragment,
@@ -22,6 +23,7 @@ import { createSimpleExpression } from '@vue/compiler-dom'
2223
import { genEventHandler } from './event'
2324
import { genDirectiveModifiers, genDirectivesForElement } from './directive'
2425
import { genModelHandler } from './modelValue'
26+
import { genBlock } from './block'
2527

2628
// TODO: generate component slots
2729
export function genCreateComponent(
@@ -32,6 +34,7 @@ export function genCreateComponent(
3234

3335
const tag = genTag()
3436
const isRoot = oper.root
37+
const { slots, dynamicSlots } = oper
3538
const rawProps = genRawProps(oper.props, context)
3639

3740
return [
@@ -41,6 +44,8 @@ export function genCreateComponent(
4144
vaporHelper('createComponent'),
4245
tag,
4346
rawProps || (isRoot ? 'null' : false),
47+
slots ? genSlots(slots, context) : isRoot ? 'null' : false,
48+
dynamicSlots ? genSlots(dynamicSlots, context) : isRoot ? 'null' : false,
4449
isRoot && 'true',
4550
),
4651
...genDirectivesForElement(oper.id, context),
@@ -134,3 +139,22 @@ function genModelModifiers(
134139
const modifiersVal = genDirectiveModifiers(modelModifiers)
135140
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
136141
}
142+
143+
function genSlots(
144+
slots: SlotContent[],
145+
context: CodegenContext,
146+
): CodeFragment[] {
147+
const frags = genMulti(
148+
SEGMENTS_OBJECT_NEWLINE,
149+
...slots.map(({ name: key, block }) => {
150+
return [
151+
...(key.isStatic
152+
? [key.content]
153+
: ['[', ...genExpression(key, context), ']']),
154+
':',
155+
...genBlock(block, context),
156+
]
157+
}),
158+
)
159+
return frags
160+
}

‎packages/compiler-vapor/src/ir.ts

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DynamicSlot } from './../../runtime-vapor/src/componentSlots'
12
import type {
23
BindingTypes,
34
CompoundExpressionNode,
@@ -31,6 +32,7 @@ export enum IRNodeTypes {
3132
CREATE_TEXT_NODE,
3233
CREATE_COMPONENT_NODE,
3334
SLOT_OUTLET_NODE,
35+
DYNAMIC_SLOTS,
3436

3537
WITH_DIRECTIVE,
3638
DECLARE_OLD_REF, // consider make it more general
@@ -199,12 +201,23 @@ export interface WithDirectiveIRNode extends BaseIRNode {
199201
builtin?: VaporHelper
200202
}
201203

204+
export interface SlotContent {
205+
name: SimpleExpressionNode
206+
block: BlockIRNode
207+
}
208+
209+
export interface DynamicSlotContent extends SlotContent {
210+
key?: SimpleExpressionNode
211+
}
212+
202213
export interface CreateComponentIRNode extends BaseIRNode {
203214
type: IRNodeTypes.CREATE_COMPONENT_NODE
204215
id: number
205216
tag: string
206217
props: IRProps[]
207218
// TODO slots
219+
slots?: SlotContent[]
220+
dynamicSlots?: DynamicSlotContent[]
208221

209222
resolve: boolean
210223
root: boolean

‎packages/compiler-vapor/src/transforms/transformChildren.ts

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ export const transformChildren: NodeTransform = (node, context) => {
1818
(node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.TEMPLATE)
1919

2020
if (!isFragment && node.type !== NodeTypes.ELEMENT) return
21+
if (
22+
node.type === NodeTypes.ELEMENT &&
23+
node.tagType === ElementTypes.COMPONENT
24+
)
25+
return
2126

2227
for (const [i, child] of node.children.entries()) {
2328
const childContext = createContext(

‎packages/compiler-vapor/src/transforms/transformElement.ts

+143-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ErrorCodes,
77
NodeTypes,
88
type SimpleExpressionNode,
9+
type TemplateChildNode,
910
createCompilerError,
1011
createSimpleExpression,
1112
} from '@vue/compiler-dom'
@@ -17,22 +18,26 @@ import {
1718
isVoidTag,
1819
makeMap,
1920
} from '@vue/shared'
20-
import type {
21-
DirectiveTransformResult,
22-
NodeTransform,
23-
TransformContext,
21+
import {
22+
type DirectiveTransformResult,
23+
type NodeTransform,
24+
type TransformContext,
25+
transformNode,
2426
} from '../transform'
2527
import {
28+
type BlockIRNode,
2629
DynamicFlag,
30+
type DynamicSlotContent,
2731
IRDynamicPropsKind,
2832
IRNodeTypes,
2933
type IRProp,
3034
type IRProps,
3135
type IRPropsDynamicAttribute,
3236
type IRPropsStatic,
37+
type SlotContent,
3338
type VaporDirectiveNode,
3439
} from '../ir'
35-
import { EMPTY_EXPRESSION } from './utils'
40+
import { EMPTY_EXPRESSION, newBlock } from './utils'
3641

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

105+
const [slots, dynamicSlots] = buildSlotContent(
106+
context as TransformContext<ElementNode>,
107+
)
108+
100109
context.registerOperation({
101110
type: IRNodeTypes.CREATE_COMPONENT_NODE,
102111
id: context.reference(),
103112
tag,
104113
props: propsResult[0] ? propsResult[1] : [propsResult[1]],
105114
resolve,
106115
root,
116+
slots,
117+
dynamicSlots,
107118
})
108119
}
109120

@@ -350,3 +361,130 @@ function mergePropValues(existing: IRProp, incoming: IRProp) {
350361
const newValues = incoming.values
351362
existing.values.push(...newValues)
352363
}
364+
365+
function buildSlotContent(
366+
context: TransformContext<ElementNode>,
367+
): [slots?: SlotContent[], dynamicSlots?: DynamicSlotContent[]] {
368+
const node = context.node
369+
const slots: SlotContent[] = []
370+
const dynamicSlots: DynamicSlotContent[] = []
371+
if (!node.children.length) return []
372+
let explictlyNamedDefaultSlot = false
373+
const defaultTemplateNodes: TemplateChildNode[] = []
374+
375+
for (let i = 0; i < node.children.length; i++) {
376+
const child = node.children[i]
377+
if (
378+
child.type === NodeTypes.ELEMENT &&
379+
child.tagType === ElementTypes.TEMPLATE
380+
) {
381+
let slotDirective: VaporDirectiveNode | undefined
382+
let slotKey: SimpleExpressionNode | undefined
383+
let isVIf = false
384+
let isVFor = false
385+
for (const prop of child.props as (
386+
| AttributeNode
387+
| VaporDirectiveNode
388+
)[]) {
389+
if (
390+
!slotDirective &&
391+
prop.type === NodeTypes.DIRECTIVE &&
392+
prop.name === 'slot'
393+
) {
394+
slotDirective = prop
395+
} else if (
396+
prop.type === NodeTypes.DIRECTIVE &&
397+
prop.name === 'if' &&
398+
prop.exp
399+
) {
400+
isVIf = true
401+
} else if (
402+
prop.type === NodeTypes.DIRECTIVE &&
403+
prop.name === 'for' &&
404+
prop.exp
405+
) {
406+
isVFor = true
407+
} else if (
408+
prop.type === NodeTypes.ATTRIBUTE &&
409+
prop.name === 'key' &&
410+
prop.value
411+
) {
412+
slotKey = createSimpleExpression(prop.value?.content, true)
413+
} else if (
414+
prop.type === NodeTypes.DIRECTIVE &&
415+
prop.name === 'bind' &&
416+
prop.arg &&
417+
prop.arg.content === 'key' &&
418+
prop.exp
419+
) {
420+
slotKey = prop.exp
421+
}
422+
}
423+
const isDynamicSlot = isVIf || (isVFor && !slotDirective?.arg?.isStatic)
424+
const slotArg = slotDirective?.arg
425+
if (slotArg?.content === 'default') explictlyNamedDefaultSlot = true
426+
if (slotArg?.content) {
427+
const slotNode = isDynamicSlot
428+
? child
429+
: extend({}, child, {
430+
type: NodeTypes.ELEMENT,
431+
tag: 'template',
432+
props: [],
433+
tagType: ElementTypes.TEMPLATE,
434+
children: child.children,
435+
})
436+
const slotBlock = buildSlotBlock(context, slotNode)
437+
if (isDynamicSlot) {
438+
dynamicSlots.push(
439+
extend(
440+
{
441+
name: slotArg,
442+
block: slotBlock,
443+
},
444+
slotKey ? { key: slotKey } : {},
445+
),
446+
)
447+
} else {
448+
slots.push({
449+
name: slotArg,
450+
block: slotBlock,
451+
})
452+
}
453+
continue
454+
} else if (!explictlyNamedDefaultSlot) {
455+
!isDynamicSlot && defaultTemplateNodes.push(...child.children)
456+
}
457+
} else if (!explictlyNamedDefaultSlot) {
458+
defaultTemplateNodes.push(child)
459+
}
460+
}
461+
462+
if (!explictlyNamedDefaultSlot) {
463+
const defaultSlotNode = extend({}, node, {
464+
type: NodeTypes.ELEMENT,
465+
tag: 'template',
466+
props: [],
467+
tagType: ElementTypes.TEMPLATE,
468+
children: defaultTemplateNodes,
469+
})
470+
slots.push({
471+
name: createSimpleExpression('default', true),
472+
block: buildSlotBlock(context, defaultSlotNode),
473+
})
474+
}
475+
476+
return [slots, dynamicSlots]
477+
}
478+
479+
function buildSlotBlock(
480+
context: TransformContext<ElementNode>,
481+
slotNode: ElementNode,
482+
): BlockIRNode {
483+
const block = newBlock(slotNode)
484+
const exit = context.enterBlock(block)
485+
context.node = slotNode
486+
context.reference()
487+
transformNode(context)
488+
exit()
489+
return block
490+
}

0 commit comments

Comments
 (0)
Please sign in to comment.