Skip to content

Commit

Permalink
feat(@ngtools/webpack): support loadchildren string syntax in Ivy
Browse files Browse the repository at this point in the history
  • Loading branch information
filipesilva authored and alexeagle committed Apr 13, 2019
1 parent b7dfdb8 commit 6d7d2eb
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 83 deletions.
31 changes: 15 additions & 16 deletions packages/ngtools/webpack/src/angular_compiler_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export class AngularCompilerPlugin {
private _resourceLoader?: WebpackResourceLoader;
private _discoverLazyRoutes = true;
private _importFactories = false;
private _useFactories = false;
// Contains `moduleImportPath#exportName` => `fullModulePath`.
private _lazyRoutes: LazyRouteMap = {};
private _tsConfigPath: string;
Expand Down Expand Up @@ -256,11 +257,8 @@ export class AngularCompilerPlugin {
}

// Determine if lazy route discovery via Compiler CLI private API should be attempted.
if (this._compilerOptions.enableIvy) {
// Never try to discover lazy routes with Ivy.
this._discoverLazyRoutes = false;
} else if (options.discoverLazyRoutes !== undefined) {
// The default is to discover routes, but it can be overriden.
// The default is to discover routes, but it can be overriden.
if (options.discoverLazyRoutes !== undefined) {
this._discoverLazyRoutes = options.discoverLazyRoutes;
}

Expand All @@ -280,7 +278,12 @@ export class AngularCompilerPlugin {
);
}

if (!this._compilerOptions.enableIvy && options.importFactories === true) {
if (!this._JitMode && !this._compilerOptions.enableIvy) {
// Only attempt to use factories when AOT and not Ivy.
this._useFactories = true;
}

if (this._useFactories && options.importFactories === true) {
// Only transform imports to use factories with View Engine.
this._importFactories = true;
}
Expand Down Expand Up @@ -488,18 +491,14 @@ export class AngularCompilerPlugin {
const lazyRouteTSFile = discoveredLazyRoutes[lazyRouteKey].replace(/\\/g, '/');
let modulePath: string, moduleKey: string;

if (this._JitMode ||
// When using Ivy and not using allowEmptyCodegenFiles, factories are not generated.
(this._compilerOptions.enableIvy && !this._compilerOptions.allowEmptyCodegenFiles)
) {
modulePath = lazyRouteTSFile;
moduleKey = `${lazyRouteModule}${moduleName ? '#' + moduleName : ''}`;
} else {
// NgFactories are only used with AOT on ngc (legacy) mode.
if (this._useFactories) {
modulePath = lazyRouteTSFile.replace(/(\.d)?\.tsx?$/, '');
modulePath += '.ngfactory.js';
const factoryModuleName = moduleName ? `#${moduleName}NgFactory` : '';
moduleKey = `${lazyRouteModule}.ngfactory${factoryModuleName}`;
} else {
modulePath = lazyRouteTSFile;
moduleKey = `${lazyRouteModule}${moduleName ? '#' + moduleName : ''}`;
}

modulePath = workaroundResolve(modulePath);
Expand Down Expand Up @@ -949,7 +948,7 @@ export class AngularCompilerPlugin {
this._normalizedLocale));
}

if (!this._JitMode) {
if (this._useFactories) {
// Replace bootstrap in browser AOT.
this._transformers.push(replaceBootstrap(
isAppPath,
Expand All @@ -960,7 +959,7 @@ export class AngularCompilerPlugin {
}
} else if (this._platform === PLATFORM.Server) {
this._transformers.push(exportLazyModuleMap(isMainPath, getLazyRoutes));
if (!this._JitMode) {
if (this._useFactories) {
this._transformers.push(
exportNgFactory(isMainPath, getEntryModule),
replaceServerBootstrap(isMainPath, getEntryModule, getTypeChecker));
Expand Down
8 changes: 1 addition & 7 deletions tests/legacy-cli/e2e/tests/basic/rebuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
import {writeFile, writeMultipleFiles} from '../../utils/fs';
import {wait} from '../../utils/utils';
import {request} from '../../utils/http';
import {getGlobalVariable} from '../../utils/env';

const validBundleRegEx = /: Compiled successfully./;

Expand All @@ -16,11 +15,6 @@ export default function() {
return Promise.resolve();
}

const argv = getGlobalVariable('argv');
const ivyProject = argv['ivy'];
const lazyImport = ivyProject ? `() => import('./lazy/lazy.module').then(m => m.LazyModule)`
: `'./lazy/lazy.module#LazyModule'`;

return execAndWaitForOutputToMatch('ng', ['serve'], validBundleRegEx)
// Add a lazy module.
.then(() => ng('generate', 'module', 'lazy', '--routing'))
Expand All @@ -47,7 +41,7 @@ export default function() {
FormsModule,
HttpClientModule,
RouterModule.forRoot([
{ path: 'lazy', loadChildren: ${lazyImport} }
{ path: 'lazy', loadChildren: './lazy/lazy.module#LazyModule' }
])
],
providers: [],
Expand Down
50 changes: 11 additions & 39 deletions tests/legacy-cli/e2e/tests/build/lazy-load-syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,6 @@ export default async function () {
});
`);

// Set factory shims to false.
await updateJsonFile('tsconfig.app.json', json => {
if (json['angularCompilerOptions'] === undefined) {
json['angularCompilerOptions'] = {};
}
json['angularCompilerOptions']['allowEmptyCodegenFiles'] = false;
});

// Convert the default config to use JIT and prod to just do AOT.
// This way we can use `ng e2e` to test JIT and `ng e2e --prod` to test AOT.
await updateJsonFile('angular.json', json => {
Expand All @@ -90,40 +82,20 @@ export default async function () {
buildTarget['configurations']['production'] = { aot: true };
});

// Test string import with factory shims.
await replaceLoadChildren(`'./lazy/lazy.module#LazyModule'`);
await replaceInFile('tsconfig.app.json', `"allowEmptyCodegenFiles": false`,
`"allowEmptyCodegenFiles": true`);
if (ivyProject) {
// Ivy should not support the string syntax.
await expectToFail(() => ng('e2e'));
await expectToFail(() => ng('e2e', '--prod'));
} else {
// View engine should support the string syntax.
await ng('e2e');
await ng('e2e', '--prod');
}

// Test string import without factory shims.
// Test string import.
// Both Ivy and View Engine should support it.
await replaceLoadChildren(`'./lazy/lazy.module#LazyModule'`);
await replaceInFile('tsconfig.app.json', `"allowEmptyCodegenFiles": true`,
`"allowEmptyCodegenFiles": false`);
if (ivyProject) {
// Ivy should not support the string syntax.
await expectToFail(() => ng('e2e'));
await expectToFail(() => ng('e2e', '--prod'));
} else {
// View engine should support the string syntax.
await ng('e2e');
await ng('e2e', '--prod');
}
await ng('e2e');
await ng('e2e', '--prod');

// Test `import()` style lazy load.
await updateJsonFile('angular.json', json => {
// Add the experimental flag to import factories in View Engine.
const buildTarget = json['projects'][projectName]['architect']['build'];
buildTarget['options']['experimentalImportFactories'] = true;
});
if (!ivyProject) {
await updateJsonFile('angular.json', json => {
// Add the experimental flag to import factories in View Engine.
const buildTarget = json['projects'][projectName]['architect']['build'];
buildTarget['options']['experimentalImportFactories'] = true;
});
}
// Both Ivy and View Engine should support it.
await replaceLoadChildren(`() => import('./lazy/lazy.module').then(m => m.LazyModule)`);
await ng('e2e');
Expand Down
26 changes: 5 additions & 21 deletions tests/legacy-cli/e2e/tests/misc/lazy-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,9 @@ import {readdirSync} from 'fs';

import {ng, silentNpm} from '../../utils/process';
import {appendToFile, writeFile, prependToFile, replaceInFile} from '../../utils/fs';
import {getGlobalVariable} from '../../utils/env';


export default function() {
const argv = getGlobalVariable('argv');
const ivyProject = argv['ivy'];
const lazyImport = ivyProject ? `() => import('src/app/lazy/lazy.module').then(m => m.LazyModule)`
: `'src/app/lazy/lazy.module#LazyModule'`;
const lazyImport1 = ivyProject ? `() => import('./lazy/lazy.module').then(m => m.LazyModule)`
: `'./lazy/lazy.module#LazyModule'`;
const lazyImport2 = ivyProject ? `() => import('./too/lazy/lazy.module').then(m => m.LazyModule)`
: `'./too/lazy/lazy.module#LazyModule'`;

let oldNumberOfFiles = 0;
return Promise.resolve()
.then(() => ng('build'))
Expand All @@ -25,9 +15,9 @@ export default function() {
import { RouterModule } from '@angular/router';
`))
.then(() => replaceInFile('src/app/app.module.ts', 'imports: [', `imports: [
RouterModule.forRoot([{ path: "lazy", loadChildren: ${lazyImport} }]),
RouterModule.forRoot([{ path: "lazy1", loadChildren: ${lazyImport1} }]),
RouterModule.forRoot([{ path: "lazy2", loadChildren: ${lazyImport2} }]),
RouterModule.forRoot([{ path: "lazy", loadChildren: 'src/app/lazy/lazy.module#LazyModule' }]),
RouterModule.forRoot([{ path: "lazy1", loadChildren: './lazy/lazy.module#LazyModule' }]),
RouterModule.forRoot([{ path: "lazy2", loadChildren: './too/lazy/lazy.module#LazyModule' }]),
`))
.then(() => ng('build', '--named-chunks'))
.then(() => readdirSync('dist/test-project'))
Expand All @@ -38,14 +28,8 @@ export default function() {
}
oldNumberOfFiles = currentNumberOfDistFiles;

// Named lazy route chunks are not available with Ivy.
if (!ivyProject) {
if (!distFiles.includes('lazy-lazy-module.js')) {
throw new Error('The lazy module chunk did not have a name.');
}
if (!distFiles.includes('too-lazy-lazy-module.js')) {
throw new Error('The lazy module chunk did not use a unique name.');
}
if (!distFiles.includes('too-lazy-lazy-module.js')) {
throw new Error('The lazy module chunk did not use a unique name.');
}
})
// verify System.import still works
Expand Down

0 comments on commit 6d7d2eb

Please sign in to comment.