feat(ivy): implement listing lazy routes for specific entry point in ngtsc
(#28542)
Related: angular/angular-cli#13532 Jira issue: FW-860 PR Close #28542
This commit is contained in:

committed by
Miško Hevery

parent
f358188ec1
commit
e6c51b3e06
@ -192,9 +192,7 @@ export class NgtscProgram implements api.Program {
|
||||
|
||||
listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] {
|
||||
this.ensureAnalyzed();
|
||||
// Listing specific routes is unsupported for now, so we erroneously return
|
||||
// all lazy routes instead (which should be okay for the CLI's usage).
|
||||
return this.routeAnalyzer !.listLazyRoutes();
|
||||
return this.routeAnalyzer !.listLazyRoutes(entryRoute);
|
||||
}
|
||||
|
||||
getLibrarySummaries(): Map<string, api.LibrarySummary> {
|
||||
|
@ -11,7 +11,7 @@ import * as ts from 'typescript';
|
||||
import {ModuleResolver} from '../../imports';
|
||||
import {PartialEvaluator} from '../../partial_evaluator';
|
||||
|
||||
import {scanForRouteEntryPoints} from './lazy';
|
||||
import {scanForCandidateTransitiveModules, scanForRouteEntryPoints} from './lazy';
|
||||
import {RouterEntryPointManager, entryPointKeyFor} from './route';
|
||||
|
||||
export interface NgModuleRawRouteData {
|
||||
@ -48,18 +48,53 @@ export class NgModuleRouteAnalyzer {
|
||||
});
|
||||
}
|
||||
|
||||
listLazyRoutes(): LazyRoute[] {
|
||||
listLazyRoutes(entryModuleKey?: string|undefined): LazyRoute[] {
|
||||
if ((entryModuleKey !== undefined) && !this.modules.has(entryModuleKey)) {
|
||||
throw new Error(`Failed to list lazy routes: Unknown module '${entryModuleKey}'.`);
|
||||
}
|
||||
|
||||
const routes: LazyRoute[] = [];
|
||||
for (const key of Array.from(this.modules.keys())) {
|
||||
const scannedModuleKeys = new Set<string>();
|
||||
const pendingModuleKeys = entryModuleKey ? [entryModuleKey] : Array.from(this.modules.keys());
|
||||
|
||||
// When listing lazy routes for a specific entry module, we need to recursively extract
|
||||
// "transitive" routes from imported/exported modules. This is not necessary when listing all
|
||||
// lazy routes, because all analyzed modules will be scanned anyway.
|
||||
const scanRecursively = entryModuleKey !== undefined;
|
||||
|
||||
while (pendingModuleKeys.length > 0) {
|
||||
const key = pendingModuleKeys.pop() !;
|
||||
|
||||
if (scannedModuleKeys.has(key)) {
|
||||
continue;
|
||||
} else {
|
||||
scannedModuleKeys.add(key);
|
||||
}
|
||||
|
||||
const data = this.modules.get(key) !;
|
||||
const entryPoints = scanForRouteEntryPoints(
|
||||
data.sourceFile, data.moduleName, data, this.entryPointManager, this.evaluator);
|
||||
|
||||
routes.push(...entryPoints.map(entryPoint => ({
|
||||
route: entryPoint.loadChildren,
|
||||
module: entryPoint.from,
|
||||
referencedModule: entryPoint.resolvedTo,
|
||||
})));
|
||||
|
||||
if (scanRecursively) {
|
||||
pendingModuleKeys.push(
|
||||
...[
|
||||
// Scan the retrieved lazy route entry points.
|
||||
...entryPoints.map(
|
||||
({resolvedTo}) => entryPointKeyFor(resolvedTo.filePath, resolvedTo.moduleName)),
|
||||
// Scan the current module's imported modules.
|
||||
...scanForCandidateTransitiveModules(data.imports, this.evaluator),
|
||||
// Scan the current module's exported modules.
|
||||
...scanForCandidateTransitiveModules(data.exports, this.evaluator),
|
||||
].filter(key => this.modules.has(key)));
|
||||
}
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {AbsoluteReference, NodeReference, Reference} from '../../imports';
|
||||
import {ForeignFunctionResolver, PartialEvaluator, ResolvedValue} from '../../partial_evaluator';
|
||||
|
||||
import {NgModuleRawRouteData} from './analyzer';
|
||||
import {RouterEntryPoint, RouterEntryPointManager} from './route';
|
||||
import {RouterEntryPoint, RouterEntryPointManager, entryPointKeyFor} from './route';
|
||||
|
||||
const ROUTES_MARKER = '__ngRoutesMarker__';
|
||||
|
||||
@ -22,6 +22,35 @@ export interface LazyRouteEntry {
|
||||
resolvedTo: RouterEntryPoint;
|
||||
}
|
||||
|
||||
export function scanForCandidateTransitiveModules(
|
||||
expr: ts.Expression | null, evaluator: PartialEvaluator): string[] {
|
||||
if (expr === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const candidateModuleKeys: string[] = [];
|
||||
const entries = evaluator.evaluate(expr);
|
||||
|
||||
function recursivelyAddModules(entry: ResolvedValue) {
|
||||
if (Array.isArray(entry)) {
|
||||
for (const e of entry) {
|
||||
recursivelyAddModules(e);
|
||||
}
|
||||
} else if (entry instanceof Map) {
|
||||
if (entry.has('ngModule')) {
|
||||
recursivelyAddModules(entry.get('ngModule') !);
|
||||
}
|
||||
} else if ((entry instanceof Reference) && hasIdentifier(entry.node)) {
|
||||
const filePath = entry.node.getSourceFile().fileName;
|
||||
const moduleName = entry.node.name.text;
|
||||
candidateModuleKeys.push(entryPointKeyFor(filePath, moduleName));
|
||||
}
|
||||
}
|
||||
|
||||
recursivelyAddModules(entries);
|
||||
return candidateModuleKeys;
|
||||
}
|
||||
|
||||
export function scanForRouteEntryPoints(
|
||||
ngModule: ts.SourceFile, moduleName: string, data: NgModuleRawRouteData,
|
||||
entryPointManager: RouterEntryPointManager, evaluator: PartialEvaluator): LazyRouteEntry[] {
|
||||
@ -152,6 +181,11 @@ const routerModuleFFR: ForeignFunctionResolver =
|
||||
]);
|
||||
};
|
||||
|
||||
function hasIdentifier(node: ts.Node): node is ts.Node&{name: ts.Identifier} {
|
||||
const node_ = node as ts.NamedDeclaration;
|
||||
return (node_.name !== undefined) && ts.isIdentifier(node_.name);
|
||||
}
|
||||
|
||||
function isMethodNodeReference(
|
||||
ref: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>):
|
||||
ref is NodeReference<ts.MethodDeclaration> {
|
||||
|
Reference in New Issue
Block a user