Skip to content

Commit e127c52

Browse files
committed
feat(@ngtools/webpack): compile a module map for commonjs bundles
1 parent 76530d0 commit e127c52

File tree

3 files changed

+58
-12
lines changed

3 files changed

+58
-12
lines changed

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

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -435,15 +435,12 @@ function _diagnoseDeps(reasons: ModuleReason[], plugin: AotPlugin, checked: Set<
435435
}
436436

437437

438-
export function _replaceExport(plugin: AotPlugin, refactor: TypeScriptFileRefactor) {
439-
if (!plugin.replaceExport) {
440-
return;
441-
}
442-
438+
export function _getModuleExports(plugin: AotPlugin,
439+
refactor: TypeScriptFileRefactor): ts.Identifier[] {
443440
const exports = refactor
444441
.findAstNodes(refactor.sourceFile, ts.SyntaxKind.ExportDeclaration, true);
445442

446-
exports
443+
return exports
447444
.filter(node => {
448445

449446
const identifiers = refactor.findAstNodes(node, ts.SyntaxKind.Identifier, false);
@@ -452,7 +449,15 @@ export function _replaceExport(plugin: AotPlugin, refactor: TypeScriptFileRefact
452449
.filter(node => node.getText() === plugin.entryModule.className);
453450

454451
return identifiers.length > 0;
455-
})
452+
}) as ts.Identifier[];
453+
}
454+
455+
456+
export function _replaceExport(plugin: AotPlugin, refactor: TypeScriptFileRefactor) {
457+
if (!plugin.replaceExport) {
458+
return;
459+
}
460+
_getModuleExports(plugin, refactor)
456461
.forEach(node => {
457462
const factoryPath = _getNgFactoryPath(plugin, refactor);
458463
const factoryClassName = plugin.entryModule.className + 'NgFactory';
@@ -462,6 +467,40 @@ export function _replaceExport(plugin: AotPlugin, refactor: TypeScriptFileRefact
462467
}
463468

464469

470+
export function _exportModuleMap(plugin: AotPlugin, refactor: TypeScriptFileRefactor) {
471+
if (!plugin.replaceExport) {
472+
return;
473+
}
474+
475+
const dirName = path.normalize(path.dirname(refactor.fileName));
476+
const classNameAppend = plugin.skipCodeGeneration ? '' : 'NgFactory';
477+
const modulePathAppend = plugin.skipCodeGeneration ? '' : '.ngfactory';
478+
479+
_getModuleExports(plugin, refactor)
480+
.forEach(node => {
481+
const jsonContent: string = Object.keys(plugin.discoveredLazyRoutes)
482+
.map((routeKey, index: number) => {
483+
let [lazyRouteKey, moduleName] = routeKey.split('#');
484+
485+
if (!lazyRouteKey || !moduleName) {
486+
throw new Error(`${routeKey} was not a proper loadChildren string`);
487+
}
488+
489+
moduleName += classNameAppend;
490+
lazyRouteKey += modulePathAppend;
491+
492+
const modulePath = plugin.lazyRoutes[lazyRouteKey];
493+
const relativePath = path.relative(dirName, modulePath).replace(/\\/g, '/');
494+
refactor.prependBefore(node, `import * as __lazy_${index}__ from './${relativePath}'`);
495+
return `"${routeKey}": __lazy_${index}__.${moduleName}`;
496+
})
497+
.join();
498+
499+
refactor.appendAfter(node, `export const moduleMap = {${jsonContent}};`);
500+
});
501+
}
502+
503+
465504
// Super simple TS transpiler loader for testing / isolated usage. does not type check!
466505
export function ngcLoader(this: LoaderContext & { _compilation: any }, source: string | null) {
467506
const cb = this.async();
@@ -492,11 +531,13 @@ export function ngcLoader(this: LoaderContext & { _compilation: any }, source: s
492531
return Promise.resolve()
493532
.then(() => _removeDecorators(refactor))
494533
.then(() => _refactorBootstrap(plugin, refactor))
495-
.then(() => _replaceExport(plugin, refactor));
534+
.then(() => _replaceExport(plugin, refactor))
535+
.then(() => _exportModuleMap(plugin, refactor));
496536
} else {
497537
return Promise.resolve()
498538
.then(() => _replaceResources(refactor))
499-
.then(() => _removeModuleId(refactor));
539+
.then(() => _removeModuleId(refactor))
540+
.then(() => _exportModuleMap(plugin, refactor));
500541
}
501542
})
502543
.then(() => {

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export class AotPlugin implements Tapable {
5050
private _rootFilePath: string[];
5151
private _compilerHost: WebpackCompilerHost;
5252
private _resourceLoader: WebpackResourceLoader;
53+
private _discoveredLazyRoutes: LazyRouteMap;
5354
private _lazyRoutes: LazyRouteMap = Object.create(null);
5455
private _tsConfigPath: string;
5556
private _entryModule: string;
@@ -97,6 +98,8 @@ export class AotPlugin implements Tapable {
9798
get i18nFormat() { return this._i18nFormat; }
9899
get locale() { return this._locale; }
99100
get firstRun() { return this._firstRun; }
101+
get lazyRoutes() { return this._lazyRoutes; }
102+
get discoveredLazyRoutes() { return this._discoveredLazyRoutes; }
100103

101104
private _setupOptions(options: AotPluginOptions) {
102105
// Fill in the missing options.
@@ -516,14 +519,14 @@ export class AotPlugin implements Tapable {
516519
.then(() => {
517520
// We need to run the `listLazyRoutes` the first time because it also navigates libraries
518521
// and other things that we might miss using the findLazyRoutesInAst.
519-
let discoveredLazyRoutes: LazyRouteMap = this.firstRun
522+
this._discoveredLazyRoutes = this.firstRun
520523
? this._getLazyRoutesFromNgtools()
521524
: this._findLazyRoutesInAst();
522525

523526
// Process the lazy routes discovered.
524-
Object.keys(discoveredLazyRoutes)
527+
Object.keys(this.discoveredLazyRoutes)
525528
.forEach(k => {
526-
const lazyRoute = discoveredLazyRoutes[k];
529+
const lazyRoute = this.discoveredLazyRoutes[k];
527530
k = k.split('#')[0];
528531
if (lazyRoute === null) {
529532
return;

tests/e2e/tests/packages/webpack/server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export default function(skipCleaning: () => void) {
2424
'--config', 'webpack.commonjs.config.js'))
2525
.then(() => expectFileToMatch('dist/app.main.js',
2626
/__webpack_exports__, "AppModuleNgFactory"/))
27+
.then(() => expectFileToMatch('dist/app.main.js',
28+
/var moduleMap = { ".\/lazy\.module#LazyModule": /))
2729
.then(() => exec(normalize('node'), 'index.js'))
2830
.then(() => expectFileToMatch('dist/index.html',
2931
new RegExp('<a ng-reflect-router-link="lazy" href="/lazy">lazy</a>')))

0 commit comments

Comments
 (0)