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

View File

@ -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",

View File

@ -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),
];

View File

@ -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",

View File

@ -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 !),

View File

@ -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),
];

View File

@ -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>(