Skip to content

Commit 3d53d61

Browse files
Alan Agiusalexeagle
Alan Agius
authored andcommitted
revert: fix(@ngtools/webpack): emit lazy routes errors on rebuilds
This reverts commit edb84b3 The team has decided to bring back the faster but potentially less accurate method of detecting lazy routes upon JIT rebuilds (first builds will always use the more complete Angular compiler method). Applications that do not have lazy routes within libraries and that only use direct string literals with loadChildren should not be affected by the potential of less accurate detection. Note that the function overload of loadChildren also does not apply to this situation. For those projects where correctness of lazy route detection outweighs rebuild speed, please consider using AOT mode for development. AOT mode will also provide a full set of template errors as well which JIT mode is not capable of doing. Fixes angular#13102
1 parent bababfd commit 3d53d61

File tree

3 files changed

+142
-8
lines changed

3 files changed

+142
-8
lines changed

src/angular_compiler_plugin.ts

+41-4
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ import { time, timeEnd } from './benchmark';
3939
import { WebpackCompilerHost, workaroundResolve } from './compiler_host';
4040
import { resolveEntryModuleFromMain } from './entry_resolver';
4141
import { DiagnosticMode, gatherDiagnostics, hasErrors } from './gather_diagnostics';
42+
import { LazyRouteMap, findLazyRoutes } from './lazy_routes';
4243
import { TypeScriptPathsPlugin } from './paths-plugin';
4344
import { WebpackResourceLoader } from './resource_loader';
4445
import {
45-
LazyRouteMap,
4646
exportLazyModuleMap,
4747
exportNgFactory,
4848
findResources,
@@ -135,7 +135,7 @@ export class AngularCompilerPlugin {
135135
private _moduleResolutionCache: ts.ModuleResolutionCache;
136136
private _resourceLoader?: WebpackResourceLoader;
137137
// Contains `moduleImportPath#exportName` => `fullModulePath`.
138-
private _lazyRoutes: LazyRouteMap = Object.create(null);
138+
private _lazyRoutes: LazyRouteMap = {};
139139
private _tsConfigPath: string;
140140
private _entryModule: string | null;
141141
private _mainPath: string | undefined;
@@ -421,6 +421,22 @@ export class AngularCompilerPlugin {
421421
}
422422
}
423423

424+
private _findLazyRoutesInAst(changedFilePaths: string[]): LazyRouteMap {
425+
time('AngularCompilerPlugin._findLazyRoutesInAst');
426+
const result: LazyRouteMap = {};
427+
for (const filePath of changedFilePaths) {
428+
const fileLazyRoutes = findLazyRoutes(filePath, this._compilerHost, undefined,
429+
this._compilerOptions);
430+
for (const routeKey of Object.keys(fileLazyRoutes)) {
431+
const route = fileLazyRoutes[routeKey];
432+
result[routeKey] = route;
433+
}
434+
}
435+
timeEnd('AngularCompilerPlugin._findLazyRoutesInAst');
436+
437+
return result;
438+
}
439+
424440
private _listLazyRoutesFromProgram(): LazyRouteMap {
425441
let entryRoute: string | undefined;
426442
let ngProgram: Program;
@@ -876,6 +892,12 @@ export class AngularCompilerPlugin {
876892
}
877893
}
878894

895+
private _getChangedTsFiles() {
896+
return this._getChangedCompilationFiles()
897+
.filter(k => (k.endsWith('.ts') || k.endsWith('.tsx')) && !k.endsWith('.d.ts'))
898+
.filter(k => this._compilerHost.fileExists(k));
899+
}
900+
879901
private async _update() {
880902
time('AngularCompilerPlugin._update');
881903
// We only want to update on TS and template changes, but all kinds of files are on this
@@ -890,11 +912,26 @@ export class AngularCompilerPlugin {
890912
// Make a new program and load the Angular structure.
891913
await this._createOrUpdateProgram();
892914

915+
// Try to find lazy routes if we have an entry module.
916+
// We need to run the `listLazyRoutes` the first time because it also navigates libraries
917+
// and other things that we might miss using the (faster) findLazyRoutesInAst.
918+
// Lazy routes modules will be read with compilerHost and added to the changed files.
919+
let lazyRouteMap: LazyRouteMap = {};
920+
if (!this._JitMode || this._firstRun) {
921+
lazyRouteMap = this._listLazyRoutesFromProgram();
922+
} else {
923+
const changedTsFiles = this._getChangedTsFiles();
924+
if (changedTsFiles.length > 0) {
925+
lazyRouteMap = this._findLazyRoutesInAst(changedTsFiles);
926+
}
927+
}
928+
893929
// Find lazy routes
894-
const lazyRouteMap: LazyRouteMap = {
895-
...this._listLazyRoutesFromProgram(),
930+
lazyRouteMap = {
931+
...lazyRouteMap,
896932
...this._options.additionalLazyModules,
897933
};
934+
898935
this._processLazyRoutes(lazyRouteMap);
899936

900937
// Emit files.

src/lazy_routes.ts

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { dirname, join } from 'path';
9+
import * as ts from 'typescript';
10+
import { findAstNodes, resolve } from './refactor';
11+
12+
13+
function _getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): string | null {
14+
if (node.kind == ts.SyntaxKind.Identifier) {
15+
return (node as ts.Identifier).text;
16+
} else if (node.kind == ts.SyntaxKind.StringLiteral) {
17+
return (node as ts.StringLiteral).text;
18+
} else {
19+
return null;
20+
}
21+
}
22+
23+
24+
export interface LazyRouteMap {
25+
[path: string]: string;
26+
}
27+
28+
29+
export function findLazyRoutes(
30+
filePath: string,
31+
host: ts.CompilerHost,
32+
program?: ts.Program,
33+
compilerOptions?: ts.CompilerOptions,
34+
): LazyRouteMap {
35+
if (!compilerOptions) {
36+
if (!program) {
37+
throw new Error('Must pass either program or compilerOptions to findLazyRoutes.');
38+
}
39+
compilerOptions = program.getCompilerOptions();
40+
}
41+
const fileName = resolve(filePath, host, compilerOptions).replace(/\\/g, '/');
42+
let sourceFile: ts.SourceFile | undefined;
43+
if (program) {
44+
sourceFile = program.getSourceFile(fileName);
45+
}
46+
47+
if (!sourceFile) {
48+
const content = host.readFile(fileName);
49+
if (content) {
50+
sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
51+
}
52+
}
53+
54+
if (!sourceFile) {
55+
throw new Error(`Source file not found: '${fileName}'.`);
56+
}
57+
const sf: ts.SourceFile = sourceFile;
58+
59+
return findAstNodes(null, sourceFile, ts.SyntaxKind.ObjectLiteralExpression, true)
60+
// Get all their property assignments.
61+
.map((node: ts.ObjectLiteralExpression) => {
62+
return findAstNodes(node, sf, ts.SyntaxKind.PropertyAssignment, false);
63+
})
64+
// Take all `loadChildren` elements.
65+
.reduce((acc: ts.PropertyAssignment[], props: ts.PropertyAssignment[]) => {
66+
return acc.concat(props.filter(literal => {
67+
return _getContentOfKeyLiteral(sf, literal.name) == 'loadChildren';
68+
}));
69+
}, [])
70+
// Get only string values.
71+
.filter((node: ts.PropertyAssignment) => node.initializer.kind == ts.SyntaxKind.StringLiteral)
72+
// Get the string value.
73+
.map((node: ts.PropertyAssignment) => (node.initializer as ts.StringLiteral).text)
74+
// Map those to either [path, absoluteModulePath], or [path, null] if the module pointing to
75+
// does not exist.
76+
.map((routePath: string) => {
77+
const moduleName = routePath.split('#')[0];
78+
const compOptions = (program && program.getCompilerOptions()) || compilerOptions || {};
79+
const resolvedModuleName: ts.ResolvedModuleWithFailedLookupLocations = moduleName[0] == '.'
80+
? ({
81+
resolvedModule: { resolvedFileName: join(dirname(filePath), moduleName) + '.ts' },
82+
} as ts.ResolvedModuleWithFailedLookupLocations)
83+
: ts.resolveModuleName(moduleName, filePath, compOptions, host);
84+
if (resolvedModuleName.resolvedModule
85+
&& resolvedModuleName.resolvedModule.resolvedFileName
86+
&& host.fileExists(resolvedModuleName.resolvedModule.resolvedFileName)) {
87+
return [routePath, resolvedModuleName.resolvedModule.resolvedFileName];
88+
} else {
89+
return [routePath, null];
90+
}
91+
})
92+
// Reduce to the LazyRouteMap map.
93+
.reduce((acc: LazyRouteMap, [routePath, resolvedModuleName]: [string, string | null]) => {
94+
if (resolvedModuleName) {
95+
acc[routePath] = resolvedModuleName;
96+
}
97+
98+
return acc;
99+
}, {});
100+
}

src/transformers/export_lazy_module_map.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,11 @@
77
*/
88
import * as path from 'path';
99
import * as ts from 'typescript';
10+
import { LazyRouteMap } from '../lazy_routes';
1011
import { getFirstNode, getLastNode } from './ast_helpers';
1112
import { AddNodeOperation, StandardTransform, TransformOperation } from './interfaces';
1213
import { makeTransform } from './make_transform';
1314

14-
export interface LazyRouteMap {
15-
[path: string]: string;
16-
}
17-
1815
export function exportLazyModuleMap(
1916
shouldTransform: (fileName: string) => boolean,
2017
lazyRoutesCb: () => LazyRouteMap,

0 commit comments

Comments
 (0)