feat(ivy): implement listLazyRoutes() for ngtsc ()

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 
This commit is contained in:
Alex Rickabaugh 2018-11-16 17:56:18 +01:00
parent 41b2499f17
commit 1964be0b17
6 changed files with 72 additions and 10 deletions
packages/compiler-cli

@ -29,6 +29,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/imports",
"//packages/compiler-cli/src/ngtsc/partial_evaluator",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/routing",
"//packages/compiler-cli/src/ngtsc/shims",
"//packages/compiler-cli/src/ngtsc/switch",
"//packages/compiler-cli/src/ngtsc/transform",

@ -76,7 +76,7 @@ export class DecorationAnalyzer {
new InjectableDecoratorHandler(this.reflectionHost, this.isCore),
new NgModuleDecoratorHandler(
this.reflectionHost, this.evaluator, this.scopeRegistry, this.referencesRegistry,
this.isCore),
this.isCore, /* routeAnalyzer */ null),
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/partial_evaluator",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/routing",
"//packages/compiler-cli/src/ngtsc/transform",
"//packages/compiler-cli/src/ngtsc/typecheck",
"@ngdeps//@types/node",

@ -13,6 +13,7 @@ import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {Reference, ResolvedReference} from '../../imports';
import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator';
import {Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection';
import {NgModuleRouteAnalyzer} from '../../routing';
import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform';
import {generateSetClassMetadataCall} from './metadata';
@ -35,7 +36,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
constructor(
private reflector: ReflectionHost, private evaluator: PartialEvaluator,
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 {
if (!decorators) {
@ -77,18 +78,20 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
declarations = this.resolveTypeList(expr, declarationMeta, 'declarations');
}
let imports: Reference<ts.Declaration>[] = [];
let rawImports: ts.Expression|null = null;
if (ngModule.has('imports')) {
const expr = ngModule.get('imports') !;
rawImports = ngModule.get('imports') !;
const importsMeta = this.evaluator.evaluate(
expr, ref => this._extractModuleFromModuleWithProvidersFn(ref.node));
imports = this.resolveTypeList(expr, importsMeta, 'imports');
rawImports, ref => this._extractModuleFromModuleWithProvidersFn(ref.node));
imports = this.resolveTypeList(rawImports, importsMeta, 'imports');
}
let exports: Reference<ts.Declaration>[] = [];
let rawExports: ts.Expression|null = null;
if (ngModule.has('exports')) {
const expr = ngModule.get('exports') !;
rawExports = ngModule.get('exports') !;
const exportsMeta = this.evaluator.evaluate(
expr, ref => this._extractModuleFromModuleWithProvidersFn(ref.node));
exports = this.resolveTypeList(expr, exportsMeta, 'exports');
rawExports, ref => this._extractModuleFromModuleWithProvidersFn(ref.node));
exports = this.resolveTypeList(rawExports, exportsMeta, 'exports');
this.referencesRegistry.add(node, ...exports);
}
let bootstrap: Reference<ts.Declaration>[] = [];
@ -123,6 +126,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
const providers: Expression = ngModule.has('providers') ?
new WrappedNodeExpr(ngModule.get('providers') !) :
new LiteralArrayExpr([]);
const rawProviders = ngModule.has('providers') ? ngModule.get('providers') ! : null;
const injectorImports: WrappedNodeExpr<ts.Expression>[] = [];
if (ngModule.has('imports')) {
@ -132,6 +136,11 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
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 = {
name: node.name !.text,
type: new WrappedNodeExpr(node.name !),

@ -20,6 +20,7 @@ import {ImportRewriter, ModuleResolver, NoopImportRewriter, R3SymbolsImportRewri
import {PartialEvaluator} from './partial_evaluator';
import {TypeScriptReflectionHost} from './reflection';
import {HostResourceLoader} from './resource_loader';
import {NgModuleRouteAnalyzer} from './routing';
import {FactoryGenerator, FactoryInfo, GeneratedShimsHostWrapper, ShimGenerator, SummaryGenerator, generatedFactoryTransform} from './shims';
import {ivySwitchTransform} from './switch';
import {IvyCompilation, ivyTransformFactory} from './transform';
@ -43,6 +44,7 @@ export class NgtscProgram implements api.Program {
private entryPoint: ts.SourceFile|null;
private exportReferenceGraph: ReferenceGraph|null = null;
private flatIndexGenerator: FlatIndexGenerator|null = null;
private routeAnalyzer: NgModuleRouteAnalyzer|null = null;
private constructionDiagnostics: ts.Diagnostic[] = [];
private moduleResolver: ModuleResolver;
@ -184,7 +186,14 @@ export class NgtscProgram implements api.Program {
.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> {
throw new Error('Method not implemented.');
@ -290,6 +299,8 @@ export class NgtscProgram implements api.Program {
referencesRegistry = new NoopReferencesRegistry();
}
this.routeAnalyzer = new NgModuleRouteAnalyzer(this.moduleResolver, evaluator);
// Set up the IvyCompilation, which manages state for the Ivy transformer.
const handlers = [
new BaseDefDecoratorHandler(this.reflector, evaluator),
@ -300,7 +311,8 @@ export class NgtscProgram implements api.Program {
new DirectiveDecoratorHandler(this.reflector, evaluator, scopeRegistry, this.isCore),
new InjectableDecoratorHandler(this.reflector, this.isCore),
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),
];

@ -1949,6 +1949,45 @@ describe('ngtsc behavioral tests', () => {
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>(