Skip to content

Commit b54a937

Browse files
filipesilvamgechev
authored andcommitted
feat(@ngtools/webpack): add option to control lazy route discovery
1 parent 7e9297b commit b54a937

File tree

1 file changed

+105
-75
lines changed

1 file changed

+105
-75
lines changed

packages/ngtools/webpack/src/angular_compiler_plugin.ts

+105-75
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ export interface AngularCompilerPluginOptions {
103103
nameLazyFiles?: boolean;
104104
logger?: logging.Logger;
105105
directTemplateLoading?: boolean;
106+
// When using the loadChildren string syntax, @ngtools/webpack must query @angular/compiler-cli
107+
// via a private API to know which lazy routes exist. This increases build and rebuild time.
108+
// When using Ivy, the string syntax is not supported at all. Thus we shouldn't attempt that.
109+
// This option is also used for when the compilation doesn't need this sort of processing at all.
110+
discoverLazyRoutes?: boolean;
106111

107112
// added to the list of lazy routes
108113
additionalLazyModules?: { [module: string]: string };
@@ -134,6 +139,7 @@ export class AngularCompilerPlugin {
134139
private _compilerHost: WebpackCompilerHost & CompilerHost;
135140
private _moduleResolutionCache: ts.ModuleResolutionCache;
136141
private _resourceLoader?: WebpackResourceLoader;
142+
private _discoverLazyRoutes = true;
137143
// Contains `moduleImportPath#exportName` => `fullModulePath`.
138144
private _lazyRoutes: LazyRouteMap = {};
139145
private _tsConfigPath: string;
@@ -292,6 +298,26 @@ export class AngularCompilerPlugin {
292298
this._platformTransformers = options.platformTransformers;
293299
}
294300

301+
if (options.discoverLazyRoutes !== undefined) {
302+
this._discoverLazyRoutes = options.discoverLazyRoutes;
303+
}
304+
305+
if (this._discoverLazyRoutes === false && this.options.additionalLazyModuleResources
306+
&& this.options.additionalLazyModuleResources.length > 0) {
307+
this._warnings.push(
308+
new Error(`Lazy route discovery is disabled but additional Lazy Module Resources were`
309+
+ ` provided. These will be ignored.`),
310+
);
311+
}
312+
313+
if (this._discoverLazyRoutes === false && this.options.additionalLazyModules
314+
&& Object.keys(this.options.additionalLazyModules).length > 0) {
315+
this._warnings.push(
316+
new Error(`Lazy route discovery is disabled but additional lazy modules were provided.`
317+
+ `These will be ignored.`),
318+
);
319+
}
320+
295321
// Default ContextElementDependency to the one we can import from here.
296322
// Failing to use the right ContextElementDependency will throw the error below:
297323
// "No module factory available for dependency type: ContextElementDependency"
@@ -411,7 +437,7 @@ export class AngularCompilerPlugin {
411437
this._entryModule = resolveEntryModuleFromMain(
412438
this._mainPath, this._compilerHost, this._getTsProgram() as ts.Program);
413439

414-
if (!this.entryModule && !this._compilerOptions.enableIvy) {
440+
if (this._discoverLazyRoutes && !this.entryModule && !this._compilerOptions.enableIvy) {
415441
this._warnings.push('Lazy routes discovery is not enabled. '
416442
+ 'Because there is neither an entryModule nor a '
417443
+ 'statically analyzable bootstrap code in the main file.',
@@ -697,64 +723,66 @@ export class AngularCompilerPlugin {
697723
);
698724
});
699725

700-
// Add lazy modules to the context module for @angular/core
701-
compiler.hooks.contextModuleFactory.tap('angular-compiler', cmf => {
702-
const angularCorePackagePath = require.resolve('@angular/core/package.json');
703-
704-
// APFv6 does not have single FESM anymore. Instead of verifying if we're pointing to
705-
// FESMs, we resolve the `@angular/core` path and verify that the path for the
706-
// module starts with it.
707-
// This may be slower but it will be compatible with both APF5, 6 and potential future
708-
// versions (until the dynamic import appears outside of core I suppose).
709-
// We resolve any symbolic links in order to get the real path that would be used in webpack.
710-
const angularCoreResourceRoot = fs.realpathSync(path.dirname(angularCorePackagePath));
711-
712-
cmf.hooks.afterResolve.tapPromise('angular-compiler', async result => {
713-
// Alter only existing request from Angular or one of the additional lazy module resources.
714-
const isLazyModuleResource = (resource: string) =>
715-
resource.startsWith(angularCoreResourceRoot) ||
716-
( this.options.additionalLazyModuleResources &&
717-
this.options.additionalLazyModuleResources.includes(resource));
718-
719-
if (!result || !this.done || !isLazyModuleResource(result.resource)) {
720-
return result;
721-
}
722-
723-
return this.done.then(
724-
() => {
725-
// This folder does not exist, but we need to give webpack a resource.
726-
// TODO: check if we can't just leave it as is (angularCoreModuleDir).
727-
result.resource = path.join(this._basePath, '$$_lazy_route_resource');
728-
// tslint:disable-next-line:no-any
729-
result.dependencies.forEach((d: any) => d.critical = false);
730-
// tslint:disable-next-line:no-any
731-
result.resolveDependencies = (_fs: any, options: any, callback: Callback) => {
732-
const dependencies = Object.keys(this._lazyRoutes)
733-
.map((key) => {
734-
const modulePath = this._lazyRoutes[key];
735-
if (modulePath !== null) {
736-
const name = key.split('#')[0];
737-
738-
return new this._contextElementDependencyConstructor(modulePath, name);
739-
} else {
740-
return null;
741-
}
742-
})
743-
.filter(x => !!x);
744-
745-
if (this._options.nameLazyFiles) {
746-
options.chunkName = '[request]';
747-
}
748-
749-
callback(null, dependencies);
750-
};
751-
726+
if (this._discoverLazyRoutes) {
727+
// Add lazy modules to the context module for @angular/core
728+
compiler.hooks.contextModuleFactory.tap('angular-compiler', cmf => {
729+
const angularCorePackagePath = require.resolve('@angular/core/package.json');
730+
731+
// APFv6 does not have single FESM anymore. Instead of verifying if we're pointing to
732+
// FESMs, we resolve the `@angular/core` path and verify that the path for the
733+
// module starts with it.
734+
// This may be slower but it will be compatible with both APF5, 6 and potential future
735+
// versions (until the dynamic import appears outside of core I suppose).
736+
// We resolve symbolic links in order to get the real path that would be used in webpack.
737+
const angularCoreResourceRoot = fs.realpathSync(path.dirname(angularCorePackagePath));
738+
739+
cmf.hooks.afterResolve.tapPromise('angular-compiler', async result => {
740+
// Alter only existing request from Angular or the additional lazy module resources.
741+
const isLazyModuleResource = (resource: string) =>
742+
resource.startsWith(angularCoreResourceRoot) ||
743+
(this.options.additionalLazyModuleResources &&
744+
this.options.additionalLazyModuleResources.includes(resource));
745+
746+
if (!result || !this.done || !isLazyModuleResource(result.resource)) {
752747
return result;
753-
},
754-
() => undefined,
755-
);
748+
}
749+
750+
return this.done.then(
751+
() => {
752+
// This folder does not exist, but we need to give webpack a resource.
753+
// TODO: check if we can't just leave it as is (angularCoreModuleDir).
754+
result.resource = path.join(this._basePath, '$$_lazy_route_resource');
755+
// tslint:disable-next-line:no-any
756+
result.dependencies.forEach((d: any) => d.critical = false);
757+
// tslint:disable-next-line:no-any
758+
result.resolveDependencies = (_fs: any, options: any, callback: Callback) => {
759+
const dependencies = Object.keys(this._lazyRoutes)
760+
.map((key) => {
761+
const modulePath = this._lazyRoutes[key];
762+
if (modulePath !== null) {
763+
const name = key.split('#')[0];
764+
765+
return new this._contextElementDependencyConstructor(modulePath, name);
766+
} else {
767+
return null;
768+
}
769+
})
770+
.filter(x => !!x);
771+
772+
if (this._options.nameLazyFiles) {
773+
options.chunkName = '[request]';
774+
}
775+
776+
callback(null, dependencies);
777+
};
778+
779+
return result;
780+
},
781+
() => undefined,
782+
);
783+
});
756784
});
757-
});
785+
}
758786

759787
// Create and destroy forked type checker on watch mode.
760788
compiler.hooks.watchRun.tap('angular-compiler', () => {
@@ -922,27 +950,29 @@ export class AngularCompilerPlugin {
922950
// Make a new program and load the Angular structure.
923951
await this._createOrUpdateProgram();
924952

925-
// Try to find lazy routes if we have an entry module.
926-
// We need to run the `listLazyRoutes` the first time because it also navigates libraries
927-
// and other things that we might miss using the (faster) findLazyRoutesInAst.
928-
// Lazy routes modules will be read with compilerHost and added to the changed files.
929-
let lazyRouteMap: LazyRouteMap = {};
930-
if (!this._JitMode || this._firstRun) {
931-
lazyRouteMap = this._listLazyRoutesFromProgram();
932-
} else {
933-
const changedTsFiles = this._getChangedTsFiles();
934-
if (changedTsFiles.length > 0) {
935-
lazyRouteMap = this._findLazyRoutesInAst(changedTsFiles);
953+
if (this._discoverLazyRoutes) {
954+
// Try to find lazy routes if we have an entry module.
955+
// We need to run the `listLazyRoutes` the first time because it also navigates libraries
956+
// and other things that we might miss using the (faster) findLazyRoutesInAst.
957+
// Lazy routes modules will be read with compilerHost and added to the changed files.
958+
let lazyRouteMap: LazyRouteMap = {};
959+
if (!this._JitMode || this._firstRun) {
960+
lazyRouteMap = this._listLazyRoutesFromProgram();
961+
} else {
962+
const changedTsFiles = this._getChangedTsFiles();
963+
if (changedTsFiles.length > 0) {
964+
lazyRouteMap = this._findLazyRoutesInAst(changedTsFiles);
965+
}
936966
}
937-
}
938967

939-
// Find lazy routes
940-
lazyRouteMap = {
941-
...lazyRouteMap,
942-
...this._options.additionalLazyModules,
943-
};
968+
// Find lazy routes
969+
lazyRouteMap = {
970+
...lazyRouteMap,
971+
...this._options.additionalLazyModules,
972+
};
944973

945-
this._processLazyRoutes(lazyRouteMap);
974+
this._processLazyRoutes(lazyRouteMap);
975+
}
946976

947977
// Emit files.
948978
time('AngularCompilerPlugin._update._emit');

0 commit comments

Comments
 (0)