From 6d7d2ebd3cf8575cd8f2ff27bc9f21f21642f9f1 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Tue, 9 Apr 2019 13:28:46 +0100 Subject: [PATCH] feat(@ngtools/webpack): support loadchildren string syntax in Ivy --- .../webpack/src/angular_compiler_plugin.ts | 31 ++++++------ tests/legacy-cli/e2e/tests/basic/rebuild.ts | 8 +-- .../e2e/tests/build/lazy-load-syntax.ts | 50 ++++--------------- .../legacy-cli/e2e/tests/misc/lazy-module.ts | 26 ++-------- 4 files changed, 32 insertions(+), 83 deletions(-) diff --git a/packages/ngtools/webpack/src/angular_compiler_plugin.ts b/packages/ngtools/webpack/src/angular_compiler_plugin.ts index 104e09a77783..636c6ceecb7d 100644 --- a/packages/ngtools/webpack/src/angular_compiler_plugin.ts +++ b/packages/ngtools/webpack/src/angular_compiler_plugin.ts @@ -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; @@ -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; } @@ -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; } @@ -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); @@ -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, @@ -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)); diff --git a/tests/legacy-cli/e2e/tests/basic/rebuild.ts b/tests/legacy-cli/e2e/tests/basic/rebuild.ts index 4c1dccf36735..085afcf30454 100644 --- a/tests/legacy-cli/e2e/tests/basic/rebuild.ts +++ b/tests/legacy-cli/e2e/tests/basic/rebuild.ts @@ -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./; @@ -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')) @@ -47,7 +41,7 @@ export default function() { FormsModule, HttpClientModule, RouterModule.forRoot([ - { path: 'lazy', loadChildren: ${lazyImport} } + { path: 'lazy', loadChildren: './lazy/lazy.module#LazyModule' } ]) ], providers: [], diff --git a/tests/legacy-cli/e2e/tests/build/lazy-load-syntax.ts b/tests/legacy-cli/e2e/tests/build/lazy-load-syntax.ts index f453dfad9cf9..1d23418caae0 100644 --- a/tests/legacy-cli/e2e/tests/build/lazy-load-syntax.ts +++ b/tests/legacy-cli/e2e/tests/build/lazy-load-syntax.ts @@ -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 => { @@ -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'); diff --git a/tests/legacy-cli/e2e/tests/misc/lazy-module.ts b/tests/legacy-cli/e2e/tests/misc/lazy-module.ts index 65183f7d6746..076f03fdc381 100644 --- a/tests/legacy-cli/e2e/tests/misc/lazy-module.ts +++ b/tests/legacy-cli/e2e/tests/misc/lazy-module.ts @@ -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')) @@ -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')) @@ -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