diff --git a/packages/ngtools/webpack/src/angular_compiler_plugin.ts b/packages/ngtools/webpack/src/angular_compiler_plugin.ts index d39cfebd57..4bb1041cc8 100644 --- a/packages/ngtools/webpack/src/angular_compiler_plugin.ts +++ b/packages/ngtools/webpack/src/angular_compiler_plugin.ts @@ -67,6 +67,7 @@ export interface AngularCompilerPluginOptions { tsConfigPath: string; basePath?: string; entryModule?: string; + entryModules?: string[]; mainPath?: string; skipCodeGeneration?: boolean; hostReplacementPaths?: { [path: string]: string }; @@ -112,6 +113,7 @@ export class AngularCompilerPlugin { private _lazyRoutes: LazyRouteMap = Object.create(null); private _tsConfigPath: string; private _entryModule: string | null; + private _entryModules: string[] | null; private _mainPath: string | undefined; private _basePath: string; private _transformers: ts.TransformerFactory[] = []; @@ -148,15 +150,18 @@ export class AngularCompilerPlugin { get options() { return this._options; } get done() { return this._donePromise; } - get entryModule() { - if (!this._entryModule) { + get entryModules() { + if (!this._entryModules) { return null; } - const splitted = this._entryModule.split(/(#[a-zA-Z_]([\w]+))$/); - const path = splitted[0]; - const className = !!splitted[1] ? splitted[1].substring(1) : 'default'; - return { path, className }; + return this._entryModules.map((entryModule) => { + const splitted = entryModule.split(/(#[a-zA-Z_]([\w]+))$/); + const path = splitted[0]; + const className = !!splitted[1] ? splitted[1].substring(1) : 'default'; + + return { path, className }; + }); } static isSupported() { @@ -294,13 +299,13 @@ export class AngularCompilerPlugin { this._mainPath = this._compilerHost.resolve(options.mainPath); } - // Use entryModule if available in options, otherwise resolve it from mainPath after program + // Use entryModules if available in options, otherwise resolve it from mainPath after program // creation. - if (this._options.entryModule) { - this._entryModule = this._options.entryModule; + if (this._options.entryModules) { + this._entryModules = this._options.entryModules || [this._options.entryModule]; } else if (this._compilerOptions.entryModule) { - this._entryModule = path.resolve(this._basePath, - this._compilerOptions.entryModule as string); // temporary cast for type issue + this._entryModules = [path.resolve(this._basePath, + this._compilerOptions.entryModule as string)]; // temporary cast for type issue } // Set platform. @@ -391,11 +396,12 @@ export class AngularCompilerPlugin { } }) .then(() => { - // If there's still no entryModule try to resolve from mainPath. - if (!this._entryModule && this._mainPath) { + // If there's still no entryModules try to resolve from mainPath. + if (!this._entryModules && this._mainPath) { time('AngularCompilerPlugin._make.resolveEntryModuleFromMain'); - this._entryModule = resolveEntryModuleFromMain( + const entryModule = resolveEntryModuleFromMain( this._mainPath, this._compilerHost, this._getTsProgram()); + this._entryModules = entryModule !== null ? [entryModule] : entryModule; timeEnd('AngularCompilerPlugin._make.resolveEntryModuleFromMain'); } }); @@ -413,7 +419,7 @@ export class AngularCompilerPlugin { }), // TODO: fix compiler-cli typings; entryModule should not be string, but also optional. // tslint:disable-next-line:non-null-operator - entryModule: this._entryModule !, + entryModules: this._entryModules !, }); timeEnd('AngularCompilerPlugin._getLazyRoutesFromNgtools'); @@ -742,9 +748,12 @@ export class AngularCompilerPlugin { const isMainPath = (fileName: string) => fileName === ( this._mainPath ? workaroundResolve(this._mainPath) : this._mainPath ); - const getEntryModule = () => this.entryModule - ? { path: workaroundResolve(this.entryModule.path), className: this.entryModule.className } - : this.entryModule; + // TODO: fix fn usage + const getEntryModules = () => this.entryModules + ? this.entryModules.map((entryModule) => { + return { path: workaroundResolve(entryModule.path), className: entryModule.className }; + }) + : this.entryModules; const getLazyRoutes = () => this._lazyRoutes; const getTypeChecker = () => this._getTsProgram().getTypeChecker(); @@ -761,20 +770,20 @@ export class AngularCompilerPlugin { // This transform must go before replaceBootstrap because it looks for the entry module // import, which will be replaced. if (this._normalizedLocale) { - this._transformers.push(registerLocaleData(isAppPath, getEntryModule, + this._transformers.push(registerLocaleData(isAppPath, getEntryModules, this._normalizedLocale)); } if (!this._JitMode) { // Replace bootstrap in browser AOT. - this._transformers.push(replaceBootstrap(isAppPath, getEntryModule, getTypeChecker)); + this._transformers.push(replaceBootstrap(isAppPath, getEntryModules, getTypeChecker)); } } else if (this._platform === PLATFORM.Server) { this._transformers.push(exportLazyModuleMap(isMainPath, getLazyRoutes)); if (!this._JitMode) { this._transformers.push( - exportNgFactory(isMainPath, getEntryModule), - replaceServerBootstrap(isMainPath, getEntryModule, getTypeChecker)); + exportNgFactory(isMainPath, getEntryModules), + replaceServerBootstrap(isMainPath, getEntryModules, getTypeChecker)); } } } @@ -794,7 +803,7 @@ export class AngularCompilerPlugin { // Make a new program and load the Angular structure. .then(() => this._createOrUpdateProgram()) .then(() => { - if (this.entryModule) { + if (this.entryModules) { // Try to find lazy routes if we have an entry module. // We need to run the `listLazyRoutes` the first time because it also navigates libraries // and other things that we might miss using the (faster) findLazyRoutesInAst. diff --git a/packages/ngtools/webpack/src/transformers/export_ngfactory.ts b/packages/ngtools/webpack/src/transformers/export_ngfactory.ts index 04a8afd1bc..c09e086993 100644 --- a/packages/ngtools/webpack/src/transformers/export_ngfactory.ts +++ b/packages/ngtools/webpack/src/transformers/export_ngfactory.ts @@ -13,18 +13,29 @@ import { makeTransform } from './make_transform'; export function exportNgFactory( shouldTransform: (fileName: string) => boolean, - getEntryModule: () => { path: string, className: string } | null, + getEntryModules: () => { path: string, className: string }[] | null, ): ts.TransformerFactory { const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) { + const ops: TransformOperation[] = []; - const entryModule = getEntryModule(); + const entryModules = getEntryModules(); - if (!shouldTransform(sourceFile.fileName) || !entryModule) { + if (!shouldTransform(sourceFile.fileName) || !entryModules) { return ops; } + return entryModules.reduce((ops, entryModule) => { + return ops.concat(standardTransformHelper(sourceFile, entryModule)); + }, ops); + }; + + const standardTransformHelper = function ( + sourceFile: ts.SourceFile, + entryModule: { path: string, className: string }) { + const ops: TransformOperation[] = []; + // Find all identifiers using the entry module class name. const entryModuleIdentifiers = collectDeepNodes(sourceFile, ts.SyntaxKind.Identifier) diff --git a/packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts b/packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts index 52dc70c098..9217b85828 100644 --- a/packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts +++ b/packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts @@ -22,7 +22,7 @@ describe('@ngtools/webpack transformers', () => { const transformer = exportNgFactory( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), ); const result = transformTypescript(input, [transformer]); @@ -40,7 +40,7 @@ describe('@ngtools/webpack transformers', () => { const transformer = exportNgFactory( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), ); const result = transformTypescript(input, [transformer]); @@ -54,7 +54,7 @@ describe('@ngtools/webpack transformers', () => { const transformer = exportNgFactory( () => false, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), ); const result = transformTypescript(input, [transformer]); diff --git a/packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts b/packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts index 0726efb20d..32e26fddd0 100644 --- a/packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts +++ b/packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts @@ -67,14 +67,14 @@ describe('@ngtools/webpack transformers', () => { const { program, compilerHost } = createTypescriptContext(input); const shouldTransform = () => true; - const getEntryModule = () => - ({ path: '/project/src/app/app.module', className: 'AppModule' }); + const getEntryModules = () => + ([{ path: '/project/src/app/app.module', className: 'AppModule' }]); const getTypeChecker = () => program.getTypeChecker(); const transformers = [ - replaceBootstrap(shouldTransform, getEntryModule, getTypeChecker), - exportNgFactory(shouldTransform, getEntryModule), + replaceBootstrap(shouldTransform, getEntryModules, getTypeChecker), + exportNgFactory(shouldTransform, getEntryModules), exportLazyModuleMap(shouldTransform, () => ({ './lazy/lazy.module.ngfactory#LazyModuleNgFactory': diff --git a/packages/ngtools/webpack/src/transformers/register_locale_data.ts b/packages/ngtools/webpack/src/transformers/register_locale_data.ts index fd2aa20566..7a5bec2774 100644 --- a/packages/ngtools/webpack/src/transformers/register_locale_data.ts +++ b/packages/ngtools/webpack/src/transformers/register_locale_data.ts @@ -14,19 +14,30 @@ import { makeTransform } from './make_transform'; export function registerLocaleData( shouldTransform: (fileName: string) => boolean, - getEntryModule: () => { path: string, className: string } | null, + getEntryModules: () => { path: string, className: string }[] | null, locale: string, ): ts.TransformerFactory { const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) { + const ops: TransformOperation[] = []; - const entryModule = getEntryModule(); + const entryModules = getEntryModules(); - if (!shouldTransform(sourceFile.fileName) || !entryModule || !locale) { + if (!shouldTransform(sourceFile.fileName) || !entryModules || !locale) { return ops; } + return entryModules.reduce((ops, entryModule) => { + return ops.concat(standardTransformHelper(sourceFile, entryModule)); + }, ops); + }; + + const standardTransformHelper = function ( + sourceFile: ts.SourceFile, + entryModule: { path: string, className: string }) { + const ops: TransformOperation[] = []; + // Find all identifiers using the entry module class name. const entryModuleIdentifiers = collectDeepNodes(sourceFile, ts.SyntaxKind.Identifier) diff --git a/packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts b/packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts index 1310bfce3e..717dcfdb04 100644 --- a/packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts +++ b/packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts @@ -45,7 +45,7 @@ describe('@ngtools/webpack transformers', () => { const transformer = registerLocaleData( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), 'fr', ); const result = transformTypescript(input, [transformer]); diff --git a/packages/ngtools/webpack/src/transformers/replace_bootstrap.ts b/packages/ngtools/webpack/src/transformers/replace_bootstrap.ts index fc05f86b92..701d822bc3 100644 --- a/packages/ngtools/webpack/src/transformers/replace_bootstrap.ts +++ b/packages/ngtools/webpack/src/transformers/replace_bootstrap.ts @@ -15,14 +15,30 @@ import { makeTransform } from './make_transform'; export function replaceBootstrap( shouldTransform: (fileName: string) => boolean, - getEntryModule: () => { path: string, className: string } | null, + getEntryModules: () => { path: string, className: string }[] | null, getTypeChecker: () => ts.TypeChecker, ): ts.TransformerFactory { + const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) { + const ops: TransformOperation[] = []; - const entryModule = getEntryModule(); + const entryModules = getEntryModules(); + + if (!shouldTransform(sourceFile.fileName) || !entryModules) { + return ops; + } + + return entryModules.reduce((ops, entryModule) => { + return ops.concat(standardTransformHelper(sourceFile, entryModule)); + }, ops); + }; + + const standardTransformHelper = function ( + sourceFile: ts.SourceFile, + entryModule: { path: string, className: string }) { + const ops: TransformOperation[] = []; if (!shouldTransform(sourceFile.fileName) || !entryModule) { return ops; diff --git a/packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts b/packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts index c56e6d8f74..1683665917 100644 --- a/packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts +++ b/packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts @@ -44,7 +44,7 @@ describe('@ngtools/webpack transformers', () => { const { program, compilerHost } = createTypescriptContext(input); const transformer = replaceBootstrap( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), () => program.getTypeChecker(), ); const result = transformTypescript(undefined, [transformer], program, compilerHost); @@ -85,7 +85,7 @@ describe('@ngtools/webpack transformers', () => { const { program, compilerHost } = createTypescriptContext(input); const transformer = replaceBootstrap( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), () => program.getTypeChecker(), ); const result = transformTypescript(undefined, [transformer], program, compilerHost); diff --git a/packages/ngtools/webpack/src/transformers/replace_server_bootstrap.ts b/packages/ngtools/webpack/src/transformers/replace_server_bootstrap.ts index 483080bf4a..41c7c06a60 100644 --- a/packages/ngtools/webpack/src/transformers/replace_server_bootstrap.ts +++ b/packages/ngtools/webpack/src/transformers/replace_server_bootstrap.ts @@ -14,19 +14,31 @@ import { makeTransform } from './make_transform'; export function replaceServerBootstrap( shouldTransform: (fileName: string) => boolean, - getEntryModule: () => { path: string, className: string } | null, + getEntryModules: () => { path: string, className: string }[] | null, getTypeChecker: () => ts.TypeChecker, ): ts.TransformerFactory { const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) { + const ops: TransformOperation[] = []; - const entryModule = getEntryModule(); + const entryModules = getEntryModules(); - if (!shouldTransform(sourceFile.fileName) || !entryModule) { + if (!shouldTransform(sourceFile.fileName) || !entryModules) { return ops; } + return entryModules.reduce((ops, entryModule) => { + return ops.concat(standardTransformHelper(sourceFile, entryModule)); + }, ops); + }; + + const standardTransformHelper = function ( + sourceFile: ts.SourceFile, + entryModule: { path: string, className: string }) { + + const ops: TransformOperation[] = []; + // Find all identifiers. const entryModuleIdentifiers = collectDeepNodes(sourceFile, ts.SyntaxKind.Identifier) diff --git a/packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts b/packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts index 1d7ebcd0b8..6ed17244e9 100644 --- a/packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts +++ b/packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts @@ -44,7 +44,7 @@ describe('@ngtools/webpack transformers', () => { const { program, compilerHost } = createTypescriptContext(input); const transformer = replaceServerBootstrap( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), () => program.getTypeChecker(), ); const result = transformTypescript(undefined, [transformer], program, compilerHost); @@ -91,7 +91,7 @@ describe('@ngtools/webpack transformers', () => { const { program, compilerHost } = createTypescriptContext(input); const transformer = replaceServerBootstrap( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), () => program.getTypeChecker(), ); const result = transformTypescript(undefined, [transformer], program, compilerHost); @@ -144,7 +144,7 @@ describe('@ngtools/webpack transformers', () => { const { program, compilerHost } = createTypescriptContext(input); const transformer = replaceServerBootstrap( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), () => program.getTypeChecker(), ); const result = transformTypescript(undefined, [transformer], program, compilerHost); @@ -185,7 +185,7 @@ describe('@ngtools/webpack transformers', () => { const { program, compilerHost } = createTypescriptContext(input); const transformer = replaceServerBootstrap( () => true, - () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]), () => program.getTypeChecker(), ); const result = transformTypescript(undefined, [transformer], program, compilerHost);