1- import { createCompilerError , ElementTypes , ErrorCodes , type NodeTransform , NodeTypes } from '@vue/compiler-dom' ;
1+ import {
2+ createCompilerError ,
3+ type DirectiveNode ,
4+ ElementTypes ,
5+ ErrorCodes ,
6+ findDir ,
7+ isStaticExp ,
8+ isTemplateNode ,
9+ type NodeTransform ,
10+ NodeTypes ,
11+ type TemplateChildNode ,
12+ } from '@vue/compiler-dom' ;
213import { isBuiltInDirective } from '@vue/shared' ;
314
415export const transformElement : NodeTransform = ( node , context ) => {
@@ -10,10 +21,6 @@ export const transformElement: NodeTransform = (node, context) => {
1021 const isComponent = node . tagType === ElementTypes . COMPONENT ;
1122 const isSlotOutlet = node . tagType === ElementTypes . SLOT ;
1223
13- if ( isComponent ) {
14- context . components . add ( node . tag ) ;
15- }
16-
1724 for ( const prop of node . props ) {
1825 if ( prop . type !== NodeTypes . DIRECTIVE ) {
1926 continue ;
@@ -45,12 +52,86 @@ export const transformElement: NodeTransform = (node, context) => {
4552 continue ;
4653 }
4754
48- const result = context . directiveTransforms [ prop . name ] ?.( prop , node , context ) ;
49- if ( isSlotOutlet && ( result ?. needRuntime || ! isBuiltInDirective ( prop . name ) ) ) {
55+ const runtimeDirectives : DirectiveNode [ ] = [ ] ;
56+ const directiveTransform = context . directiveTransforms [ prop . name ] ;
57+ if ( directiveTransform ) {
58+ const { needRuntime } = directiveTransform ( prop , node , context ) ;
59+ if ( needRuntime ) {
60+ runtimeDirectives . push ( prop ) ;
61+ }
62+ }
63+ else if ( ! isBuiltInDirective ( prop . name ) ) {
64+ runtimeDirectives . push ( prop ) ;
65+ }
66+
67+ if ( isSlotOutlet && runtimeDirectives . length ) {
5068 context . onError (
5169 createCompilerError ( ErrorCodes . X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET , prop . loc ) ,
5270 ) ;
5371 }
5472 }
73+
74+ if ( isComponent ) {
75+ let hasTemplateSlots = false ;
76+ let hasNamedDefaultSlot = false ;
77+ const implicitDefaultChildren : TemplateChildNode [ ] = [ ] ;
78+ const seenSlotNames = new Set < string > ( ) ;
79+ const onComponentSlot = findDir ( node , 'slot' , true ) ;
80+
81+ for ( const child of node . children ) {
82+ let slotDir : DirectiveNode | undefined ;
83+ if ( ! isTemplateNode ( child ) || ! ( slotDir = findDir ( child , 'slot' , true ) ) ) {
84+ if ( child . type !== NodeTypes . COMMENT ) {
85+ implicitDefaultChildren . push ( child ) ;
86+ }
87+ continue ;
88+ }
89+
90+ if ( onComponentSlot ) {
91+ context . onError (
92+ createCompilerError ( ErrorCodes . X_V_SLOT_MIXED_SLOT_USAGE , slotDir . loc ) ,
93+ ) ;
94+ break ;
95+ }
96+
97+ if ( findDir ( child , / ^ (?: i f | e l s e - i f | e l s e | f o r ) $ / , true ) ) {
98+ continue ;
99+ }
100+
101+ hasTemplateSlots = true ;
102+ const staticSlotName = slotDir . arg
103+ ? isStaticExp ( slotDir . arg )
104+ ? slotDir . arg . content
105+ : undefined
106+ : 'default' ;
107+
108+ if ( staticSlotName ) {
109+ if ( seenSlotNames . has ( staticSlotName ) ) {
110+ context . onError (
111+ createCompilerError ( ErrorCodes . X_V_SLOT_DUPLICATE_SLOT_NAMES , slotDir . loc ) ,
112+ ) ;
113+ continue ;
114+ }
115+ seenSlotNames . add ( staticSlotName ) ;
116+ if ( staticSlotName === 'default' ) {
117+ hasNamedDefaultSlot = true ;
118+ }
119+ }
120+ }
121+
122+ if (
123+ hasTemplateSlots && hasNamedDefaultSlot
124+ && implicitDefaultChildren . some ( node => node . type !== NodeTypes . TEXT || ! ! node . content . trim ( ) )
125+ ) {
126+ context . onError (
127+ createCompilerError (
128+ ErrorCodes . X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN ,
129+ implicitDefaultChildren [ 0 ] ! . loc ,
130+ ) ,
131+ ) ;
132+ }
133+
134+ context . components . add ( node . tag ) ;
135+ }
55136 } ;
56137} ;
0 commit comments