From fbf3ac247bbbf98c73b6aa0dfa6e20774359031b Mon Sep 17 00:00:00 2001 From: Payam Valadkhan Date: Tue, 5 Sep 2023 15:19:39 -0400 Subject: [PATCH] refactor(compiler-cli): move NgModule bootstrap definition to runtime in local compilation mode (#51767) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Today in local compilation mode the NgModule bootstrap definition is moved as it is into the runtime `ɵɵdefineNgModule`. This runtime was initially made for AoT full compilation mode and assumes that the bootstrap info is already flattened and resolved. This is not the case in local compilation where the bootstrap is the raw expression coming from the NgModule decorator and can be a nested array. To get around this problem we move the bootstrap along with other scope info (e.g., declarations, imports, exports) to the runtime`ɵɵsetNgModuleScope` to be further analyzed and flattened in runtime. PR Close #51767 --- .../test/ngtsc/local_compilation_spec.ts | 50 +++++++++++++++++++ .../src/render3/r3_module_compiler.ts | 26 ++++++---- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts b/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts index 8e2201a79b6..2a8084e797c 100644 --- a/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts +++ b/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts @@ -562,5 +562,55 @@ runInEachFileSystem(() => { expect(text).toContain('either inline it or move it to a separate file'); }); }); + + describe('ng module bootstrap def', () => { + it('should include the bootstrap definition in ɵɵsetNgModuleScope instead of ɵɵdefineNgModule', + () => { + env.write('test.ts', ` + import {NgModule} from '@angular/core'; + import {App} from './some-where'; + + @NgModule({ + declarations: [App], + bootstrap: [App], + }) + export class AppModule { + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + 'AppModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: AppModule });'); + expect(jsContents) + .toContain( + 'ɵɵsetNgModuleScope(AppModule, { declarations: [App], bootstrap: [App] }); })();'); + }); + + it('should include no bootstrap definition in ɵɵsetNgModuleScope if the NgModule has no bootstrap field', + () => { + env.write('test.ts', ` + import {NgModule} from '@angular/core'; + import {App} from './some-where'; + + @NgModule({ + declarations: [App], + }) + export class AppModule { + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + 'AppModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: AppModule });'); + expect(jsContents) + .toContain('ɵɵsetNgModuleScope(AppModule, { declarations: [App] }); })();'); + }); + }); }); }); diff --git a/packages/compiler/src/render3/r3_module_compiler.ts b/packages/compiler/src/render3/r3_module_compiler.ts index 9471c16efec..a1485cb30dd 100644 --- a/packages/compiler/src/render3/r3_module_compiler.ts +++ b/packages/compiler/src/render3/r3_module_compiler.ts @@ -204,15 +204,11 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3CompiledExpression const definitionMap = new DefinitionMap(); definitionMap.set('type', meta.type.value); - // Assign bootstrap definition - if (meta.kind === R3NgModuleMetadataKind.Global) { - if (meta.bootstrap.length > 0) { - definitionMap.set('bootstrap', refsToArray(meta.bootstrap, meta.containsForwardDecls)); - } - } else { - if (meta.bootstrapExpression) { - definitionMap.set('bootstrap', meta.bootstrapExpression); - } + // Assign bootstrap definition. In local compilation mode (i.e., for + // `R3NgModuleMetadataKind.LOCAL`) we assign the bootstrap field using the runtime + // `ɵɵsetNgModuleScope`. + if (meta.kind === R3NgModuleMetadataKind.Global && meta.bootstrap.length > 0) { + definitionMap.set('bootstrap', refsToArray(meta.bootstrap, meta.containsForwardDecls)); } if (meta.selectorScopeMode === R3SelectorScopeMode.Inline) { @@ -321,8 +317,12 @@ export function createNgModuleType(meta: R3NgModuleMetadata): o.ExpressionType { * symbols to become tree-shakeable. */ function generateSetNgModuleScopeCall(meta: R3NgModuleMetadata): o.Statement|null { - const scopeMap = new DefinitionMap< - {declarations: o.Expression, imports: o.Expression, exports: o.Expression}>(); + const scopeMap = new DefinitionMap<{ + declarations: o.Expression, + imports: o.Expression, + exports: o.Expression, + bootstrap: o.Expression + }>(); if (meta.kind === R3NgModuleMetadataKind.Global) { if (meta.declarations.length > 0) { @@ -354,6 +354,10 @@ function generateSetNgModuleScopeCall(meta: R3NgModuleMetadata): o.Statement|nul } } + if (meta.kind === R3NgModuleMetadataKind.Local && meta.bootstrapExpression) { + scopeMap.set('bootstrap', meta.bootstrapExpression); + } + if (Object.keys(scopeMap.values).length === 0) { return null; }