6
6
ErrorCodes ,
7
7
NodeTypes ,
8
8
type SimpleExpressionNode ,
9
+ type TemplateChildNode ,
9
10
createCompilerError ,
10
11
createSimpleExpression ,
11
12
} from '@vue/compiler-dom'
@@ -17,22 +18,26 @@ import {
17
18
isVoidTag ,
18
19
makeMap ,
19
20
} 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 ,
24
26
} from '../transform'
25
27
import {
28
+ type BlockIRNode ,
26
29
DynamicFlag ,
30
+ type DynamicSlotContent ,
27
31
IRDynamicPropsKind ,
28
32
IRNodeTypes ,
29
33
type IRProp ,
30
34
type IRProps ,
31
35
type IRPropsDynamicAttribute ,
32
36
type IRPropsStatic ,
37
+ type SlotContent ,
33
38
type VaporDirectiveNode ,
34
39
} from '../ir'
35
- import { EMPTY_EXPRESSION } from './utils'
40
+ import { EMPTY_EXPRESSION , newBlock } from './utils'
36
41
37
42
export const isReservedProp = /*#__PURE__*/ makeMap (
38
43
// the leading comma is intentional so empty string "" is also included
@@ -97,13 +102,19 @@ function transformComponentElement(
97
102
const root =
98
103
context . root === context . parent && context . parent . node . children . length === 1
99
104
105
+ const [ slots , dynamicSlots ] = buildSlotContent (
106
+ context as TransformContext < ElementNode > ,
107
+ )
108
+
100
109
context . registerOperation ( {
101
110
type : IRNodeTypes . CREATE_COMPONENT_NODE ,
102
111
id : context . reference ( ) ,
103
112
tag,
104
113
props : propsResult [ 0 ] ? propsResult [ 1 ] : [ propsResult [ 1 ] ] ,
105
114
resolve,
106
115
root,
116
+ slots,
117
+ dynamicSlots,
107
118
} )
108
119
}
109
120
@@ -350,3 +361,130 @@ function mergePropValues(existing: IRProp, incoming: IRProp) {
350
361
const newValues = incoming . values
351
362
existing . values . push ( ...newValues )
352
363
}
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