Skip to content

Commit d439496

Browse files
artongeRush
artonge
authored andcommitted
feat(@ngtools/webpack): add support for multiple entry modules
When the webpack config is building two different apps from two entry points, one of the apps will not work because metadatas are not generated. The commit add a new property (entryModules) in AngularCompilerPluginOptions that permit to regsiter multiple entry modules
1 parent 8440a17 commit d439496

10 files changed

+105
-46
lines changed

packages/ngtools/webpack/src/angular_compiler_plugin.ts

+30-21
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export interface AngularCompilerPluginOptions {
8989
tsConfigPath: string;
9090
basePath?: string;
9191
entryModule?: string;
92+
entryModules?: string[];
9293
mainPath?: string;
9394
skipCodeGeneration?: boolean;
9495
hostReplacementPaths?: { [path: string]: string } | ((path: string) => string);
@@ -138,6 +139,7 @@ export class AngularCompilerPlugin {
138139
private _lazyRoutes: LazyRouteMap = {};
139140
private _tsConfigPath: string;
140141
private _entryModule: string | null;
142+
private _entryModules: string[] | null;
141143
private _mainPath: string | undefined;
142144
private _basePath: string;
143145
private _transformers: ts.TransformerFactory<ts.SourceFile>[] = [];
@@ -170,15 +172,18 @@ export class AngularCompilerPlugin {
170172

171173
get options() { return this._options; }
172174
get done() { return this._donePromise; }
173-
get entryModule() {
174-
if (!this._entryModule) {
175+
get entryModules() {
176+
if (!this._entryModules) {
175177
return null;
176178
}
177-
const splitted = this._entryModule.split(/(#[a-zA-Z_]([\w]+))$/);
178-
const path = splitted[0];
179-
const className = !!splitted[1] ? splitted[1].substring(1) : 'default';
180179

181-
return { path, className };
180+
return this._entryModules.map((entryModule) => {
181+
const splitted = entryModule.split(/(#[a-zA-Z_]([\w]+))$/);
182+
const path = splitted[0];
183+
const className = !!splitted[1] ? splitted[1].substring(1) : 'default';
184+
185+
return { path, className };
186+
});
182187
}
183188

184189
get typeChecker(): ts.TypeChecker | null {
@@ -301,13 +306,13 @@ export class AngularCompilerPlugin {
301306
this._contextElementDependencyConstructor = options.contextElementDependencyConstructor
302307
|| require('webpack/lib/dependencies/ContextElementDependency');
303308

304-
// Use entryModule if available in options, otherwise resolve it from mainPath after program
309+
// Use entryModules if available in options, otherwise resolve it from mainPath after program
305310
// creation.
306-
if (this._options.entryModule) {
307-
this._entryModule = this._options.entryModule;
311+
if (this._options.entryModules) {
312+
this._entryModules = this._options.entryModules || [this._options.entryModule];
308313
} else if (this._compilerOptions.entryModule) {
309-
this._entryModule = path.resolve(this._basePath,
310-
this._compilerOptions.entryModule as string); // temporary cast for type issue
314+
this._entryModules = [path.resolve(this._basePath,
315+
this._compilerOptions.entryModule as string)]; // temporary cast for type issue
311316
}
312317

313318
// Set platform.
@@ -411,7 +416,7 @@ export class AngularCompilerPlugin {
411416
this._entryModule = resolveEntryModuleFromMain(
412417
this._mainPath, this._compilerHost, this._getTsProgram() as ts.Program);
413418

414-
if (!this.entryModule && !this._compilerOptions.enableIvy) {
419+
if (!this.entryModules && !this._compilerOptions.enableIvy) {
415420
this._warnings.push('Lazy routes discovery is not enabled. '
416421
+ 'Because there is neither an entryModule nor a '
417422
+ 'statically analyzable bootstrap code in the main file.',
@@ -442,7 +447,7 @@ export class AngularCompilerPlugin {
442447
let ngProgram: Program;
443448

444449
if (this._JitMode) {
445-
if (!this.entryModule) {
450+
if (!this.entryModules) {
446451
return {};
447452
}
448453

@@ -454,7 +459,8 @@ export class AngularCompilerPlugin {
454459
});
455460
timeEnd('AngularCompilerPlugin._listLazyRoutesFromProgram.createProgram');
456461

457-
entryRoute = this.entryModule.path + '#' + this.entryModule.className;
462+
const entryModule = this.entryModules[0];
463+
entryRoute = entryModule.path + '#' + entryModule.className;
458464
} else {
459465
ngProgram = this._program as Program;
460466
}
@@ -851,9 +857,12 @@ export class AngularCompilerPlugin {
851857
const isMainPath = (fileName: string) => fileName === (
852858
this._mainPath ? workaroundResolve(this._mainPath) : this._mainPath
853859
);
854-
const getEntryModule = () => this.entryModule
855-
? { path: workaroundResolve(this.entryModule.path), className: this.entryModule.className }
856-
: this.entryModule;
860+
// TODO: fix fn usage
861+
const getEntryModules = () => this.entryModules
862+
? this.entryModules.map((entryModule) => {
863+
return { path: workaroundResolve(entryModule.path), className: entryModule.className };
864+
})
865+
: this.entryModules;
857866
const getLazyRoutes = () => this._lazyRoutes;
858867
const getTypeChecker = () => (this._getTsProgram() as ts.Program).getTypeChecker();
859868

@@ -873,15 +882,15 @@ export class AngularCompilerPlugin {
873882
// This transform must go before replaceBootstrap because it looks for the entry module
874883
// import, which will be replaced.
875884
if (this._normalizedLocale) {
876-
this._transformers.push(registerLocaleData(isAppPath, getEntryModule,
885+
this._transformers.push(registerLocaleData(isAppPath, getEntryModules,
877886
this._normalizedLocale));
878887
}
879888

880889
if (!this._JitMode) {
881890
// Replace bootstrap in browser AOT.
882891
this._transformers.push(replaceBootstrap(
883892
isAppPath,
884-
getEntryModule,
893+
getEntryModules,
885894
getTypeChecker,
886895
!!this._compilerOptions.enableIvy,
887896
));
@@ -890,8 +899,8 @@ export class AngularCompilerPlugin {
890899
this._transformers.push(exportLazyModuleMap(isMainPath, getLazyRoutes));
891900
if (!this._JitMode) {
892901
this._transformers.push(
893-
exportNgFactory(isMainPath, getEntryModule),
894-
replaceServerBootstrap(isMainPath, getEntryModule, getTypeChecker));
902+
exportNgFactory(isMainPath, getEntryModules),
903+
replaceServerBootstrap(isMainPath, getEntryModules, getTypeChecker));
895904
}
896905
}
897906
}

packages/ngtools/webpack/src/transformers/export_ngfactory.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,29 @@ import { makeTransform } from './make_transform';
1313

1414
export function exportNgFactory(
1515
shouldTransform: (fileName: string) => boolean,
16-
getEntryModule: () => { path: string, className: string } | null,
16+
getEntryModules: () => { path: string, className: string }[] | null,
1717
): ts.TransformerFactory<ts.SourceFile> {
1818

1919
const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) {
20+
2021
const ops: TransformOperation[] = [];
2122

22-
const entryModule = getEntryModule();
23+
const entryModules = getEntryModules();
2324

24-
if (!shouldTransform(sourceFile.fileName) || !entryModule) {
25+
if (!shouldTransform(sourceFile.fileName) || !entryModules) {
2526
return ops;
2627
}
2728

29+
return entryModules.reduce((ops, entryModule) => {
30+
return ops.concat(standardTransformHelper(sourceFile, entryModule));
31+
}, ops);
32+
};
33+
34+
const standardTransformHelper = function (
35+
sourceFile: ts.SourceFile,
36+
entryModule: { path: string, className: string }) {
37+
const ops: TransformOperation[] = [];
38+
2839
// Find all identifiers using the entry module class name.
2940
const entryModuleIdentifiers = collectDeepNodes<ts.Identifier>(sourceFile,
3041
ts.SyntaxKind.Identifier)

packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('@ngtools/webpack transformers', () => {
2222

2323
const transformer = exportNgFactory(
2424
() => true,
25-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
25+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
2626
);
2727
const result = transformTypescript(input, [transformer]);
2828

@@ -40,7 +40,7 @@ describe('@ngtools/webpack transformers', () => {
4040

4141
const transformer = exportNgFactory(
4242
() => true,
43-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
43+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
4444
);
4545
const result = transformTypescript(input, [transformer]);
4646

@@ -54,7 +54,7 @@ describe('@ngtools/webpack transformers', () => {
5454

5555
const transformer = exportNgFactory(
5656
() => false,
57-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
57+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
5858
);
5959
const result = transformTypescript(input, [transformer]);
6060

packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@ describe('@ngtools/webpack transformers', () => {
6767
const { program, compilerHost } = createTypescriptContext(input);
6868

6969
const shouldTransform = () => true;
70-
const getEntryModule = () =>
71-
({ path: '/project/src/app/app.module', className: 'AppModule' });
70+
const getEntryModules = () =>
71+
([{ path: '/project/src/app/app.module', className: 'AppModule' }]);
7272
const getTypeChecker = () => program.getTypeChecker();
7373

7474

7575
const transformers = [
76-
replaceBootstrap(shouldTransform, getEntryModule, getTypeChecker),
77-
exportNgFactory(shouldTransform, getEntryModule),
76+
replaceBootstrap(shouldTransform, getEntryModules, getTypeChecker),
77+
exportNgFactory(shouldTransform, getEntryModules),
7878
exportLazyModuleMap(shouldTransform,
7979
() => ({
8080
'./lazy/lazy.module.ngfactory#LazyModuleNgFactory':

packages/ngtools/webpack/src/transformers/register_locale_data.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,30 @@ import { makeTransform } from './make_transform';
1414

1515
export function registerLocaleData(
1616
shouldTransform: (fileName: string) => boolean,
17-
getEntryModule: () => { path: string, className: string } | null,
17+
getEntryModules: () => { path: string, className: string }[] | null,
1818
locale: string,
1919
): ts.TransformerFactory<ts.SourceFile> {
2020

2121
const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) {
22+
2223
const ops: TransformOperation[] = [];
2324

24-
const entryModule = getEntryModule();
25+
const entryModules = getEntryModules();
2526

26-
if (!shouldTransform(sourceFile.fileName) || !entryModule || !locale) {
27+
if (!shouldTransform(sourceFile.fileName) || !entryModules || !locale) {
2728
return ops;
2829
}
2930

31+
return entryModules.reduce((ops, entryModule) => {
32+
return ops.concat(standardTransformHelper(sourceFile, entryModule));
33+
}, ops);
34+
};
35+
36+
const standardTransformHelper = function (
37+
sourceFile: ts.SourceFile,
38+
entryModule: { path: string, className: string }) {
39+
const ops: TransformOperation[] = [];
40+
3041
// Find all identifiers using the entry module class name.
3142
const entryModuleIdentifiers = collectDeepNodes<ts.Identifier>(sourceFile,
3243
ts.SyntaxKind.Identifier)

packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('@ngtools/webpack transformers', () => {
4545

4646
const transformer = registerLocaleData(
4747
() => true,
48-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
48+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
4949
'fr',
5050
);
5151
const result = transformTypescript(input, [transformer]);

packages/ngtools/webpack/src/transformers/replace_bootstrap.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,31 @@ import { makeTransform } from './make_transform';
1515

1616
export function replaceBootstrap(
1717
shouldTransform: (fileName: string) => boolean,
18-
getEntryModule: () => { path: string, className: string } | null,
18+
getEntryModules: () => { path: string, className: string }[] | null,
1919
getTypeChecker: () => ts.TypeChecker,
2020
enableIvy?: boolean,
2121
): ts.TransformerFactory<ts.SourceFile> {
2222

23+
2324
const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) {
25+
2426
const ops: TransformOperation[] = [];
2527

26-
const entryModule = getEntryModule();
28+
const entryModules = getEntryModules();
29+
30+
if (!shouldTransform(sourceFile.fileName) || !entryModules) {
31+
return ops;
32+
}
33+
34+
return entryModules.reduce((ops, entryModule) => {
35+
return ops.concat(standardTransformHelper(sourceFile, entryModule));
36+
}, ops);
37+
};
38+
39+
const standardTransformHelper = function (
40+
sourceFile: ts.SourceFile,
41+
entryModule: { path: string, className: string }) {
42+
const ops: TransformOperation[] = [];
2743

2844
if (!shouldTransform(sourceFile.fileName) || !entryModule) {
2945
return ops;

packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe('@ngtools/webpack transformers', () => {
4444
const { program, compilerHost } = createTypescriptContext(input);
4545
const transformer = replaceBootstrap(
4646
() => true,
47-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
47+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
4848
() => program.getTypeChecker(),
4949
);
5050
const result = transformTypescript(undefined, [transformer], program, compilerHost);
@@ -127,7 +127,7 @@ describe('@ngtools/webpack transformers', () => {
127127
const { program, compilerHost } = createTypescriptContext(input);
128128
const transformer = replaceBootstrap(
129129
() => true,
130-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
130+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
131131
() => program.getTypeChecker(),
132132
);
133133
const result = transformTypescript(undefined, [transformer], program, compilerHost);

packages/ngtools/webpack/src/transformers/replace_server_bootstrap.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,31 @@ import { makeTransform } from './make_transform';
1414

1515
export function replaceServerBootstrap(
1616
shouldTransform: (fileName: string) => boolean,
17-
getEntryModule: () => { path: string, className: string } | null,
17+
getEntryModules: () => { path: string, className: string }[] | null,
1818
getTypeChecker: () => ts.TypeChecker,
1919
): ts.TransformerFactory<ts.SourceFile> {
2020

2121
const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) {
22+
2223
const ops: TransformOperation[] = [];
2324

24-
const entryModule = getEntryModule();
25+
const entryModules = getEntryModules();
2526

26-
if (!shouldTransform(sourceFile.fileName) || !entryModule) {
27+
if (!shouldTransform(sourceFile.fileName) || !entryModules) {
2728
return ops;
2829
}
2930

31+
return entryModules.reduce((ops, entryModule) => {
32+
return ops.concat(standardTransformHelper(sourceFile, entryModule));
33+
}, ops);
34+
};
35+
36+
const standardTransformHelper = function (
37+
sourceFile: ts.SourceFile,
38+
entryModule: { path: string, className: string }) {
39+
40+
const ops: TransformOperation[] = [];
41+
3042
// Find all identifiers.
3143
const entryModuleIdentifiers = collectDeepNodes<ts.Identifier>(sourceFile,
3244
ts.SyntaxKind.Identifier)

packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('@ngtools/webpack transformers', () => {
4545
const { program, compilerHost } = createTypescriptContext(input);
4646
const transformer = replaceServerBootstrap(
4747
() => true,
48-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
48+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
4949
() => program.getTypeChecker(),
5050
);
5151
const result = transformTypescript(undefined, [transformer], program, compilerHost);
@@ -92,7 +92,7 @@ describe('@ngtools/webpack transformers', () => {
9292
const { program, compilerHost } = createTypescriptContext(input);
9393
const transformer = replaceServerBootstrap(
9494
() => true,
95-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
95+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
9696
() => program.getTypeChecker(),
9797
);
9898
const result = transformTypescript(undefined, [transformer], program, compilerHost);
@@ -145,7 +145,7 @@ describe('@ngtools/webpack transformers', () => {
145145
const { program, compilerHost } = createTypescriptContext(input);
146146
const transformer = replaceServerBootstrap(
147147
() => true,
148-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
148+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
149149
() => program.getTypeChecker(),
150150
);
151151
const result = transformTypescript(undefined, [transformer], program, compilerHost);
@@ -186,7 +186,7 @@ describe('@ngtools/webpack transformers', () => {
186186
const { program, compilerHost } = createTypescriptContext(input);
187187
const transformer = replaceServerBootstrap(
188188
() => true,
189-
() => ({ path: '/project/src/app/app.module', className: 'AppModule' }),
189+
() => ([{ path: '/project/src/app/app.module', className: 'AppModule' }]),
190190
() => program.getTypeChecker(),
191191
);
192192
const result = transformTypescript(undefined, [transformer], program, compilerHost);

0 commit comments

Comments
 (0)