Skip to content

Commit fb75650

Browse files
committed
fix(@ngtools/webpack): AngularCompilerPlugin support for multiple entryModules
1 parent 4774049 commit fb75650

File tree

3 files changed

+63
-30
lines changed

3 files changed

+63
-30
lines changed

packages/@ngtools/webpack/src/angular_compiler_plugin.ts

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export interface AngularCompilerPluginOptions {
5757
sourceMap?: boolean;
5858
tsConfigPath: string;
5959
basePath?: string;
60-
entryModule?: string;
60+
entryModule?: string | string[];
6161
mainPath?: string;
6262
skipCodeGeneration?: boolean;
6363
hostReplacementPaths?: { [path: string]: string };
@@ -95,7 +95,7 @@ export class AngularCompilerPlugin implements Tapable {
9595
// Contains `moduleImportPath#exportName` => `fullModulePath`.
9696
private _lazyRoutes: LazyRouteMap = Object.create(null);
9797
private _tsConfigPath: string;
98-
private _entryModule: string;
98+
private _entryModule: string[];
9999
private _mainPath: string | undefined;
100100
private _basePath: string;
101101
private _transformers: ts.TransformerFactory<ts.SourceFile>[] = [];
@@ -129,14 +129,17 @@ export class AngularCompilerPlugin implements Tapable {
129129

130130
get options() { return this._options; }
131131
get done() { return this._donePromise; }
132-
get entryModule() {
132+
get entryModule(): {path: string, className: string}[] {
133133
if (!this._entryModule) {
134134
return undefined;
135135
}
136-
const splitted = this._entryModule.split('#');
137-
const path = splitted[0];
138-
const className = splitted[1] || 'default';
139-
return { path, className };
136+
137+
return this._entryModule.map((entryModule) => {
138+
const splitted = entryModule.split('#');
139+
const path = splitted[0];
140+
const className = splitted[1] || 'default';
141+
return { path, className };
142+
});
140143
}
141144

142145
static isSupported() {
@@ -266,10 +269,16 @@ export class AngularCompilerPlugin implements Tapable {
266269
// Use entryModule if available in options, otherwise resolve it from mainPath after program
267270
// creation.
268271
if (this._options.entryModule) {
269-
this._entryModule = this._options.entryModule;
272+
this._entryModule = Array.isArray(this._options.entryModule) ?
273+
this._options.entryModule : [this._options.entryModule];
270274
} else if (this._compilerOptions.entryModule) {
271-
this._entryModule = path.resolve(this._basePath,
272-
this._compilerOptions.entryModule);
275+
if (Array.isArray(this._compilerOptions.entryModule)) {
276+
this._entryModule = (<string[]>this._compilerOptions.entryModule).map((entryModule) => {
277+
return path.resolve(this._basePath, entryModule);
278+
});
279+
} else {
280+
this._entryModule = [path.resolve(this._basePath, this._compilerOptions.entryModule)];
281+
}
273282
}
274283

275284
// Set platform.
@@ -355,14 +364,20 @@ export class AngularCompilerPlugin implements Tapable {
355364
private _getLazyRoutesFromNgtools() {
356365
try {
357366
time('AngularCompilerPlugin._getLazyRoutesFromNgtools');
358-
const result = __NGTOOLS_PRIVATE_API_2.listLazyRoutes({
359-
program: this._getTsProgram(),
360-
host: this._compilerHost,
361-
angularCompilerOptions: Object.assign({}, this._compilerOptions, {
362-
// genDir seems to still be needed in @angular\compiler-cli\src\compiler_host.js:226.
363-
genDir: ''
364-
}),
365-
entryModule: this._entryModule
367+
const result = {};
368+
if (!Array.isArray(this._entryModule)) {
369+
this._entryModule = [this._entryModule];
370+
}
371+
this._entryModule.forEach((entryModule) => {
372+
Object.assign(result, __NGTOOLS_PRIVATE_API_2.listLazyRoutes({
373+
program: this._getTsProgram(),
374+
host: this._compilerHost,
375+
angularCompilerOptions: Object.assign({}, this._compilerOptions, {
376+
// genDir seems to still be needed in @angular\compiler-cli\src\compiler_host.js:226.
377+
genDir: ''
378+
}),
379+
entryModule: entryModule
380+
}));
366381
});
367382
timeEnd('AngularCompilerPlugin._getLazyRoutesFromNgtools');
368383
return result;
@@ -636,7 +651,10 @@ export class AngularCompilerPlugin implements Tapable {
636651
const isAppPath = (fileName: string) =>
637652
!fileName.endsWith('.ngfactory.ts') && !fileName.endsWith('.ngstyle.ts');
638653
const isMainPath = (fileName: string) => fileName === this._mainPath;
639-
const getEntryModule = () => this.entryModule;
654+
const getEntryModule = (i: number) => () => this.entryModule[i];
655+
const entryModulesIdxs = Array.from(
656+
Array(this.entryModule ? this.entryModule.length : 0).keys()
657+
);
640658
const getLazyRoutes = () => this._lazyRoutes;
641659
const getTypeChecker = () => this._getTsProgram().getTypeChecker();
642660

@@ -653,18 +671,24 @@ export class AngularCompilerPlugin implements Tapable {
653671
// This transform must go before replaceBootstrap because it looks for the entry module
654672
// import, which will be replaced.
655673
if (this._compilerOptions.i18nInLocale) {
656-
this._transformers.push(registerLocaleData(isAppPath, getEntryModule,
657-
this._compilerOptions.i18nInLocale));
674+
entryModulesIdxs.forEach((idx) => {
675+
this._transformers.push(registerLocaleData(isAppPath, getEntryModule(idx),
676+
this._compilerOptions.i18nInLocale));
677+
});
658678
}
659679

660680
if (!this._JitMode) {
661681
// Replace bootstrap in browser AOT.
662-
this._transformers.push(replaceBootstrap(isAppPath, getEntryModule, getTypeChecker));
682+
entryModulesIdxs.forEach((idx) => {
683+
this._transformers.push(replaceBootstrap(isAppPath, getEntryModule(idx), getTypeChecker));
684+
});
663685
}
664686
} else if (this._platform === PLATFORM.Server) {
665687
this._transformers.push(exportLazyModuleMap(isMainPath, getLazyRoutes));
666688
if (!this._JitMode) {
667-
this._transformers.push(exportNgFactory(isMainPath, getEntryModule));
689+
entryModulesIdxs.forEach((idx) => {
690+
this._transformers.push(exportNgFactory(isMainPath, getEntryModule(idx)));
691+
});
668692
}
669693
}
670694
}

packages/@ngtools/webpack/src/entry_resolver.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export function resolveEntryModuleFromMain(mainPath: string,
133133
program: ts.Program) {
134134
const source = new TypeScriptFileRefactor(mainPath, host, program);
135135

136-
const bootstrap = source.findAstNodes(source.sourceFile, ts.SyntaxKind.CallExpression, true)
136+
const bootstraps = source.findAstNodes(source.sourceFile, ts.SyntaxKind.CallExpression, true)
137137
.map(node => node as ts.CallExpression)
138138
.filter(call => {
139139
const access = call.expression as ts.PropertyAccessExpression;
@@ -145,15 +145,23 @@ export function resolveEntryModuleFromMain(mainPath: string,
145145
.map(node => node.arguments[0] as ts.Identifier)
146146
.filter(node => node.kind == ts.SyntaxKind.Identifier);
147147

148-
if (bootstrap.length != 1) {
148+
if (bootstraps.length < 1) {
149149
throw new Error('Tried to find bootstrap code, but could not. Specify either '
150150
+ 'statically analyzable bootstrap code or pass in an entryModule '
151151
+ 'to the plugins options.');
152152
}
153-
const bootstrapSymbolName = bootstrap[0].text;
154-
const module = _symbolImportLookup(source, bootstrapSymbolName, host, program);
155-
if (module) {
156-
return `${module.replace(/\.ts$/, '')}#${bootstrapSymbolName}`;
153+
154+
const entryModules: string[] = [];
155+
bootstraps.forEach((bootstrap) => {
156+
const bootstrapSymbolName = bootstrap.text;
157+
const module = _symbolImportLookup(source, bootstrapSymbolName, host, program);
158+
if (module) {
159+
entryModules.push(`${module.replace(/\.ts$/, '')}#${bootstrapSymbolName}`);
160+
}
161+
});
162+
163+
if (entryModules.length > 0) {
164+
return entryModules;
157165
}
158166

159167
// shrug... something bad happened and we couldn't find the import statement.

packages/@ngtools/webpack/src/plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ export class AotPlugin implements Tapable {
278278
// still no _entryModule? => try to resolve from mainPath
279279
if (!this._entryModule && options.mainPath) {
280280
const mainPath = path.resolve(basePath, options.mainPath);
281-
this._entryModule = resolveEntryModuleFromMain(mainPath, this._compilerHost, this._program);
281+
this._entryModule = resolveEntryModuleFromMain(mainPath,
282+
this._compilerHost, this._program)[0];
282283
}
283284

284285
if (options.hasOwnProperty('i18nFile')) {

0 commit comments

Comments
 (0)