@@ -93,7 +93,10 @@ type ReferenceMap<DirectiveT> = Map<
9393> ;
9494
9595/** 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 [ ] > ;
97100
98101/**
99102 * Mapping between a scoped not and the template entities that exist in it.
@@ -162,7 +165,7 @@ export function findMatchingDirectivesAndPipes(template: string, directiveSelect
162165/** Object used to match template nodes to directives. */
163166export type DirectiveMatcher < DirectiveT extends DirectiveMeta > =
164167 | SelectorMatcher < DirectiveT [ ] >
165- | SelectorlessMatcher < DirectiveT [ ] > ;
168+ | SelectorlessMatcher < DirectiveT > ;
166169
167170/**
168171 * 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
182185 }
183186
184187 const directives : MatchedDirectives < DirectiveT > = new Map ( ) ;
188+ const ownedDirectives : OwnedDirectives < DirectiveT > = new Map ( ) ;
185189 const eagerDirectives : DirectiveT [ ] = [ ] ;
190+ const missingDirectives = new Set < string > ( ) ;
186191 const bindings : BindingsMap < DirectiveT > = new Map ( ) ;
187192 const references : ReferenceMap < DirectiveT > = new Map ( ) ;
188193 const scopedNodeEntities : ScopedNodeEntities = new Map ( ) ;
@@ -210,7 +215,9 @@ export class R3TargetBinder<DirectiveT extends DirectiveMeta> implements TargetB
210215 target . template ,
211216 this . directiveMatcher ,
212217 directives ,
218+ ownedDirectives ,
213219 eagerDirectives ,
220+ missingDirectives ,
214221 bindings ,
215222 references ,
216223 ) ;
@@ -247,7 +254,9 @@ export class R3TargetBinder<DirectiveT extends DirectiveMeta> implements TargetB
247254 return new R3BoundTarget (
248255 target ,
249256 directives ,
257+ ownedDirectives ,
250258 eagerDirectives ,
259+ missingDirectives ,
251260 bindings ,
252261 references ,
253262 expressions ,
@@ -503,7 +512,9 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
503512 private constructor (
504513 private directiveMatcher : DirectiveMatcher < DirectiveT > ,
505514 private directives : MatchedDirectives < DirectiveT > ,
515+ private ownedDirectives : OwnedDirectives < DirectiveT > ,
506516 private eagerDirectives : DirectiveT [ ] ,
517+ private missingDirectives : Set < string > ,
507518 private bindings : BindingsMap < DirectiveT > ,
508519 private references : ReferenceMap < DirectiveT > ,
509520 ) { }
@@ -524,14 +535,18 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
524535 template : Node [ ] ,
525536 directiveMatcher : DirectiveMatcher < DirectiveT > ,
526537 directives : MatchedDirectives < DirectiveT > ,
538+ ownedDirectives : OwnedDirectives < DirectiveT > ,
527539 eagerDirectives : DirectiveT [ ] ,
540+ missingDirectives : Set < string > ,
528541 bindings : BindingsMap < DirectiveT > ,
529542 references : ReferenceMap < DirectiveT > ,
530543 ) : void {
531544 const matcher = new DirectiveBinder (
532545 directiveMatcher ,
533546 directives ,
547+ ownedDirectives ,
534548 eagerDirectives ,
549+ missingDirectives ,
535550 bindings ,
536551 references ,
537552 ) ;
@@ -606,29 +621,30 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
606621 }
607622
608623 visitComponent ( node : Component ) : void {
609- const directives : DirectiveT [ ] = [ ] ;
610- let componentMetas : DirectiveT [ ] | null = null ;
611-
612624 if ( this . directiveMatcher instanceof SelectorlessMatcher ) {
613- componentMetas = this . directiveMatcher . match ( node . componentName ) ;
625+ const componentMatches = this . directiveMatcher . match ( node . componentName ) ;
626+ const directives : DirectiveT [ ] = [ ] ;
614627
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 ) ;
617633 }
618634
619635 for ( const directive of node . directives ) {
620636 const directiveMetas = this . directiveMatcher . match ( directive . name ) ;
621637
622- if ( directiveMetas !== null ) {
638+ if ( directiveMetas . length > 0 ) {
623639 directives . push ( ...directiveMetas ) ;
640+ this . ownedDirectives . set ( directive , directiveMetas ) ;
641+ } else {
642+ this . missingDirectives . add ( directive . name ) ;
624643 }
625644 }
626- }
627-
628- this . trackMatchedDirectives ( node , directives ) ;
629645
630- if ( componentMetas !== null ) {
631- this . trackSelectorlessBindings ( node , componentMetas ) ;
646+ this . trackMatchedDirectives ( node , directives ) ;
647+ this . trackSelectorlessBindings ( node , componentMatches ) ;
632648 }
633649
634650 node . directives . forEach ( ( directive ) => directive . visit ( this ) ) ;
@@ -641,7 +657,7 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
641657 ? this . directiveMatcher . match ( node . name )
642658 : null ;
643659
644- if ( directives !== null ) {
660+ if ( directives !== null && directives . length > 0 ) {
645661 this . trackSelectorlessBindings ( node , directives ) ;
646662 }
647663 }
@@ -663,8 +679,11 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
663679 for ( const directive of node . directives ) {
664680 const matchedDirectives = this . directiveMatcher . match ( directive . name ) ;
665681
666- if ( matchedDirectives !== null ) {
682+ if ( matchedDirectives . length > 0 ) {
667683 directives . push ( ...matchedDirectives ) ;
684+ this . ownedDirectives . set ( directive , matchedDirectives ) ;
685+ } else {
686+ this . missingDirectives . add ( directive . name ) ;
668687 }
669688 }
670689 }
@@ -687,6 +706,10 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
687706 }
688707
689708 private trackSelectorlessBindings ( node : Component | Directive , metas : DirectiveT [ ] ) : void {
709+ if ( metas . length === 0 ) {
710+ return ;
711+ }
712+
690713 const setBinding = (
691714 meta : DirectiveT ,
692715 attribute : BoundAttribute | BoundEvent | TextAttribute ,
@@ -706,9 +729,7 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
706729 // TODO(crisbeto): currently it's unclear how references should behave under selectorless,
707730 // given that there's one named class which can bring in multiple host directives.
708731 // 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 } ) ) ;
712733 }
713734
714735 private trackSelectorMatchedBindings ( node : Element | Template , directives : DirectiveT [ ] ) : void {
@@ -1118,7 +1139,9 @@ class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTarget<Dir
11181139 constructor (
11191140 readonly target : Target ,
11201141 private directives : MatchedDirectives < DirectiveT > ,
1142+ private ownedDirectives : OwnedDirectives < DirectiveT > ,
11211143 private eagerDirectives : DirectiveT [ ] ,
1144+ private missingDirectives : Set < string > ,
11221145 private bindings : BindingsMap < DirectiveT > ,
11231146 private references : ReferenceMap < DirectiveT > ,
11241147 private exprTargets : Map < AST , TemplateEntity > ,
@@ -1137,7 +1160,7 @@ class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTarget<Dir
11371160 return this . scopedNodeEntities . get ( node ) ?? new Set ( ) ;
11381161 }
11391162
1140- getDirectivesOfNode ( node : Element | Template ) : DirectiveT [ ] | null {
1163+ getDirectivesOfNode ( node : Element | Template | Component ) : DirectiveT [ ] | null {
11411164 return this . directives . get ( node ) || null ;
11421165 }
11431166
@@ -1251,7 +1274,7 @@ class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTarget<Dir
12511274 return null ;
12521275 }
12531276
1254- isDeferred ( element : Element | Component ) : boolean {
1277+ isDeferred ( element : Element ) : boolean {
12551278 for ( const block of this . deferredBlocks ) {
12561279 if ( ! this . deferredScopes . has ( block ) ) {
12571280 continue ;
@@ -1273,6 +1296,14 @@ class R3BoundTarget<DirectiveT extends DirectiveMeta> implements BoundTarget<Dir
12731296 return false ;
12741297 }
12751298
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+
12761307 /**
12771308 * Finds an entity with a specific name in a scope.
12781309 * @param rootNode Root node of the scope.
0 commit comments