feat(router): register NgModuleFactory objects. (#11211)
When lazily loading code, users need to be able to get hold of the NgModuleFactory. For SystemJS environments, the SystemJS registry serves this purpose. However other environments, such as modules compiled with Closure compiler, do not expose exports object or a path based registry. For these environments, `@NgModule` objects can include an identifier, and the loading code can then pass `loadModule(id).then(() => getNgModule(id))` to the router.
This commit is contained in:
parent
c9e5b599e4
commit
ebc8e808a9
|
@ -525,13 +525,14 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
|||
importedModules: CompileNgModuleMetadata[];
|
||||
exportedModules: CompileNgModuleMetadata[];
|
||||
schemas: SchemaMetadata[];
|
||||
id: string;
|
||||
|
||||
transitiveModule: TransitiveCompileNgModuleMetadata;
|
||||
|
||||
constructor(
|
||||
{type, providers, declaredDirectives, exportedDirectives, declaredPipes, exportedPipes,
|
||||
entryComponents, bootstrapComponents, importedModules, exportedModules, schemas,
|
||||
transitiveModule}: {
|
||||
transitiveModule, id}: {
|
||||
type?: CompileTypeMetadata,
|
||||
providers?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
|
@ -544,7 +545,8 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
|||
importedModules?: CompileNgModuleMetadata[],
|
||||
exportedModules?: CompileNgModuleMetadata[],
|
||||
transitiveModule?: TransitiveCompileNgModuleMetadata,
|
||||
schemas?: SchemaMetadata[]
|
||||
schemas?: SchemaMetadata[],
|
||||
id?: string
|
||||
} = {}) {
|
||||
this.type = type;
|
||||
this.declaredDirectives = _normalizeArray(declaredDirectives);
|
||||
|
@ -557,6 +559,7 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
|||
this.importedModules = _normalizeArray(importedModules);
|
||||
this.exportedModules = _normalizeArray(exportedModules);
|
||||
this.schemas = _normalizeArray(schemas);
|
||||
this.id = id;
|
||||
this.transitiveModule = transitiveModule;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||
import {AnimationGroupPlayer, AnimationKeyframe, AnimationOutput, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, renderStyles} from './private_import_core';
|
||||
import {AnimationGroupPlayer, AnimationKeyframe, AnimationOutput, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core';
|
||||
import {assetUrl} from './util';
|
||||
|
||||
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
|
||||
|
@ -107,6 +107,11 @@ export class Identifiers {
|
|||
runtime: NgModuleInjector,
|
||||
moduleUrl: assetUrl('core', 'linker/ng_module_factory')
|
||||
};
|
||||
static RegisterModuleFactoryFn: IdentifierSpec = {
|
||||
name: 'registerModuleFactory',
|
||||
runtime: registerModuleFactory,
|
||||
moduleUrl: assetUrl('core', 'linker/ng_module_factory_loader')
|
||||
};
|
||||
static ValueUnwrapper:
|
||||
IdentifierSpec = {name: 'ValueUnwrapper', moduleUrl: CD_MODULE_URL, runtime: ValueUnwrapper};
|
||||
static Injector: IdentifierSpec = {
|
||||
|
|
|
@ -328,7 +328,8 @@ export class CompileMetadataResolver {
|
|||
exportedPipes: exportedPipes,
|
||||
importedModules: importedModules,
|
||||
exportedModules: exportedModules,
|
||||
transitiveModule: transitiveModule
|
||||
transitiveModule: transitiveModule,
|
||||
id: meta.id,
|
||||
});
|
||||
transitiveModule.modules.push(compileMeta);
|
||||
this._verifyModule(compileMeta);
|
||||
|
|
|
@ -69,8 +69,16 @@ export class NgModuleCompiler {
|
|||
[o.importType(ngModuleMeta.type)], [o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||
|
||||
return new NgModuleCompileResult(
|
||||
[injectorClass, ngModuleFactoryStmt], ngModuleFactoryVar, deps);
|
||||
let stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt];
|
||||
if (ngModuleMeta.id) {
|
||||
let registerFactoryStmt =
|
||||
o.importExpr(resolveIdentifier(Identifiers.RegisterModuleFactoryFn))
|
||||
.callFn([o.literal(ngModuleMeta.id), o.variable(ngModuleFactoryVar)])
|
||||
.toStmt();
|
||||
stmts.push(registerFactoryStmt);
|
||||
}
|
||||
|
||||
return new NgModuleCompileResult(stmts, ngModuleFactoryVar, deps);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ export const CodegenComponentFactoryResolver: typeof r.CodegenComponentFactoryRe
|
|||
export const AppView: typeof r.AppView = r.AppView;
|
||||
export const DebugAppView: typeof r.DebugAppView = r.DebugAppView;
|
||||
export const NgModuleInjector: typeof r.NgModuleInjector = r.NgModuleInjector;
|
||||
export const registerModuleFactory: typeof r.registerModuleFactory = r.registerModuleFactory;
|
||||
export type ViewType = typeof r._ViewType;
|
||||
export const ViewType: typeof r.ViewType = r.ViewType;
|
||||
export const MAX_INTERPOLATION_VALUES: typeof r.MAX_INTERPOLATION_VALUES =
|
||||
|
|
|
@ -24,6 +24,7 @@ import * as component_factory_resolver from './linker/component_factory_resolver
|
|||
import * as debug_context from './linker/debug_context';
|
||||
import * as element from './linker/element';
|
||||
import * as ng_module_factory from './linker/ng_module_factory';
|
||||
import * as ng_module_factory_loader from './linker/ng_module_factory_loader';
|
||||
import * as template_ref from './linker/template_ref';
|
||||
import * as view from './linker/view';
|
||||
import * as view_type from './linker/view_type';
|
||||
|
@ -61,6 +62,7 @@ export var __core_private__: {
|
|||
DebugAppView: typeof view.DebugAppView, _DebugAppView?: view.DebugAppView<any>,
|
||||
NgModuleInjector: typeof ng_module_factory.NgModuleInjector,
|
||||
_NgModuleInjector?: ng_module_factory.NgModuleInjector<any>,
|
||||
registerModuleFactory: typeof ng_module_factory_loader.registerModuleFactory,
|
||||
ViewType: typeof view_type.ViewType, _ViewType?: view_type.ViewType,
|
||||
MAX_INTERPOLATION_VALUES: typeof view_utils.MAX_INTERPOLATION_VALUES,
|
||||
checkBinding: typeof view_utils.checkBinding,
|
||||
|
@ -131,6 +133,7 @@ export var __core_private__: {
|
|||
AppView: view.AppView,
|
||||
DebugAppView: view.DebugAppView,
|
||||
NgModuleInjector: ng_module_factory.NgModuleInjector,
|
||||
registerModuleFactory: ng_module_factory_loader.registerModuleFactory,
|
||||
ViewType: view_type.ViewType,
|
||||
MAX_INTERPOLATION_VALUES: view_utils.MAX_INTERPOLATION_VALUES,
|
||||
checkBinding: view_utils.checkBinding,
|
||||
|
|
|
@ -12,7 +12,7 @@ export {ComponentFactory, ComponentRef} from './linker/component_factory';
|
|||
export {ComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||
export {ElementRef} from './linker/element_ref';
|
||||
export {NgModuleFactory, NgModuleRef} from './linker/ng_module_factory';
|
||||
export {NgModuleFactoryLoader} from './linker/ng_module_factory_loader';
|
||||
export {NgModuleFactoryLoader, getModuleFactory} from './linker/ng_module_factory_loader';
|
||||
export {QueryList} from './linker/query_list';
|
||||
export {SystemJsNgModuleLoader, SystemJsNgModuleLoaderConfig} from './linker/system_js_ng_module_factory_loader';
|
||||
export {TemplateRef} from './linker/template_ref';
|
||||
|
|
|
@ -15,3 +15,34 @@ import {NgModuleFactory} from './ng_module_factory';
|
|||
export abstract class NgModuleFactoryLoader {
|
||||
abstract load(path: string): Promise<NgModuleFactory<any>>;
|
||||
}
|
||||
|
||||
let moduleFactories = new Map<string, NgModuleFactory<any>>();
|
||||
|
||||
/**
|
||||
* Registers a loaded module. Should only be called from generated NgModuleFactory code.
|
||||
* @experimental
|
||||
*/
|
||||
export function registerModuleFactory(id: string, factory: NgModuleFactory<any>) {
|
||||
let existing = moduleFactories.get(id);
|
||||
if (existing) {
|
||||
throw new Error(`Duplicate module registered for ${id
|
||||
} - ${existing.moduleType.name} vs ${factory.moduleType.name}`);
|
||||
}
|
||||
moduleFactories.set(id, factory);
|
||||
}
|
||||
|
||||
export function clearModulesForTest() {
|
||||
moduleFactories = new Map<string, NgModuleFactory<any>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the NgModuleFactory with the given id, if it exists and has been loaded.
|
||||
* Factories for modules that do not specify an `id` cannot be retrieved. Throws if the module
|
||||
* cannot be found.
|
||||
* @experimental
|
||||
*/
|
||||
export function getModuleFactory(id: string): NgModuleFactory<any> {
|
||||
let factory = moduleFactories.get(id);
|
||||
if (!factory) throw new Error(`No module with ID ${id} loaded`);
|
||||
return factory;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ export interface NgModuleMetadataType {
|
|||
entryComponents?: Array<Type<any>|any[]>;
|
||||
bootstrap?: Array<Type<any>|any[]>;
|
||||
schemas?: Array<SchemaMetadata|any[]>;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,6 +178,13 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
|
|||
*/
|
||||
schemas: Array<SchemaMetadata|any[]>;
|
||||
|
||||
/**
|
||||
* An opaque ID for this module, e.g. a name or a path. Used to identify modules in
|
||||
* `getModuleFactory`. If left `undefined`, the `NgModule` will not be registered with
|
||||
* `getModuleFactory`.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
constructor(options: NgModuleMetadataType = {}) {
|
||||
// We cannot use destructuring of the constructor argument because `exports` is a
|
||||
// protected symbol in CommonJS and closure tries to aggressively optimize it away.
|
||||
|
@ -188,5 +196,6 @@ export class NgModuleMetadata extends InjectableMetadata implements NgModuleMeta
|
|||
this.entryComponents = options.entryComponents;
|
||||
this.bootstrap = options.bootstrap;
|
||||
this.schemas = options.schemas;
|
||||
this.id = options.id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,14 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, SelfMetadata, Type, forwardRef} from '@angular/core';
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, SelfMetadata, Type, forwardRef, getModuleFactory} from '@angular/core';
|
||||
import {Console} from '@angular/core/src/console';
|
||||
import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {stringify} from '../../src/facade/lang';
|
||||
import {NgModuleInjector} from '../../src/linker/ng_module_factory';
|
||||
import {clearModulesForTest} from '../../src/linker/ng_module_factory_loader';
|
||||
|
||||
class Engine {}
|
||||
|
||||
|
@ -246,6 +247,30 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||
});
|
||||
});
|
||||
|
||||
describe('id', () => {
|
||||
const token = 'myid';
|
||||
@NgModule({id: token})
|
||||
class SomeModule {
|
||||
}
|
||||
@NgModule({id: token})
|
||||
class SomeOtherModule {
|
||||
}
|
||||
|
||||
afterEach(() => clearModulesForTest());
|
||||
|
||||
it('should register loaded modules', () => {
|
||||
createModule(SomeModule);
|
||||
let factory = getModuleFactory(token);
|
||||
expect(factory).toBeTruthy();
|
||||
expect(factory.moduleType).toBe(SomeModule);
|
||||
});
|
||||
|
||||
it('should throw when registering a duplicate module', () => {
|
||||
createModule(SomeModule);
|
||||
expect(() => createModule(SomeOtherModule)).toThrowError(/Duplicate module registered/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('entryComponents', () => {
|
||||
it('should create ComponentFactories in root modules', () => {
|
||||
@NgModule({declarations: [SomeComp], entryComponents: [SomeComp]})
|
||||
|
|
|
@ -539,6 +539,9 @@ export interface ForwardRefFn {
|
|||
/** @experimental */
|
||||
export declare function getDebugNode(nativeNode: any): DebugNode;
|
||||
|
||||
/** @experimental */
|
||||
export declare function getModuleFactory(id: string): NgModuleFactory<any>;
|
||||
|
||||
/** @experimental */
|
||||
export declare function getPlatform(): PlatformRef;
|
||||
|
||||
|
@ -749,6 +752,7 @@ export declare class NgModuleMetadata extends InjectableMetadata implements NgMo
|
|||
declarations: Array<Type<any> | any[]>;
|
||||
entryComponents: Array<Type<any> | any[]>;
|
||||
exports: Array<Type<any> | any[]>;
|
||||
id: string;
|
||||
imports: Array<Type<any> | ModuleWithProviders | any[]>;
|
||||
providers: Provider[];
|
||||
schemas: Array<SchemaMetadata | any[]>;
|
||||
|
@ -767,6 +771,7 @@ export interface NgModuleMetadataType {
|
|||
declarations?: Array<Type<any> | any[]>;
|
||||
entryComponents?: Array<Type<any> | any[]>;
|
||||
exports?: Array<Type<any> | any[]>;
|
||||
id?: string;
|
||||
imports?: Array<Type<any> | ModuleWithProviders | any[]>;
|
||||
providers?: Provider[];
|
||||
schemas?: Array<SchemaMetadata | any[]>;
|
||||
|
|
Loading…
Reference in New Issue