Skip to content

perf(@ngtools/webpack): avoid full compiler lazy route analysis on JIT rebuilds #20237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 11, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 53 additions & 29 deletions packages/ngtools/webpack/src/ivy/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
NormalModuleReplacementPlugin,
compilation,
} from 'webpack';
import { findLazyRoutes } from '../lazy_routes';
import { NgccProcessor } from '../ngcc_processor';
import { TypeScriptPathsPlugin } from '../paths-plugin';
import { WebpackResourceLoader } from '../resource_loader';
Expand Down Expand Up @@ -229,7 +230,7 @@ export class AngularWebpackPlugin {

// Create the file emitter used by the webpack loader
const { fileEmitter, builder, internalFiles } = this.pluginOptions.jitMode
? this.updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter)
? this.updateJitProgram(compilerOptions, rootNames, host, diagnosticsReporter, changedFiles)
: this.updateAotProgram(
compilerOptions,
rootNames,
Expand Down Expand Up @@ -523,6 +524,7 @@ export class AngularWebpackPlugin {
rootNames: readonly string[],
host: CompilerHost,
diagnosticsReporter: DiagnosticsReporter,
changedFiles: Set<string> | undefined,
) {
const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
rootNames,
Expand All @@ -547,39 +549,61 @@ export class AngularWebpackPlugin {

const transformers = createJitTransformers(builder, this.pluginOptions);

// Required to support asynchronous resource loading
// Must be done before listing lazy routes
// NOTE: This can be removed once support for the deprecated lazy route string format is removed
const angularProgram = new NgtscProgram(
rootNames,
compilerOptions,
host,
this.ngtscNextProgram,
);
const angularCompiler = angularProgram.compiler;
const pendingAnalysis = angularCompiler.analyzeAsync().then(() => {
for (const lazyRoute of angularCompiler.listLazyRoutes()) {
const [routeKey] = lazyRoute.route.split('#');
this.lazyRouteMap[routeKey] = lazyRoute.referencedModule.filePath;
// Only do a full, expensive Angular compiler string lazy route analysis on the first build
// `changedFiles` will be undefined on a first build
if (!changedFiles) {
// Required to support asynchronous resource loading
// Must be done before listing lazy routes
// NOTE: This can be removed once support for the deprecated lazy route string format is removed
const angularProgram = new NgtscProgram(
rootNames,
compilerOptions,
host,
this.ngtscNextProgram,
);
const angularCompiler = angularProgram.compiler;
const pendingAnalysis = angularCompiler.analyzeAsync().then(() => {
for (const lazyRoute of angularCompiler.listLazyRoutes()) {
const [routeKey] = lazyRoute.route.split('#');
this.lazyRouteMap[routeKey] = lazyRoute.referencedModule.filePath;
}

return this.createFileEmitter(builder, transformers, () => []);
});
const analyzingFileEmitter: FileEmitter = async (file) => {
const innerFileEmitter = await pendingAnalysis;

return innerFileEmitter(file);
};

if (this.watchMode) {
this.ngtscNextProgram = angularProgram;
Comment on lines +579 to +580
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be dropped now?

}

return this.createFileEmitter(builder, transformers, () => []);
});
const analyzingFileEmitter: FileEmitter = async (file) => {
const innerFileEmitter = await pendingAnalysis;
return {
fileEmitter: analyzingFileEmitter,
builder,
internalFiles: undefined,
};
} else {
// Update lazy route map for changed files using fast but less accurate method
for (const changedFile of changedFiles) {
if (!builder.getSourceFile(changedFile)) {
continue;
}

return innerFileEmitter(file);
};
const routes = findLazyRoutes(changedFile, host, builder.getProgram());
for (const [routeKey, filePath] of Object.entries(routes)) {
this.lazyRouteMap[routeKey] = filePath;
}
}

if (this.watchMode) {
this.ngtscNextProgram = angularProgram;
return {
fileEmitter: this.createFileEmitter(builder, transformers, () => []),
builder,
internalFiles: undefined,
};
}

return {
fileEmitter: analyzingFileEmitter,
builder,
internalFiles: undefined,
};
}

private createFileEmitter(
Expand Down