@@ -52,6 +52,13 @@ export type DirectiveMatcherStrategy = (
5252 tNode : TElementNode | TContainerNode | TElementContainerNode ,
5353) => DirectiveDef < unknown > [ ] | null ;
5454
55+ /** Data produced after host directives are resolved for a node. */
56+ type HostDirectiveResolution = [
57+ matches : DirectiveDef < unknown > [ ] ,
58+ hostDirectiveDefs : HostDirectiveDefs | null ,
59+ hostDirectiveRanges : HostDirectiveRanges | null ,
60+ ] ;
61+
5562/**
5663 * Map that tracks a selector-matched directive to the range within which its host directives
5764 * are declared. Host directives for a specific directive are always contiguous within the runtime.
@@ -76,8 +83,16 @@ export function resolveDirectives(
7683 const matchedDirectiveDefs = directiveMatcher ( tView , tNode ) ;
7784
7885 if ( matchedDirectiveDefs !== null ) {
79- const [ directiveDefs , hostDirectiveDefs , hostDirectiveRanges ] =
80- resolveHostDirectives ( matchedDirectiveDefs ) ;
86+ let directiveDefs : DirectiveDef < unknown > [ ] ;
87+ let hostDirectiveDefs : HostDirectiveDefs | null = null ;
88+ let hostDirectiveRanges : HostDirectiveRanges | null = null ;
89+ const hostDirectiveResolution = resolveHostDirectives ( matchedDirectiveDefs ) ;
90+
91+ if ( hostDirectiveResolution === null ) {
92+ directiveDefs = matchedDirectiveDefs ;
93+ } else {
94+ [ directiveDefs , hostDirectiveDefs , hostDirectiveRanges ] = hostDirectiveResolution ;
95+ }
8196
8297 initializeDirectives (
8398 tView ,
@@ -116,15 +131,28 @@ function cacheMatchingLocalNames(
116131 }
117132}
118133
119- function resolveHostDirectives (
120- matches : DirectiveDef < unknown > [ ] ,
121- ) : [
122- matches : DirectiveDef < unknown > [ ] ,
123- hostDirectiveDefs : HostDirectiveDefs | null ,
124- hostDirectiveRanges : HostDirectiveRanges | null ,
125- ] {
126- const allDirectiveDefs : DirectiveDef < unknown > [ ] = [ ] ;
127- const hasComponent = matches . length > 0 && isComponentDef ( matches [ 0 ] ) ;
134+ function resolveHostDirectives ( matches : DirectiveDef < unknown > [ ] ) : HostDirectiveResolution | null {
135+ let componentDef : ComponentDef < unknown > | null = null ;
136+ let hasHostDirectives = false ;
137+
138+ for ( let i = 0 ; i < matches . length ; i ++ ) {
139+ const def = matches [ i ] ;
140+
141+ if ( i === 0 && isComponentDef ( def ) ) {
142+ componentDef = def ;
143+ }
144+
145+ if ( def . findHostDirectiveDefs !== null ) {
146+ hasHostDirectives = true ;
147+ break ;
148+ }
149+ }
150+
151+ if ( ! hasHostDirectives ) {
152+ return null ;
153+ }
154+
155+ let allDirectiveDefs : DirectiveDef < unknown > [ ] | null = null ;
128156 let hostDirectiveDefs : HostDirectiveDefs | null = null ;
129157 let hostDirectiveRanges : HostDirectiveRanges | null = null ;
130158
@@ -138,37 +166,28 @@ function resolveHostDirectives(
138166 // 2. Selector-matched component.
139167 // 3. Host directives belonging to selector-matched directives.
140168 // 4. Selector-matched dir
141- if ( hasComponent ) {
142- const def = matches [ 0 ] ;
169+ for ( const def of matches ) {
143170 if ( def . findHostDirectiveDefs !== null ) {
144- hostDirectiveRanges ??= new Map ( ) ;
171+ allDirectiveDefs ??= [ ] ;
145172 hostDirectiveDefs ??= new Map ( ) ;
146- resolveHostDirectivesForDef ( def , allDirectiveDefs , hostDirectiveRanges , hostDirectiveDefs ) ;
147- }
148- allDirectiveDefs . push ( def ) ;
149- }
150-
151- // If there's a component, we already processed it above so we can skip it here.
152- for ( let i = hasComponent ? 1 : 0 ; i < matches . length ; i ++ ) {
153- const def = matches [ i ] ;
154- if ( def . findHostDirectiveDefs !== null ) {
155173 hostDirectiveRanges ??= new Map ( ) ;
156- hostDirectiveDefs ??= new Map ( ) ;
157174 resolveHostDirectivesForDef ( def , allDirectiveDefs , hostDirectiveRanges , hostDirectiveDefs ) ;
158175 }
159- }
160176
161- if ( hasComponent ) {
162- allDirectiveDefs . push ( ...matches . slice ( 1 ) ) ;
163- } else {
164- allDirectiveDefs . push ( ...matches ) ;
177+ // Component definition needs to be pushed early to maintain the correct ordering.
178+ if ( def === componentDef ) {
179+ allDirectiveDefs ??= [ ] ;
180+ allDirectiveDefs . push ( def ) ;
181+ }
165182 }
166183
167- if ( ngDevMode ) {
168- assertNoDuplicateDirectives ( allDirectiveDefs ) ;
184+ if ( allDirectiveDefs !== null ) {
185+ allDirectiveDefs . push ( ...( componentDef === null ? matches : matches . slice ( 1 ) ) ) ;
186+ ngDevMode && assertNoDuplicateDirectives ( allDirectiveDefs ) ;
187+ return [ allDirectiveDefs , hostDirectiveDefs , hostDirectiveRanges ] ;
169188 }
170189
171- return [ allDirectiveDefs , hostDirectiveDefs , hostDirectiveRanges ] ;
190+ return null ;
172191}
173192
174193function resolveHostDirectivesForDef (
0 commit comments