feat(ivy): implement listLazyRoutes() for ngtsc (#27697)
This commit uses the NgModuleRouteAnalyzer introduced previously to implement listLazyRoutes() for NgtscProgram. Currently this implementation is limited to listing routes globally and cannot list routes for a given lazy module. Testing seems to indicate that the CLI uses the global form, but this should be verified. Jira issue: FW-629 PR Close #27697
This commit is contained in:
		
							parent
							
								
									41b2499f17
								
							
						
					
					
						commit
						1964be0b17
					
				| @ -29,6 +29,7 @@ ts_library( | |||||||
|         "//packages/compiler-cli/src/ngtsc/imports", |         "//packages/compiler-cli/src/ngtsc/imports", | ||||||
|         "//packages/compiler-cli/src/ngtsc/partial_evaluator", |         "//packages/compiler-cli/src/ngtsc/partial_evaluator", | ||||||
|         "//packages/compiler-cli/src/ngtsc/reflection", |         "//packages/compiler-cli/src/ngtsc/reflection", | ||||||
|  |         "//packages/compiler-cli/src/ngtsc/routing", | ||||||
|         "//packages/compiler-cli/src/ngtsc/shims", |         "//packages/compiler-cli/src/ngtsc/shims", | ||||||
|         "//packages/compiler-cli/src/ngtsc/switch", |         "//packages/compiler-cli/src/ngtsc/switch", | ||||||
|         "//packages/compiler-cli/src/ngtsc/transform", |         "//packages/compiler-cli/src/ngtsc/transform", | ||||||
|  | |||||||
| @ -76,7 +76,7 @@ export class DecorationAnalyzer { | |||||||
|     new InjectableDecoratorHandler(this.reflectionHost, this.isCore), |     new InjectableDecoratorHandler(this.reflectionHost, this.isCore), | ||||||
|     new NgModuleDecoratorHandler( |     new NgModuleDecoratorHandler( | ||||||
|         this.reflectionHost, this.evaluator, this.scopeRegistry, this.referencesRegistry, |         this.reflectionHost, this.evaluator, this.scopeRegistry, this.referencesRegistry, | ||||||
|         this.isCore), |         this.isCore, /* routeAnalyzer */ null), | ||||||
|     new PipeDecoratorHandler(this.reflectionHost, this.evaluator, this.scopeRegistry, this.isCore), |     new PipeDecoratorHandler(this.reflectionHost, this.evaluator, this.scopeRegistry, this.isCore), | ||||||
|   ]; |   ]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ ts_library( | |||||||
|         "//packages/compiler-cli/src/ngtsc/imports", |         "//packages/compiler-cli/src/ngtsc/imports", | ||||||
|         "//packages/compiler-cli/src/ngtsc/partial_evaluator", |         "//packages/compiler-cli/src/ngtsc/partial_evaluator", | ||||||
|         "//packages/compiler-cli/src/ngtsc/reflection", |         "//packages/compiler-cli/src/ngtsc/reflection", | ||||||
|  |         "//packages/compiler-cli/src/ngtsc/routing", | ||||||
|         "//packages/compiler-cli/src/ngtsc/transform", |         "//packages/compiler-cli/src/ngtsc/transform", | ||||||
|         "//packages/compiler-cli/src/ngtsc/typecheck", |         "//packages/compiler-cli/src/ngtsc/typecheck", | ||||||
|         "@ngdeps//@types/node", |         "@ngdeps//@types/node", | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; | |||||||
| import {Reference, ResolvedReference} from '../../imports'; | import {Reference, ResolvedReference} from '../../imports'; | ||||||
| import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; | import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; | ||||||
| import {Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection'; | import {Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection'; | ||||||
|  | import {NgModuleRouteAnalyzer} from '../../routing'; | ||||||
| import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform'; | import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform'; | ||||||
| 
 | 
 | ||||||
| import {generateSetClassMetadataCall} from './metadata'; | import {generateSetClassMetadataCall} from './metadata'; | ||||||
| @ -35,7 +36,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys | |||||||
|   constructor( |   constructor( | ||||||
|       private reflector: ReflectionHost, private evaluator: PartialEvaluator, |       private reflector: ReflectionHost, private evaluator: PartialEvaluator, | ||||||
|       private scopeRegistry: SelectorScopeRegistry, private referencesRegistry: ReferencesRegistry, |       private scopeRegistry: SelectorScopeRegistry, private referencesRegistry: ReferencesRegistry, | ||||||
|       private isCore: boolean) {} |       private isCore: boolean, private routeAnalyzer: NgModuleRouteAnalyzer|null) {} | ||||||
| 
 | 
 | ||||||
|   detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined { |   detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined { | ||||||
|     if (!decorators) { |     if (!decorators) { | ||||||
| @ -77,18 +78,20 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys | |||||||
|       declarations = this.resolveTypeList(expr, declarationMeta, 'declarations'); |       declarations = this.resolveTypeList(expr, declarationMeta, 'declarations'); | ||||||
|     } |     } | ||||||
|     let imports: Reference<ts.Declaration>[] = []; |     let imports: Reference<ts.Declaration>[] = []; | ||||||
|  |     let rawImports: ts.Expression|null = null; | ||||||
|     if (ngModule.has('imports')) { |     if (ngModule.has('imports')) { | ||||||
|       const expr = ngModule.get('imports') !; |       rawImports = ngModule.get('imports') !; | ||||||
|       const importsMeta = this.evaluator.evaluate( |       const importsMeta = this.evaluator.evaluate( | ||||||
|           expr, ref => this._extractModuleFromModuleWithProvidersFn(ref.node)); |           rawImports, ref => this._extractModuleFromModuleWithProvidersFn(ref.node)); | ||||||
|       imports = this.resolveTypeList(expr, importsMeta, 'imports'); |       imports = this.resolveTypeList(rawImports, importsMeta, 'imports'); | ||||||
|     } |     } | ||||||
|     let exports: Reference<ts.Declaration>[] = []; |     let exports: Reference<ts.Declaration>[] = []; | ||||||
|  |     let rawExports: ts.Expression|null = null; | ||||||
|     if (ngModule.has('exports')) { |     if (ngModule.has('exports')) { | ||||||
|       const expr = ngModule.get('exports') !; |       rawExports = ngModule.get('exports') !; | ||||||
|       const exportsMeta = this.evaluator.evaluate( |       const exportsMeta = this.evaluator.evaluate( | ||||||
|           expr, ref => this._extractModuleFromModuleWithProvidersFn(ref.node)); |           rawExports, ref => this._extractModuleFromModuleWithProvidersFn(ref.node)); | ||||||
|       exports = this.resolveTypeList(expr, exportsMeta, 'exports'); |       exports = this.resolveTypeList(rawExports, exportsMeta, 'exports'); | ||||||
|       this.referencesRegistry.add(node, ...exports); |       this.referencesRegistry.add(node, ...exports); | ||||||
|     } |     } | ||||||
|     let bootstrap: Reference<ts.Declaration>[] = []; |     let bootstrap: Reference<ts.Declaration>[] = []; | ||||||
| @ -123,6 +126,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys | |||||||
|     const providers: Expression = ngModule.has('providers') ? |     const providers: Expression = ngModule.has('providers') ? | ||||||
|         new WrappedNodeExpr(ngModule.get('providers') !) : |         new WrappedNodeExpr(ngModule.get('providers') !) : | ||||||
|         new LiteralArrayExpr([]); |         new LiteralArrayExpr([]); | ||||||
|  |     const rawProviders = ngModule.has('providers') ? ngModule.get('providers') ! : null; | ||||||
| 
 | 
 | ||||||
|     const injectorImports: WrappedNodeExpr<ts.Expression>[] = []; |     const injectorImports: WrappedNodeExpr<ts.Expression>[] = []; | ||||||
|     if (ngModule.has('imports')) { |     if (ngModule.has('imports')) { | ||||||
| @ -132,6 +136,11 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys | |||||||
|       injectorImports.push(new WrappedNodeExpr(ngModule.get('exports') !)); |       injectorImports.push(new WrappedNodeExpr(ngModule.get('exports') !)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (this.routeAnalyzer !== null) { | ||||||
|  |       this.routeAnalyzer.add( | ||||||
|  |           node.getSourceFile(), node.name !.text, rawImports, rawExports, rawProviders); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const ngInjectorDef: R3InjectorMetadata = { |     const ngInjectorDef: R3InjectorMetadata = { | ||||||
|       name: node.name !.text, |       name: node.name !.text, | ||||||
|       type: new WrappedNodeExpr(node.name !), |       type: new WrappedNodeExpr(node.name !), | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ import {ImportRewriter, ModuleResolver, NoopImportRewriter, R3SymbolsImportRewri | |||||||
| import {PartialEvaluator} from './partial_evaluator'; | import {PartialEvaluator} from './partial_evaluator'; | ||||||
| import {TypeScriptReflectionHost} from './reflection'; | import {TypeScriptReflectionHost} from './reflection'; | ||||||
| import {HostResourceLoader} from './resource_loader'; | import {HostResourceLoader} from './resource_loader'; | ||||||
|  | import {NgModuleRouteAnalyzer} from './routing'; | ||||||
| import {FactoryGenerator, FactoryInfo, GeneratedShimsHostWrapper, ShimGenerator, SummaryGenerator, generatedFactoryTransform} from './shims'; | import {FactoryGenerator, FactoryInfo, GeneratedShimsHostWrapper, ShimGenerator, SummaryGenerator, generatedFactoryTransform} from './shims'; | ||||||
| import {ivySwitchTransform} from './switch'; | import {ivySwitchTransform} from './switch'; | ||||||
| import {IvyCompilation, ivyTransformFactory} from './transform'; | import {IvyCompilation, ivyTransformFactory} from './transform'; | ||||||
| @ -43,6 +44,7 @@ export class NgtscProgram implements api.Program { | |||||||
|   private entryPoint: ts.SourceFile|null; |   private entryPoint: ts.SourceFile|null; | ||||||
|   private exportReferenceGraph: ReferenceGraph|null = null; |   private exportReferenceGraph: ReferenceGraph|null = null; | ||||||
|   private flatIndexGenerator: FlatIndexGenerator|null = null; |   private flatIndexGenerator: FlatIndexGenerator|null = null; | ||||||
|  |   private routeAnalyzer: NgModuleRouteAnalyzer|null = null; | ||||||
| 
 | 
 | ||||||
|   private constructionDiagnostics: ts.Diagnostic[] = []; |   private constructionDiagnostics: ts.Diagnostic[] = []; | ||||||
|   private moduleResolver: ModuleResolver; |   private moduleResolver: ModuleResolver; | ||||||
| @ -184,7 +186,14 @@ export class NgtscProgram implements api.Program { | |||||||
|                           .filter((result): result is Promise<void> => result !== undefined)); |                           .filter((result): result is Promise<void> => result !== undefined)); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] { return []; } |   listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] { | ||||||
|  |     if (entryRoute !== undefined) { | ||||||
|  |       throw new Error( | ||||||
|  |           `Listing specific routes is unsupported for now (got query for ${entryRoute})`); | ||||||
|  |     } | ||||||
|  |     this.ensureAnalyzed(); | ||||||
|  |     return this.routeAnalyzer !.listLazyRoutes(); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   getLibrarySummaries(): Map<string, api.LibrarySummary> { |   getLibrarySummaries(): Map<string, api.LibrarySummary> { | ||||||
|     throw new Error('Method not implemented.'); |     throw new Error('Method not implemented.'); | ||||||
| @ -290,6 +299,8 @@ export class NgtscProgram implements api.Program { | |||||||
|       referencesRegistry = new NoopReferencesRegistry(); |       referencesRegistry = new NoopReferencesRegistry(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     this.routeAnalyzer = new NgModuleRouteAnalyzer(this.moduleResolver, evaluator); | ||||||
|  | 
 | ||||||
|     // Set up the IvyCompilation, which manages state for the Ivy transformer.
 |     // Set up the IvyCompilation, which manages state for the Ivy transformer.
 | ||||||
|     const handlers = [ |     const handlers = [ | ||||||
|       new BaseDefDecoratorHandler(this.reflector, evaluator), |       new BaseDefDecoratorHandler(this.reflector, evaluator), | ||||||
| @ -300,7 +311,8 @@ export class NgtscProgram implements api.Program { | |||||||
|       new DirectiveDecoratorHandler(this.reflector, evaluator, scopeRegistry, this.isCore), |       new DirectiveDecoratorHandler(this.reflector, evaluator, scopeRegistry, this.isCore), | ||||||
|       new InjectableDecoratorHandler(this.reflector, this.isCore), |       new InjectableDecoratorHandler(this.reflector, this.isCore), | ||||||
|       new NgModuleDecoratorHandler( |       new NgModuleDecoratorHandler( | ||||||
|           this.reflector, evaluator, scopeRegistry, referencesRegistry, this.isCore), |           this.reflector, evaluator, scopeRegistry, referencesRegistry, this.isCore, | ||||||
|  |           this.routeAnalyzer), | ||||||
|       new PipeDecoratorHandler(this.reflector, evaluator, scopeRegistry, this.isCore), |       new PipeDecoratorHandler(this.reflector, evaluator, scopeRegistry, this.isCore), | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1949,6 +1949,45 @@ describe('ngtsc behavioral tests', () => { | |||||||
|       expect(trim(jsContents)).toContain(trim(hostBindingsFn)); |       expect(trim(jsContents)).toContain(trim(hostBindingsFn)); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   it('should detect all lazy routes', () => { | ||||||
|  |     env.tsconfig(); | ||||||
|  |     env.write('test.ts', ` | ||||||
|  |     import {NgModule} from '@angular/core'; | ||||||
|  |     import {RouterModule} from '@angular/router'; | ||||||
|  | 
 | ||||||
|  |     @NgModule({ | ||||||
|  |       imports: [ | ||||||
|  |         RouterModule.forChild([ | ||||||
|  |           {path: '', loadChildren: './lazy#LazyModule'}, | ||||||
|  |         ]), | ||||||
|  |       ], | ||||||
|  |     }) | ||||||
|  |     export class TestModule {} | ||||||
|  |     `);
 | ||||||
|  |     env.write('lazy.ts', ` | ||||||
|  |     import {NgModule} from '@angular/core'; | ||||||
|  |     import {RouterModule} from '@angular/router'; | ||||||
|  | 
 | ||||||
|  |     @NgModule({}) | ||||||
|  |     export class LazyModule {} | ||||||
|  |     `);
 | ||||||
|  |     env.write('node_modules/@angular/router/index.d.ts', ` | ||||||
|  |     import {ModuleWithProviders} from '@angular/core'; | ||||||
|  | 
 | ||||||
|  |     export declare var ROUTES; | ||||||
|  |     export declare class RouterModule { | ||||||
|  |       static forRoot(arg1: any, arg2: any): ModuleWithProviders<RouterModule>; | ||||||
|  |       static forChild(arg1: any): ModuleWithProviders<RouterModule>; | ||||||
|  |     } | ||||||
|  |     `);
 | ||||||
|  | 
 | ||||||
|  |     const routes = env.driveRoutes(); | ||||||
|  |     expect(routes.length).toBe(1); | ||||||
|  |     expect(routes[0].route).toEqual('./lazy#LazyModule'); | ||||||
|  |     expect(routes[0].module.filePath.endsWith('/test.ts')).toBe(true); | ||||||
|  |     expect(routes[0].referencedModule.filePath.endsWith('/lazy.ts')).toBe(true); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| function expectTokenAtPosition<T extends ts.Node>( | function expectTokenAtPosition<T extends ts.Node>( | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user