@@ -93,7 +93,10 @@ type ReferenceMap<DirectiveT> = Map<
93
93
> ;
94
94
95
95
/** Mapping between AST nodes and the directives that have been matched on them. */
96
- type MatchedDirectives < DirectiveT > = Map < Template | Element | Component , DirectiveT [ ] > ;
96
+ type MatchedDirectives < DirectiveT > = Map < Template | Element | Component | Directive , DirectiveT [ ] > ;
97
+
98
+ /** Mapping between AST nodes and the directives that they own. */
99
+ type OwnedDirectives < DirectiveT > = Map < Component | Directive , DirectiveT [ ] > ;
97
100
98
101
/**
99
102
* Mapping between a scoped not and the template entities that exist in it.
@@ -162,7 +165,7 @@ export function findMatchingDirectivesAndPipes(template: string, directiveSelect
162
165
/** Object used to match template nodes to directives. */
163
166
export type DirectiveMatcher < DirectiveT extends DirectiveMeta > =
164
167
| SelectorMatcher < DirectiveT [ ] >
165
- | SelectorlessMatcher < DirectiveT [ ] > ;
168
+ | SelectorlessMatcher < DirectiveT > ;
166
169
167
170
/**
168
171
* Processes `Target`s with a given set of directives and performs a binding operation, which
@@ -182,7 +185,9 @@ export class R3TargetBinder<DirectiveT extends DirectiveMeta> implements TargetB
182
185
}
183
186
184
187
const directives : MatchedDirectives < DirectiveT > = new Map ( ) ;
188
+ const ownedDirectives : OwnedDirectives < DirectiveT > = new Map ( ) ;
185
189
const eagerDirectives : DirectiveT [ ] = [ ] ;
190
+ const missingDirectives = new Set < string > ( ) ;
186
191
const bindings : BindingsMap < DirectiveT > = new Map ( ) ;
187
192
const references : ReferenceMap < DirectiveT > = new Map ( ) ;
188
193
const scopedNodeEntities : ScopedNodeEntities = new Map ( ) ;
@@ -210,7 +215,9 @@ export class R3TargetBinder<DirectiveT extends DirectiveMeta> implements TargetB
210
215
target . template ,
211
216
this . directiveMatcher ,
212
217
directives ,
218
+ ownedDirectives ,
213
219
eagerDirectives ,
220
+ missingDirectives ,
214
221
bindings ,
215
222
references ,
216
223
) ;
@@ -247,7 +254,9 @@ export class R3TargetBinder<DirectiveT extends DirectiveMeta> implements TargetB
247
254
return new R3BoundTarget (
248
255
target ,
249
256
directives ,
257
+ ownedDirectives ,
250
258
eagerDirectives ,
259
+ missingDirectives ,
251
260
bindings ,
252
261
references ,
253
262
expressions ,
@@ -503,7 +512,9 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
503
512
private constructor (
504
513
private directiveMatcher : DirectiveMatcher < DirectiveT > ,
505
514
private directives : MatchedDirectives < DirectiveT > ,
515
+ private ownedDirectives : OwnedDirectives < DirectiveT > ,
506
516
private eagerDirectives : DirectiveT [ ] ,
517
+ private missingDirectives : Set < string > ,
507
518
private bindings : BindingsMap < DirectiveT > ,
508
519
private references : ReferenceMap < DirectiveT > ,
509
520
) { }
@@ -524,14 +535,18 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
524
535
template : Node [ ] ,
525
536
directiveMatcher : DirectiveMatcher < DirectiveT > ,
526
537
directives : MatchedDirectives < DirectiveT > ,
538
+ ownedDirectives : OwnedDirectives < DirectiveT > ,
527
539
eagerDirectives : DirectiveT [ ] ,
540
+ missingDirectives : Set < string > ,
528
541
bindings : BindingsMap < DirectiveT > ,
529
542
references : ReferenceMap < DirectiveT > ,
530
543
) : void {
531
544
const matcher = new DirectiveBinder (
532
545
directiveMatcher ,
533
546
directives ,
547
+ ownedDirectives ,
534
548
eagerDirectives ,
549
+ missingDirectives ,
535
550
bindings ,
536
551
references ,
537
552
) ;
@@ -606,29 +621,30 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
606
621
}
607
622
608
623
visitComponent ( node : Component ) : void {
609
- const directives : DirectiveT [ ] = [ ] ;
610
- let componentMetas : DirectiveT [ ] | null = null ;
611
-
612
624
if ( this . directiveMatcher instanceof SelectorlessMatcher ) {
613
- componentMetas = this . directiveMatcher . match ( node . componentName ) ;
625
+ const componentMatches = this . directiveMatcher . match ( node . componentName ) ;
626
+ const directives : DirectiveT [ ] = [ ] ;
614
627
615
- if ( componentMetas !== null ) {
616
- directives . push ( ...componentMetas ) ;
628
+ if ( componentMatches . length > 0 ) {
629
+ directives . push ( ...componentMatches ) ;
630
+ this . ownedDirectives . set ( node , componentMatches ) ;
631
+ } else {
632
+ this . missingDirectives . add ( node . componentName ) ;
617
633
}
618
634
619
635
for ( const directive of node . directives ) {
620
636
const directiveMetas = this . directiveMatcher . match ( directive . name ) ;
621
637
622
- if ( directiveMetas !== null ) {
638
+ if ( directiveMetas . length > 0 ) {
623
639
directives . push ( ...directiveMetas ) ;
640
+ this . ownedDirectives . set ( directive , directiveMetas ) ;
641
+ } else {
642
+ this . missingDirectives . add ( directive . name ) ;
624
643
}
625
644
}
626
- }
627
-
628
- this . trackMatchedDirectives ( node , directives ) ;
629
645
630
- if ( componentMetas !== null ) {
631
- this . trackSelectorlessBindings ( node , componentMetas ) ;
646
+ this . trackMatchedDirectives ( node , directives ) ;
647
+ this . trackSelectorlessBindings ( node , componentMatches ) ;
632
648
}
633
649
634
650
node . directives . forEach ( ( directive ) => directive . visit ( this ) ) ;
@@ -641,7 +657,7 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
641
657
? this . directiveMatcher . match ( node . name )
642
658
: null ;
643
659
644
- if ( directives !== null ) {
660
+ if ( directives !== null && directives . length > 0 ) {
645
661
this . trackSelectorlessBindings ( node , directives ) ;
646
662
}
647
663
}
@@ -663,8 +679,11 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
663
679
for ( const directive of node . directives ) {
664
680
const matchedDirectives = this . directiveMatcher . match ( directive . name ) ;
665
681
666
- if ( matchedDirectives !== null ) {
682
+ if ( matchedDirectives . length > 0 ) {
667
683
directives . push ( ...matchedDirectives ) ;
684
+ this . ownedDirectives . set ( directive , matchedDirectives ) ;
685
+ } else {
686
+ this . missingDirectives . add ( directive . name ) ;
668
687
}
669
688
}
670
689
}
@@ -687,6 +706,10 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
687
706
}
688
707
689
708
private trackSelectorlessBindings ( node : Component | Directive , metas : DirectiveT [ ] ) : void {
709
+ if ( metas . length === 0 ) {
710
+ return ;
711
+ }
712
+
690
713
const setBinding = (
691
714
meta : DirectiveT ,
692
715
attribute : BoundAttribute | BoundEvent | TextAttribute ,
@@ -706,9 +729,7 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
706
729
// TODO(crisbeto): currently it's unclear how references should behave under selectorless,
707
730
// given that there's one named class which can bring in multiple host directives.
708
731
// For the time being only register the first directive as the reference target.
709
- if ( metas . length > 0 ) {
710
- node . references . forEach ( ( ref ) => this . references . set ( ref , { directive : metas [ 0 ] , node : node } ) ) ;
711
- }
732
+ node . references . forEach ( ( ref ) => this . references . set ( ref , { directive : metas [ 0 ] , node : node } ) ) ;
712
733
}
713
734
714
735
private trackSelectorMatchedBindings ( node : Element | Template , directives : DirectiveT [ ] ) : void {
@@ -1118,7 +1139,9 @@ class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTarget<Dir
1118
1139
constructor (
1119
1140
readonly target : Target ,
1120
1141
private directives : MatchedDirectives < DirectiveT > ,
1142
+ private ownedDirectives : OwnedDirectives < DirectiveT > ,
1121
1143
private eagerDirectives : DirectiveT [ ] ,
1144
+ private missingDirectives : Set < string > ,
1122
1145
private bindings : BindingsMap < DirectiveT > ,
1123
1146
private references : ReferenceMap < DirectiveT > ,
1124
1147
private exprTargets : Map < AST , TemplateEntity > ,
@@ -1137,7 +1160,7 @@ class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTarget<Dir
1137
1160
return this . scopedNodeEntities . get ( node ) ?? new Set ( ) ;
1138
1161
}
1139
1162
1140
- getDirectivesOfNode ( node : Element | Template ) : DirectiveT [ ] | null {
1163
+ getDirectivesOfNode ( node : Element | Template | Component ) : DirectiveT [ ] | null {
1141
1164
return this . directives . get ( node ) || null ;
1142
1165
}
1143
1166
@@ -1251,7 +1274,7 @@ class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTarget<Dir
1251
1274
return null ;
1252
1275
}
1253
1276
1254
- isDeferred ( element : Element | Component ) : boolean {
1277
+ isDeferred ( element : Element ) : boolean {
1255
1278
for ( const block of this . deferredBlocks ) {
1256
1279
if ( ! this . deferredScopes . has ( block ) ) {
1257
1280
continue ;
@@ -1273,6 +1296,14 @@ class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTarget<Dir
1273
1296
return false ;
1274
1297
}
1275
1298
1299
+ getOwnedDirectives ( node : Component | Directive ) : DirectiveT [ ] | null {
1300
+ return this . ownedDirectives . get ( node ) || null ;
1301
+ }
1302
+
1303
+ referencedDirectiveExists ( name : string ) : boolean {
1304
+ return ! this . missingDirectives . has ( name ) ;
1305
+ }
1306
+
1276
1307
/**
1277
1308
* Finds an entity with a specific name in a scope.
1278
1309
* @param rootNode Root node of the scope.
0 commit comments