fix(core): adhere to bootstrap options for JIT compiled components (#35534)

When using `platformBrowserDynamic().bootstrapModule()`, it is possible
to set `defaultEncapsulation` and `preserveWhitespaces` as default
configuration to influence how components are compiled. When compiling
components in JIT with Ivy, these options were not taken into account.

This commit publishes the options to be globally available, so that the
lazy compilation of JIT components has access to the configured
bootstrap options. Note that this approach does not allow changing the
options once they have been set, as Ivy's compilation model does not
allow for multiple compilations to exist at the same time.

For applications that bootstrap multiple modules, it is now required
to provide the exact same bootstrap options. An error is logged if
incompatible bootstrap options are provided, in which case the updated
options will be ignored.

Fixes #35230
Resolved FW-1838

PR Close #35534
This commit is contained in:
JoostK
2020-02-18 19:55:55 +01:00
committed by Andrew Kushnir
parent c0143cb2ab
commit e342ffd855
7 changed files with 329 additions and 8 deletions

View File

@ -6,6 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import './util/ng_jit_mode';
import {Observable, Observer, Subscription, merge} from 'rxjs';
import {share} from 'rxjs/operators';
@ -29,6 +31,7 @@ import {isComponentResourceResolutionQueueEmpty, resolveComponentResources} from
import {assertNgModuleType} from './render3/assert';
import {ComponentFactory as R3ComponentFactory} from './render3/component_ref';
import {setLocaleId} from './render3/i18n';
import {setJitOptions} from './render3/jit/jit_options';
import {NgModuleFactory as R3NgModuleFactory} from './render3/ng_module_ref';
import {publishDefaultGlobalUtils as _publishDefaultGlobalUtils} from './render3/util/global_utils';
import {Testability, TestabilityRegistry} from './testability/testability';
@ -56,13 +59,27 @@ export function compileNgModuleFactory__POST_R3__<M>(
injector: Injector, options: CompilerOptions,
moduleType: Type<M>): Promise<NgModuleFactory<M>> {
ngDevMode && assertNgModuleType(moduleType);
const compilerOptions = injector.get(COMPILER_OPTIONS, []).concat(options);
if (typeof ngJitMode === 'undefined' || ngJitMode) {
// Configure the compiler to use the provided options. This call may fail when multiple modules
// are bootstrapped with incompatible options, as a component can only be compiled according to
// a single set of options.
setJitOptions({
defaultEncapsulation:
_lastDefined(compilerOptions.map(options => options.defaultEncapsulation)),
preserveWhitespaces:
_lastDefined(compilerOptions.map(options => options.preserveWhitespaces)),
});
}
const moduleFactory = new R3NgModuleFactory(moduleType);
if (isComponentResourceResolutionQueueEmpty()) {
return Promise.resolve(moduleFactory);
}
const compilerOptions = injector.get(COMPILER_OPTIONS, []).concat(options);
const compilerProviders = _mergeArrays(compilerOptions.map(o => o.providers !));
// In case there are no compiler providers, we just return the module factory as
@ -748,6 +765,15 @@ function remove<T>(list: T[], el: T): void {
}
}
function _lastDefined<T>(args: T[]): T|undefined {
for (let i = args.length - 1; i >= 0; i--) {
if (args[i] !== undefined) {
return args[i];
}
}
return undefined;
}
function _mergeArrays(parts: any[][]): any[] {
const result: any[] = [];
parts.forEach((part) => part && result.push(...part));