fix(core): allow tree shaking of component factories and styles (#15214)
Closure compiler is very sensitive to top level function calls. This commit makes the function calls `createComponentFactory` and `createRendererTypeV2` logic-less. Fixes #15181 PR Close #15214
This commit is contained in:
		
							parent
							
								
									9429032da1
								
							
						
					
					
						commit
						2a0e55ffb5
					
				| @ -136,30 +136,23 @@ export class CompileMetadataResolver { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private getComponentFactory(selector: string, dirType: any): StaticSymbol|ComponentFactory<any> { |   private getComponentFactory( | ||||||
|  |       selector: string, dirType: any, inputs: {[key: string]: string}, | ||||||
|  |       outputs: {[key: string]: string}): StaticSymbol|ComponentFactory<any> { | ||||||
|     if (dirType instanceof StaticSymbol) { |     if (dirType instanceof StaticSymbol) { | ||||||
|       return this._staticSymbolCache.get( |       return this._staticSymbolCache.get( | ||||||
|           ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType)); |           ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType)); | ||||||
|     } else { |     } else { | ||||||
|       const hostView = this.getHostComponentViewClass(dirType); |       const hostView = this.getHostComponentViewClass(dirType); | ||||||
|       // Note: inputs / outputs / ngContentSelectors will be filled later once the template is
 |       // Note: ngContentSelectors will be filled later once the template is
 | ||||||
|       // loaded.
 |       // loaded.
 | ||||||
|       return createComponentFactory(selector, dirType, <any>hostView, {}, {}, []); |       return createComponentFactory(selector, dirType, <any>hostView, inputs, outputs, []); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private initComponentFactory( |   private initComponentFactory( | ||||||
|       factory: StaticSymbol|ComponentFactory<any>, inputs: {[key: string]: string}, |       factory: StaticSymbol|ComponentFactory<any>, ngContentSelectors: string[]) { | ||||||
|       outputs: {[key: string]: string}, ngContentSelectors: string[]) { |  | ||||||
|     if (!(factory instanceof StaticSymbol)) { |     if (!(factory instanceof StaticSymbol)) { | ||||||
|       for (let propName in inputs) { |  | ||||||
|         const templateName = inputs[propName]; |  | ||||||
|         factory.inputs.push({propName, templateName}); |  | ||||||
|       } |  | ||||||
|       for (let propName in outputs) { |  | ||||||
|         const templateName = outputs[propName]; |  | ||||||
|         factory.outputs.push({propName, templateName}); |  | ||||||
|       } |  | ||||||
|       factory.ngContentSelectors.push(...ngContentSelectors); |       factory.ngContentSelectors.push(...ngContentSelectors); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -205,9 +198,7 @@ export class CompileMetadataResolver { | |||||||
|         template: templateMetadata |         template: templateMetadata | ||||||
|       }); |       }); | ||||||
|       if (templateMetadata) { |       if (templateMetadata) { | ||||||
|         this.initComponentFactory( |         this.initComponentFactory(metadata.componentFactory, templateMetadata.ngContentSelectors); | ||||||
|             metadata.componentFactory, metadata.inputs, metadata.outputs, |  | ||||||
|             templateMetadata.ngContentSelectors); |  | ||||||
|       } |       } | ||||||
|       this._directiveCache.set(directiveType, normalizedDirMeta); |       this._directiveCache.set(directiveType, normalizedDirMeta); | ||||||
|       this._summaryCache.set(directiveType, normalizedDirMeta.toSummary()); |       this._summaryCache.set(directiveType, normalizedDirMeta.toSummary()); | ||||||
| @ -343,10 +334,12 @@ export class CompileMetadataResolver { | |||||||
|       componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) : |       componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) : | ||||||
|                                                          undefined, |                                                          undefined, | ||||||
|       rendererType: nonNormalizedTemplateMetadata ? this.getRendererType(directiveType) : undefined, |       rendererType: nonNormalizedTemplateMetadata ? this.getRendererType(directiveType) : undefined, | ||||||
|       componentFactory: nonNormalizedTemplateMetadata ? |       componentFactory: undefined | ||||||
|           this.getComponentFactory(selector, directiveType) : |  | ||||||
|           undefined |  | ||||||
|     }); |     }); | ||||||
|  |     if (nonNormalizedTemplateMetadata) { | ||||||
|  |       metadata.componentFactory = | ||||||
|  |           this.getComponentFactory(selector, directiveType, metadata.inputs, metadata.outputs); | ||||||
|  |     } | ||||||
|     cacheEntry = {metadata, annotation: dirMeta}; |     cacheEntry = {metadata, annotation: dirMeta}; | ||||||
|     this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry); |     this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry); | ||||||
|     return cacheEntry; |     return cacheEntry; | ||||||
|  | |||||||
| @ -6,11 +6,12 @@ | |||||||
|  * found in the LICENSE file at https://angular.io/license
 |  * found in the LICENSE file at https://angular.io/license
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | import {ViewEncapsulation} from '../metadata/view'; | ||||||
| import {Renderer2, RendererType2} from '../render/api'; | import {Renderer2, RendererType2} from '../render/api'; | ||||||
| import {SecurityContext} from '../security'; | import {SecurityContext} from '../security'; | ||||||
| 
 | 
 | ||||||
| import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementHandleEventFn, NodeData, NodeDef, NodeFlags, OutputDef, OutputType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData, asProviderData} from './types'; | import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementHandleEventFn, NodeData, NodeDef, NodeFlags, OutputDef, OutputType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData, asProviderData} from './types'; | ||||||
| import {NOOP, checkAndUpdateBinding, dispatchEvent, elementEventFullName, filterQueryId, getParentRenderElement, resolveViewDefinition, splitMatchedQueriesDsl, splitNamespace} from './util'; | import {NOOP, checkAndUpdateBinding, dispatchEvent, elementEventFullName, filterQueryId, getParentRenderElement, resolveRendererType2, resolveViewDefinition, splitMatchedQueriesDsl, splitNamespace} from './util'; | ||||||
| 
 | 
 | ||||||
| export function anchorDef( | export function anchorDef( | ||||||
|     flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][], |     flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][], | ||||||
| @ -112,11 +113,7 @@ export function elementDef( | |||||||
|     const [ns, name] = splitNamespace(namespaceAndName); |     const [ns, name] = splitNamespace(namespaceAndName); | ||||||
|     return [ns, name, value]; |     return [ns, name, value]; | ||||||
|   }); |   }); | ||||||
|   // This is needed as the jit compiler always uses an empty hash as default RendererType2,
 |   componentRendererType = resolveRendererType2(componentRendererType); | ||||||
|   // which is not filled for host views.
 |  | ||||||
|   if (componentRendererType && componentRendererType.encapsulation == null) { |  | ||||||
|     componentRendererType = null; |  | ||||||
|   } |  | ||||||
|   if (componentView) { |   if (componentView) { | ||||||
|     flags |= NodeFlags.ComponentView; |     flags |= NodeFlags.ComponentView; | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -25,22 +25,14 @@ import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachVi | |||||||
| 
 | 
 | ||||||
| const EMPTY_CONTEXT = new Object(); | const EMPTY_CONTEXT = new Object(); | ||||||
| 
 | 
 | ||||||
|  | // Attention: this function is called as top level function.
 | ||||||
|  | // Putting any logic in here will destroy closure tree shaking!
 | ||||||
| export function createComponentFactory( | export function createComponentFactory( | ||||||
|     selector: string, componentType: Type<any>, viewDefFactory: ViewDefinitionFactory, |     selector: string, componentType: Type<any>, viewDefFactory: ViewDefinitionFactory, | ||||||
|     inputs: {[propName: string]: string}, outputs: {[propName: string]: string}, |     inputs: {[propName: string]: string}, outputs: {[propName: string]: string}, | ||||||
|     ngContentSelectors: string[]): ComponentFactory<any> { |     ngContentSelectors: string[]): ComponentFactory<any> { | ||||||
|   const inputsArr: {propName: string, templateName: string}[] = []; |  | ||||||
|   for (let propName in inputs) { |  | ||||||
|     const templateName = inputs[propName]; |  | ||||||
|     inputsArr.push({propName, templateName}); |  | ||||||
|   } |  | ||||||
|   const outputsArr: {propName: string, templateName: string}[] = []; |  | ||||||
|   for (let propName in outputs) { |  | ||||||
|     const templateName = outputs[propName]; |  | ||||||
|     outputsArr.push({propName, templateName}); |  | ||||||
|   } |  | ||||||
|   return new ComponentFactory_( |   return new ComponentFactory_( | ||||||
|       selector, componentType, viewDefFactory, inputsArr, outputsArr, ngContentSelectors); |       selector, componentType, viewDefFactory, inputs, outputs, ngContentSelectors); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function getComponentViewDefinitionFactory(componentFactory: ComponentFactory<any>): | export function getComponentViewDefinitionFactory(componentFactory: ComponentFactory<any>): | ||||||
| @ -56,14 +48,32 @@ class ComponentFactory_ extends ComponentFactory<any> { | |||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|       public selector: string, public componentType: Type<any>, |       public selector: string, public componentType: Type<any>, | ||||||
|       viewDefFactory: ViewDefinitionFactory, |       viewDefFactory: ViewDefinitionFactory, private _inputs: {[propName: string]: string}, | ||||||
|       public inputs: {propName: string, templateName: string}[], |       private _outputs: {[propName: string]: string}, public ngContentSelectors: string[]) { | ||||||
|       public outputs: {propName: string, templateName: string}[], |     // Attention: this ctor is called as top level function.
 | ||||||
|       public ngContentSelectors: string[]) { |     // Putting any logic in here will destroy closure tree shaking!
 | ||||||
|     super(); |     super(); | ||||||
|     this.viewDefFactory = viewDefFactory; |     this.viewDefFactory = viewDefFactory; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   get inputs() { | ||||||
|  |     const inputsArr: {propName: string, templateName: string}[] = []; | ||||||
|  |     for (let propName in this._inputs) { | ||||||
|  |       const templateName = this._inputs[propName]; | ||||||
|  |       inputsArr.push({propName, templateName}); | ||||||
|  |     } | ||||||
|  |     return inputsArr; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get outputs() { | ||||||
|  |     const outputsArr: {propName: string, templateName: string}[] = []; | ||||||
|  |     for (let propName in this._outputs) { | ||||||
|  |       const templateName = this._outputs[propName]; | ||||||
|  |       outputsArr.push({propName, templateName}); | ||||||
|  |     } | ||||||
|  |     return outputsArr; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * Creates a new component. |    * Creates a new component. | ||||||
|    */ |    */ | ||||||
|  | |||||||
| @ -42,21 +42,42 @@ export function unwrapValue(value: any): any { | |||||||
|   return value; |   return value; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| let _renderCompCount = 0; | const UNDEFINED_RENDERER_TYPE_ID = '$$undefined'; | ||||||
|  | const EMPTY_RENDERER_TYPE_ID = '$$empty'; | ||||||
| 
 | 
 | ||||||
|  | // Attention: this function is called as top level function.
 | ||||||
|  | // Putting any logic in here will destroy closure tree shaking!
 | ||||||
| export function createRendererType2(values: { | export function createRendererType2(values: { | ||||||
|   styles: (string | any[])[], |   styles: (string | any[])[], | ||||||
|   encapsulation: ViewEncapsulation, |   encapsulation: ViewEncapsulation, | ||||||
|   data: {[kind: string]: any[]} |   data: {[kind: string]: any[]} | ||||||
| }): RendererType2 { | }): RendererType2 { | ||||||
|   const isFilled = values && (values.encapsulation !== ViewEncapsulation.None || |   return { | ||||||
|                               values.styles.length || Object.keys(values.data).length); |     id: UNDEFINED_RENDERER_TYPE_ID, | ||||||
|   if (isFilled) { |     styles: values.styles, | ||||||
|     const id = `c${_renderCompCount++}`; |     encapsulation: values.encapsulation, | ||||||
|     return {id: id, styles: values.styles, encapsulation: values.encapsulation, data: values.data}; |     data: values.data | ||||||
|   } else { |   }; | ||||||
|     return null; | } | ||||||
|  | 
 | ||||||
|  | let _renderCompCount = 0; | ||||||
|  | 
 | ||||||
|  | export function resolveRendererType2(type: RendererType2): RendererType2 { | ||||||
|  |   if (type && type.id === UNDEFINED_RENDERER_TYPE_ID) { | ||||||
|  |     // first time we see this RendererType2. Initialize it...
 | ||||||
|  |     const isFilled = | ||||||
|  |         ((type.encapsulation != null && type.encapsulation !== ViewEncapsulation.None) || | ||||||
|  |          type.styles.length || Object.keys(type.data).length); | ||||||
|  |     if (isFilled) { | ||||||
|  |       type.id = `c${_renderCompCount++}`; | ||||||
|  |     } else { | ||||||
|  |       type.id = EMPTY_RENDERER_TYPE_ID; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |   if (type && type.id === EMPTY_RENDERER_TYPE_ID) { | ||||||
|  |     type = null; | ||||||
|  |   } | ||||||
|  |   return type; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function checkBinding( | export function checkBinding( | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user