fix(ivy): platform module bootstrap does not resolve resources (#29083)

Currently with ViewEngine, if someone runs the platform's
`bootstrapModule` method in order to boostrap a module in
JIT mode, external component resources are properly resolved
*automatically*.

Currently with Ivy, the developer would need to manually call
`resolveComponentResources` in order to asynchronously fetch
the determined external component resources. In order to make
this backwards compatible with ViewEngine, and also since
platforms can already specify a `ResourceLoader` compiler
provider, we need to automatically resolve all external
component resources on module bootstrap.

--

Since the `ResourceLoader` is part of the `@angular/compiler`,
because ViewEngine performed the factory creation in the compiler,
we can't access the `ResourceLoader` token from within core.

In order to workaround this without introducing a breaking change,
we just proxy the `ResourceLoader` token to `core` through the
compiler facade. In the future, we should be able to move the
`ResourceLoader` to core when ViewEngine code no longer exists in
the `@angular/compiler`.

PR Close #29083
This commit is contained in:
Paul Gschwendtner
2019-03-03 18:19:27 +01:00
committed by Kara Erickson
parent 7315a68ac6
commit 6085f335e8
8 changed files with 91 additions and 7 deletions

View File

@ -11,15 +11,17 @@ import {share} from 'rxjs/operators';
import {ApplicationInitStatus} from './application_init';
import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
import {getCompilerFacade} from './compiler/compiler_facade';
import {Console} from './console';
import {Injectable, InjectionToken, Injector, StaticProvider} from './di';
import {ErrorHandler} from './error_handler';
import {Type} from './interface/type';
import {CompilerFactory, CompilerOptions} from './linker/compiler';
import {COMPILER_OPTIONS, CompilerFactory, CompilerOptions} from './linker/compiler';
import {ComponentFactory, ComponentRef} from './linker/component_factory';
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from './linker/component_factory_resolver';
import {InternalNgModuleRef, NgModuleFactory, NgModuleRef} from './linker/ng_module_factory';
import {InternalViewRef, ViewRef} from './linker/view_ref';
import {isComponentResourceResolutionQueueEmpty, resolveComponentResources} from './metadata/resource_loading';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile';
import {assertNgModuleType} from './render3/assert';
import {ComponentFactory as R3ComponentFactory} from './render3/component_ref';
@ -49,7 +51,30 @@ export function compileNgModuleFactory__POST_R3__<M>(
injector: Injector, options: CompilerOptions,
moduleType: Type<M>): Promise<NgModuleFactory<M>> {
ngDevMode && assertNgModuleType(moduleType);
return Promise.resolve(new R3NgModuleFactory(moduleType));
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
// there won't be any resource loader. This can happen with Ivy, because AOT compiled
// modules can be still passed through "bootstrapModule". In that case we shouldn't
// unnecessarily require the JIT compiler.
if (compilerProviders.length === 0) {
return Promise.resolve(moduleFactory);
}
const compiler = getCompilerFacade();
const compilerInjector = Injector.create({providers: compilerProviders});
const resourceLoader = compilerInjector.get(compiler.ResourceLoader);
// The resource loader can also return a string while the "resolveComponentResources"
// always expects a promise. Therefore we need to wrap the returned value in a promise.
return resolveComponentResources(url => Promise.resolve(resourceLoader.get(url)))
.then(() => moduleFactory);
}
let isBoundToModule: <C>(cf: ComponentFactory<C>) => boolean = isBoundToModule__PRE_R3__;
@ -671,3 +696,9 @@ function remove<T>(list: T[], el: T): void {
list.splice(index, 1);
}
}
function _mergeArrays(parts: any[][]): any[] {
const result: any[] = [];
parts.forEach((part) => part && result.push(...part));
return result;
}