Skip to content
This repository was archived by the owner on Apr 4, 2025. It is now read-only.

feat(@ngtools/webpack): add support for multiple entry modules #875

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 32 additions & 23 deletions packages/ngtools/webpack/src/angular_compiler_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export interface AngularCompilerPluginOptions {
tsConfigPath: string;
basePath?: string;
entryModule?: string;
entryModules?: string[];
mainPath?: string;
skipCodeGeneration?: boolean;
hostReplacementPaths?: { [path: string]: string };
Expand Down Expand Up @@ -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<ts.SourceFile>[] = [];
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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');
}
});
Expand All @@ -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');

Expand Down Expand Up @@ -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();

Expand All @@ -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));
}
}
}
Expand All @@ -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.
Expand Down
17 changes: 14 additions & 3 deletions packages/ngtools/webpack/src/transformers/export_ngfactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ts.SourceFile> {

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<ts.Identifier>(sourceFile,
ts.SyntaxKind.Identifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand All @@ -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]);

Expand All @@ -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]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ts.SourceFile> {

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<ts.Identifier>(sourceFile,
ts.SyntaxKind.Identifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
20 changes: 18 additions & 2 deletions packages/ngtools/webpack/src/transformers/replace_bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ts.SourceFile> {


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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ts.SourceFile> {

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<ts.Identifier>(sourceFile,
ts.SyntaxKind.Identifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down