@@ -91,7 +91,6 @@ export class Compiler {
9191 private _appUrl : string ;
9292 private _render : renderApi . RenderCompiler ;
9393 private _protoViewFactory : ProtoViewFactory ;
94- private _protoViewsToBeMerged : AppProtoView [ ] = [ ] ;
9594
9695 /**
9796 * @private
@@ -141,55 +140,28 @@ export class Compiler {
141140 hostPvPromise =
142141 this . _render . compileHost ( directiveMetadata )
143142 . then ( ( hostRenderPv ) => {
144- var protoView = this . _protoViewFactory . createAppProtoViews (
143+ var protoViews = this . _protoViewFactory . createAppProtoViews (
145144 componentBinding , hostRenderPv , [ componentBinding ] ) ;
146- this . _compilerCache . setHost ( componentType , protoView ) ;
147- return this . _compileNestedProtoViews ( hostRenderPv , protoView , componentType ) ;
145+ return this . _compileNestedProtoViews ( protoViews , componentType , new Map ( ) ) ;
146+ } )
147+ . then ( ( appProtoView ) => {
148+ this . _compilerCache . setHost ( componentType , appProtoView ) ;
149+ return appProtoView ;
148150 } ) ;
149151 }
150- return hostPvPromise . then ( hostAppProtoView =>
151- this . _mergeUnmergedProtoViews ( ) . then ( _ => hostAppProtoView . ref ) ) ;
152+ return hostPvPromise . then ( hostAppProtoView => hostAppProtoView . ref ) ;
152153 }
153154
154- private _mergeUnmergedProtoViews ( ) : Promise < any > {
155- var protoViewsToBeMerged = this . _protoViewsToBeMerged ;
156- this . _protoViewsToBeMerged = [ ] ;
157- return PromiseWrapper . all ( protoViewsToBeMerged . map ( ( appProtoView ) => {
158- return this . _render . mergeProtoViewsRecursively (
159- this . _collectMergeRenderProtoViews ( appProtoView ) )
160- . then ( ( mergeResult : renderApi . RenderProtoViewMergeMapping ) => {
161- appProtoView . mergeMapping = new AppProtoViewMergeMapping ( mergeResult ) ;
162- } ) ;
163- } ) ) ;
164- }
165-
166- private _collectMergeRenderProtoViews (
167- appProtoView : AppProtoView ) : List < renderApi . RenderProtoViewRef | List < any > > {
168- var result = [ appProtoView . render ] ;
169- for ( var i = 0 ; i < appProtoView . elementBinders . length ; i ++ ) {
170- var binder = appProtoView . elementBinders [ i ] ;
171- if ( isPresent ( binder . nestedProtoView ) ) {
172- if ( binder . hasStaticComponent ( ) ||
173- ( binder . hasEmbeddedProtoView ( ) && binder . nestedProtoView . isEmbeddedFragment ) ) {
174- result . push ( this . _collectMergeRenderProtoViews ( binder . nestedProtoView ) ) ;
175- } else {
176- result . push ( null ) ;
177- }
178- }
179- }
180- return result ;
181- }
182-
183- private _compile ( componentBinding : DirectiveBinding ) : Promise < AppProtoView > | AppProtoView {
155+ private _compile ( componentBinding : DirectiveBinding ,
156+ componentPath : Map < Type , AppProtoView > ) : Promise < AppProtoView > |
157+ AppProtoView {
184158 var component = < Type > componentBinding . key . token ;
185159 var protoView = this . _compilerCache . get ( component ) ;
186160 if ( isPresent ( protoView ) ) {
187161 // The component has already been compiled into an AppProtoView,
188- // returns a plain AppProtoView, not wrapped inside of a Promise.
189- // Needed for recursive components.
162+ // returns a plain AppProtoView, not wrapped inside of a Promise, for performance reasons.
190163 return protoView ;
191164 }
192-
193165 var resultPromise = this . _compiling . get ( component ) ;
194166 if ( isPresent ( resultPromise ) ) {
195167 // The component is already being compiled, attach to the existing Promise
@@ -212,17 +184,18 @@ export class Compiler {
212184 ListWrapper . map ( directives , ( directive ) => this . _bindDirective ( directive ) ) ) ;
213185
214186 var renderTemplate = this . _buildRenderTemplate ( component , view , boundDirectives ) ;
215- resultPromise = this . _render . compile ( renderTemplate )
216- . then ( ( renderPv ) => {
217- var protoView = this . _protoViewFactory . createAppProtoViews (
218- componentBinding , renderPv , boundDirectives ) ;
219- // Populate the cache before compiling the nested components,
220- // so that components can reference themselves in their template.
221- this . _compilerCache . set ( component , protoView ) ;
222- MapWrapper . delete ( this . _compiling , component ) ;
223-
224- return this . _compileNestedProtoViews ( renderPv , protoView , component ) ;
225- } ) ;
187+ resultPromise =
188+ this . _render . compile ( renderTemplate )
189+ . then ( ( renderPv ) => {
190+ var protoViews = this . _protoViewFactory . createAppProtoViews (
191+ componentBinding , renderPv , boundDirectives ) ;
192+ return this . _compileNestedProtoViews ( protoViews , component , componentPath ) ;
193+ } )
194+ . then ( ( appProtoView ) => {
195+ this . _compilerCache . set ( component , appProtoView ) ;
196+ MapWrapper . delete ( this . _compiling , component ) ;
197+ return appProtoView ;
198+ } ) ;
226199 this . _compiling . set ( component , resultPromise ) ;
227200 return resultPromise ;
228201 }
@@ -233,67 +206,83 @@ export class Compiler {
233206 return MapWrapper . values ( directivesMap ) ;
234207 }
235208
236- private _compileNestedProtoViews ( renderProtoView : renderApi . ProtoViewDto ,
237- appProtoView : AppProtoView ,
238- componentType : Type ) : Promise < AppProtoView > {
209+ private _compileNestedProtoViews ( appProtoViews : AppProtoView [ ] , componentType : Type ,
210+ componentPath : Map < Type , AppProtoView > ) : Promise < AppProtoView > {
239211 var nestedPVPromises = [ ] ;
240- this . _loopComponentElementBinders ( appProtoView , ( parentPv , elementBinder : ElementBinder ) => {
241- var nestedComponent = elementBinder . componentDirective ;
242- var elementBinderDone =
243- ( nestedPv : AppProtoView ) => { elementBinder . nestedProtoView = nestedPv ; } ;
244- var nestedCall = this . _compile ( nestedComponent ) ;
245- if ( isPromise ( nestedCall ) ) {
246- nestedPVPromises . push ( ( < Promise < AppProtoView > > nestedCall ) . then ( elementBinderDone ) ) ;
247- } else {
248- elementBinderDone ( < AppProtoView > nestedCall ) ;
249- }
212+ componentPath = MapWrapper . clone ( componentPath ) ;
213+ if ( appProtoViews [ 0 ] . type === renderApi . ViewType . COMPONENT ) {
214+ componentPath . set ( componentType , appProtoViews [ 0 ] ) ;
215+ }
216+ appProtoViews . forEach ( appProtoView => {
217+ this . _collectComponentElementBinders ( appProtoView )
218+ . forEach ( ( elementBinder : ElementBinder ) => {
219+ var nestedComponent = elementBinder . componentDirective ;
220+ var nestedComponentType = < Type > nestedComponent . key . token ;
221+ var elementBinderDone =
222+ ( nestedPv : AppProtoView ) => { elementBinder . nestedProtoView = nestedPv ; } ;
223+ if ( componentPath . has ( nestedComponentType ) ) {
224+ // cycle...
225+ if ( appProtoView . isEmbeddedFragment ) {
226+ throw new BaseException (
227+ `<ng-content> is used within the recursive path of ${ stringify ( nestedComponentType ) } ` ) ;
228+ } else if ( appProtoView . type === renderApi . ViewType . COMPONENT ) {
229+ throw new BaseException (
230+ `Unconditional component cycle in ${ stringify ( nestedComponentType ) } ` ) ;
231+ } else {
232+ elementBinderDone ( componentPath . get ( nestedComponentType ) ) ;
233+ }
234+ } else {
235+ var nestedCall = this . _compile ( nestedComponent , componentPath ) ;
236+ if ( isPromise ( nestedCall ) ) {
237+ nestedPVPromises . push ( ( < Promise < AppProtoView > > nestedCall ) . then ( elementBinderDone ) ) ;
238+ } else {
239+ elementBinderDone ( < AppProtoView > nestedCall ) ;
240+ }
241+ }
242+ } ) ;
250243 } ) ;
251244 return PromiseWrapper . all ( nestedPVPromises )
252- . then ( ( _ ) => {
253- this . _collectMergableProtoViews ( appProtoView , componentType ) ;
254- return appProtoView ;
245+ . then ( _ => PromiseWrapper . all (
246+ appProtoViews . map ( appProtoView => this . _mergeProtoView ( appProtoView ) ) ) )
247+ . then ( _ => appProtoViews [ 0 ] ) ;
248+ }
249+
250+ private _mergeProtoView ( appProtoView : AppProtoView ) : Promise < any > {
251+ if ( appProtoView . type !== renderApi . ViewType . HOST &&
252+ appProtoView . type !== renderApi . ViewType . EMBEDDED ) {
253+ return null ;
254+ }
255+ return this . _render . mergeProtoViewsRecursively ( this . _collectMergeRenderProtoViews ( appProtoView ) )
256+ . then ( ( mergeResult : renderApi . RenderProtoViewMergeMapping ) => {
257+ appProtoView . mergeMapping = new AppProtoViewMergeMapping ( mergeResult ) ;
255258 } ) ;
256259 }
257260
258- private _collectMergableProtoViews ( appProtoView : AppProtoView , componentType : Type ) {
259- var isRecursive = false ;
261+ private _collectMergeRenderProtoViews (
262+ appProtoView : AppProtoView ) : List < renderApi . RenderProtoViewRef | List < any > > {
263+ var result = [ appProtoView . render ] ;
260264 for ( var i = 0 ; i < appProtoView . elementBinders . length ; i ++ ) {
261265 var binder = appProtoView . elementBinders [ i ] ;
262- if ( binder . hasStaticComponent ( ) ) {
263- if ( isBlank ( binder . nestedProtoView . isRecursive ) ) {
264- // cycle via a component. We are in the tail recursion,
265- // so all components should have their isRecursive flag set already.
266- isRecursive = true ;
267- break ;
266+ if ( isPresent ( binder . nestedProtoView ) ) {
267+ if ( binder . hasStaticComponent ( ) ||
268+ ( binder . hasEmbeddedProtoView ( ) && binder . nestedProtoView . isEmbeddedFragment ) ) {
269+ result . push ( this . _collectMergeRenderProtoViews ( binder . nestedProtoView ) ) ;
270+ } else {
271+ result . push ( null ) ;
268272 }
269- } else if ( binder . hasEmbeddedProtoView ( ) ) {
270- this . _collectMergableProtoViews ( binder . nestedProtoView , componentType ) ;
271- }
272- }
273- if ( isRecursive ) {
274- if ( appProtoView . isEmbeddedFragment ) {
275- throw new BaseException (
276- `<ng-content> is used within the recursive path of ${ stringify ( componentType ) } ` ) ;
277- }
278- if ( appProtoView . type === renderApi . ViewType . COMPONENT ) {
279- throw new BaseException ( `Unconditional component cycle in ${ stringify ( componentType ) } ` ) ;
280273 }
281274 }
282- if ( appProtoView . type === renderApi . ViewType . EMBEDDED ||
283- appProtoView . type === renderApi . ViewType . HOST ) {
284- this . _protoViewsToBeMerged . push ( appProtoView ) ;
285- }
286- appProtoView . isRecursive = isRecursive ;
275+ return result ;
287276 }
288277
289- private _loopComponentElementBinders ( appProtoView : AppProtoView , callback : Function ) {
278+ private _collectComponentElementBinders ( appProtoView : AppProtoView ) : ElementBinder [ ] {
279+ var componentElementBinders = [ ] ;
290280 appProtoView . elementBinders . forEach ( ( elementBinder ) => {
291281 if ( isPresent ( elementBinder . componentDirective ) ) {
292- callback ( appProtoView , elementBinder ) ;
293- } else if ( isPresent ( elementBinder . nestedProtoView ) ) {
294- this . _loopComponentElementBinders ( elementBinder . nestedProtoView , callback ) ;
282+ componentElementBinders . push ( elementBinder ) ;
295283 }
296284 } ) ;
285+ return componentElementBinders ;
297286 }
298287
299288 private _buildRenderTemplate ( component , view , directives ) : renderApi . ViewDefinition {
0 commit comments