mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-23 15:36:23 +08:00
feat(@ngtools/webpack): add option to control lazy route discovery
This commit is contained in:
parent
7e9297b64d
commit
b54a9379a6
@ -103,6 +103,11 @@ export interface AngularCompilerPluginOptions {
|
|||||||
nameLazyFiles?: boolean;
|
nameLazyFiles?: boolean;
|
||||||
logger?: logging.Logger;
|
logger?: logging.Logger;
|
||||||
directTemplateLoading?: boolean;
|
directTemplateLoading?: boolean;
|
||||||
|
// When using the loadChildren string syntax, @ngtools/webpack must query @angular/compiler-cli
|
||||||
|
// via a private API to know which lazy routes exist. This increases build and rebuild time.
|
||||||
|
// When using Ivy, the string syntax is not supported at all. Thus we shouldn't attempt that.
|
||||||
|
// This option is also used for when the compilation doesn't need this sort of processing at all.
|
||||||
|
discoverLazyRoutes?: boolean;
|
||||||
|
|
||||||
// added to the list of lazy routes
|
// added to the list of lazy routes
|
||||||
additionalLazyModules?: { [module: string]: string };
|
additionalLazyModules?: { [module: string]: string };
|
||||||
@ -134,6 +139,7 @@ export class AngularCompilerPlugin {
|
|||||||
private _compilerHost: WebpackCompilerHost & CompilerHost;
|
private _compilerHost: WebpackCompilerHost & CompilerHost;
|
||||||
private _moduleResolutionCache: ts.ModuleResolutionCache;
|
private _moduleResolutionCache: ts.ModuleResolutionCache;
|
||||||
private _resourceLoader?: WebpackResourceLoader;
|
private _resourceLoader?: WebpackResourceLoader;
|
||||||
|
private _discoverLazyRoutes = true;
|
||||||
// Contains `moduleImportPath#exportName` => `fullModulePath`.
|
// Contains `moduleImportPath#exportName` => `fullModulePath`.
|
||||||
private _lazyRoutes: LazyRouteMap = {};
|
private _lazyRoutes: LazyRouteMap = {};
|
||||||
private _tsConfigPath: string;
|
private _tsConfigPath: string;
|
||||||
@ -292,6 +298,26 @@ export class AngularCompilerPlugin {
|
|||||||
this._platformTransformers = options.platformTransformers;
|
this._platformTransformers = options.platformTransformers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.discoverLazyRoutes !== undefined) {
|
||||||
|
this._discoverLazyRoutes = options.discoverLazyRoutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._discoverLazyRoutes === false && this.options.additionalLazyModuleResources
|
||||||
|
&& this.options.additionalLazyModuleResources.length > 0) {
|
||||||
|
this._warnings.push(
|
||||||
|
new Error(`Lazy route discovery is disabled but additional Lazy Module Resources were`
|
||||||
|
+ ` provided. These will be ignored.`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._discoverLazyRoutes === false && this.options.additionalLazyModules
|
||||||
|
&& Object.keys(this.options.additionalLazyModules).length > 0) {
|
||||||
|
this._warnings.push(
|
||||||
|
new Error(`Lazy route discovery is disabled but additional lazy modules were provided.`
|
||||||
|
+ `These will be ignored.`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Default ContextElementDependency to the one we can import from here.
|
// Default ContextElementDependency to the one we can import from here.
|
||||||
// Failing to use the right ContextElementDependency will throw the error below:
|
// Failing to use the right ContextElementDependency will throw the error below:
|
||||||
// "No module factory available for dependency type: ContextElementDependency"
|
// "No module factory available for dependency type: ContextElementDependency"
|
||||||
@ -411,7 +437,7 @@ export class AngularCompilerPlugin {
|
|||||||
this._entryModule = resolveEntryModuleFromMain(
|
this._entryModule = resolveEntryModuleFromMain(
|
||||||
this._mainPath, this._compilerHost, this._getTsProgram() as ts.Program);
|
this._mainPath, this._compilerHost, this._getTsProgram() as ts.Program);
|
||||||
|
|
||||||
if (!this.entryModule && !this._compilerOptions.enableIvy) {
|
if (this._discoverLazyRoutes && !this.entryModule && !this._compilerOptions.enableIvy) {
|
||||||
this._warnings.push('Lazy routes discovery is not enabled. '
|
this._warnings.push('Lazy routes discovery is not enabled. '
|
||||||
+ 'Because there is neither an entryModule nor a '
|
+ 'Because there is neither an entryModule nor a '
|
||||||
+ 'statically analyzable bootstrap code in the main file.',
|
+ 'statically analyzable bootstrap code in the main file.',
|
||||||
@ -697,64 +723,66 @@ export class AngularCompilerPlugin {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add lazy modules to the context module for @angular/core
|
if (this._discoverLazyRoutes) {
|
||||||
compiler.hooks.contextModuleFactory.tap('angular-compiler', cmf => {
|
// Add lazy modules to the context module for @angular/core
|
||||||
const angularCorePackagePath = require.resolve('@angular/core/package.json');
|
compiler.hooks.contextModuleFactory.tap('angular-compiler', cmf => {
|
||||||
|
const angularCorePackagePath = require.resolve('@angular/core/package.json');
|
||||||
|
|
||||||
// APFv6 does not have single FESM anymore. Instead of verifying if we're pointing to
|
// APFv6 does not have single FESM anymore. Instead of verifying if we're pointing to
|
||||||
// FESMs, we resolve the `@angular/core` path and verify that the path for the
|
// FESMs, we resolve the `@angular/core` path and verify that the path for the
|
||||||
// module starts with it.
|
// module starts with it.
|
||||||
// This may be slower but it will be compatible with both APF5, 6 and potential future
|
// This may be slower but it will be compatible with both APF5, 6 and potential future
|
||||||
// versions (until the dynamic import appears outside of core I suppose).
|
// versions (until the dynamic import appears outside of core I suppose).
|
||||||
// We resolve any symbolic links in order to get the real path that would be used in webpack.
|
// We resolve symbolic links in order to get the real path that would be used in webpack.
|
||||||
const angularCoreResourceRoot = fs.realpathSync(path.dirname(angularCorePackagePath));
|
const angularCoreResourceRoot = fs.realpathSync(path.dirname(angularCorePackagePath));
|
||||||
|
|
||||||
cmf.hooks.afterResolve.tapPromise('angular-compiler', async result => {
|
cmf.hooks.afterResolve.tapPromise('angular-compiler', async result => {
|
||||||
// Alter only existing request from Angular or one of the additional lazy module resources.
|
// Alter only existing request from Angular or the additional lazy module resources.
|
||||||
const isLazyModuleResource = (resource: string) =>
|
const isLazyModuleResource = (resource: string) =>
|
||||||
resource.startsWith(angularCoreResourceRoot) ||
|
resource.startsWith(angularCoreResourceRoot) ||
|
||||||
( this.options.additionalLazyModuleResources &&
|
(this.options.additionalLazyModuleResources &&
|
||||||
this.options.additionalLazyModuleResources.includes(resource));
|
this.options.additionalLazyModuleResources.includes(resource));
|
||||||
|
|
||||||
if (!result || !this.done || !isLazyModuleResource(result.resource)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.done.then(
|
|
||||||
() => {
|
|
||||||
// This folder does not exist, but we need to give webpack a resource.
|
|
||||||
// TODO: check if we can't just leave it as is (angularCoreModuleDir).
|
|
||||||
result.resource = path.join(this._basePath, '$$_lazy_route_resource');
|
|
||||||
// tslint:disable-next-line:no-any
|
|
||||||
result.dependencies.forEach((d: any) => d.critical = false);
|
|
||||||
// tslint:disable-next-line:no-any
|
|
||||||
result.resolveDependencies = (_fs: any, options: any, callback: Callback) => {
|
|
||||||
const dependencies = Object.keys(this._lazyRoutes)
|
|
||||||
.map((key) => {
|
|
||||||
const modulePath = this._lazyRoutes[key];
|
|
||||||
if (modulePath !== null) {
|
|
||||||
const name = key.split('#')[0];
|
|
||||||
|
|
||||||
return new this._contextElementDependencyConstructor(modulePath, name);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(x => !!x);
|
|
||||||
|
|
||||||
if (this._options.nameLazyFiles) {
|
|
||||||
options.chunkName = '[request]';
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, dependencies);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
if (!result || !this.done || !isLazyModuleResource(result.resource)) {
|
||||||
return result;
|
return result;
|
||||||
},
|
}
|
||||||
() => undefined,
|
|
||||||
);
|
return this.done.then(
|
||||||
|
() => {
|
||||||
|
// This folder does not exist, but we need to give webpack a resource.
|
||||||
|
// TODO: check if we can't just leave it as is (angularCoreModuleDir).
|
||||||
|
result.resource = path.join(this._basePath, '$$_lazy_route_resource');
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
result.dependencies.forEach((d: any) => d.critical = false);
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
result.resolveDependencies = (_fs: any, options: any, callback: Callback) => {
|
||||||
|
const dependencies = Object.keys(this._lazyRoutes)
|
||||||
|
.map((key) => {
|
||||||
|
const modulePath = this._lazyRoutes[key];
|
||||||
|
if (modulePath !== null) {
|
||||||
|
const name = key.split('#')[0];
|
||||||
|
|
||||||
|
return new this._contextElementDependencyConstructor(modulePath, name);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(x => !!x);
|
||||||
|
|
||||||
|
if (this._options.nameLazyFiles) {
|
||||||
|
options.chunkName = '[request]';
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, dependencies);
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
() => undefined,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// Create and destroy forked type checker on watch mode.
|
// Create and destroy forked type checker on watch mode.
|
||||||
compiler.hooks.watchRun.tap('angular-compiler', () => {
|
compiler.hooks.watchRun.tap('angular-compiler', () => {
|
||||||
@ -922,28 +950,30 @@ export class AngularCompilerPlugin {
|
|||||||
// Make a new program and load the Angular structure.
|
// Make a new program and load the Angular structure.
|
||||||
await this._createOrUpdateProgram();
|
await this._createOrUpdateProgram();
|
||||||
|
|
||||||
// Try to find lazy routes if we have an entry module.
|
if (this._discoverLazyRoutes) {
|
||||||
// We need to run the `listLazyRoutes` the first time because it also navigates libraries
|
// Try to find lazy routes if we have an entry module.
|
||||||
// and other things that we might miss using the (faster) findLazyRoutesInAst.
|
// We need to run the `listLazyRoutes` the first time because it also navigates libraries
|
||||||
// Lazy routes modules will be read with compilerHost and added to the changed files.
|
// and other things that we might miss using the (faster) findLazyRoutesInAst.
|
||||||
let lazyRouteMap: LazyRouteMap = {};
|
// Lazy routes modules will be read with compilerHost and added to the changed files.
|
||||||
if (!this._JitMode || this._firstRun) {
|
let lazyRouteMap: LazyRouteMap = {};
|
||||||
lazyRouteMap = this._listLazyRoutesFromProgram();
|
if (!this._JitMode || this._firstRun) {
|
||||||
} else {
|
lazyRouteMap = this._listLazyRoutesFromProgram();
|
||||||
const changedTsFiles = this._getChangedTsFiles();
|
} else {
|
||||||
if (changedTsFiles.length > 0) {
|
const changedTsFiles = this._getChangedTsFiles();
|
||||||
lazyRouteMap = this._findLazyRoutesInAst(changedTsFiles);
|
if (changedTsFiles.length > 0) {
|
||||||
|
lazyRouteMap = this._findLazyRoutesInAst(changedTsFiles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find lazy routes
|
||||||
|
lazyRouteMap = {
|
||||||
|
...lazyRouteMap,
|
||||||
|
...this._options.additionalLazyModules,
|
||||||
|
};
|
||||||
|
|
||||||
|
this._processLazyRoutes(lazyRouteMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find lazy routes
|
|
||||||
lazyRouteMap = {
|
|
||||||
...lazyRouteMap,
|
|
||||||
...this._options.additionalLazyModules,
|
|
||||||
};
|
|
||||||
|
|
||||||
this._processLazyRoutes(lazyRouteMap);
|
|
||||||
|
|
||||||
// Emit files.
|
// Emit files.
|
||||||
time('AngularCompilerPlugin._update._emit');
|
time('AngularCompilerPlugin._update._emit');
|
||||||
const { emitResult, diagnostics } = this._emit();
|
const { emitResult, diagnostics } = this._emit();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user