2018-05-09 08:35:25 -07:00
/ * *
* @license
* Copyright Google Inc . All Rights Reserved .
*
* Use of this source code is governed by an MIT - style license that can be
* found in the LICENSE file at https : //angular.io/license
* /
2019-01-11 16:07:01 -08:00
import { R3InjectorMetadataFacade , getCompilerFacade } from '../../compiler/compiler_facade' ;
2018-12-03 13:58:07 -08:00
import { resolveForwardRef } from '../../di/forward_ref' ;
2019-01-09 13:49:16 -08:00
import { NG_INJECTOR_DEF } from '../../di/interface/defs' ;
2019-01-11 16:07:01 -08:00
import { reflectDependencies } from '../../di/jit/util' ;
2019-01-09 13:49:16 -08:00
import { Type } from '../../interface/type' ;
2018-12-11 10:43:02 -08:00
import { registerNgModuleType } from '../../linker/ng_module_factory_loader' ;
import { Component } from '../../metadata' ;
2018-09-21 12:12:06 -07:00
import { ModuleWithProviders , NgModule , NgModuleDef , NgModuleTransitiveScopes } from '../../metadata/ng_module' ;
2019-01-09 13:49:16 -08:00
import { assertDefined } from '../../util/assert' ;
2018-08-29 16:34:44 -07:00
import { getComponentDef , getDirectiveDef , getNgModuleDef , getPipeDef } from '../definition' ;
2019-01-09 13:49:16 -08:00
import { NG_COMPONENT_DEF , NG_DIRECTIVE_DEF , NG_MODULE_DEF , NG_PIPE_DEF } from '../fields' ;
2018-09-21 12:12:06 -07:00
import { ComponentDef } from '../interfaces/definition' ;
2018-12-11 10:43:02 -08:00
import { NgModuleType } from '../ng_module_ref' ;
2019-01-12 00:59:48 -08:00
import { renderStringify } from '../util' ;
2018-05-09 08:35:25 -07:00
import { angularCoreEnv } from './environment' ;
const EMPTY_ARRAY : Type < any > [ ] = [ ] ;
2018-12-03 13:58:07 -08:00
interface ModuleQueueItem {
moduleType : Type < any > ;
ngModule : NgModule ;
}
const moduleQueue : ModuleQueueItem [ ] = [ ] ;
/ * *
* Enqueues moduleDef to be checked later to see if scope can be set on its
* component declarations .
* /
function enqueueModuleForDelayedScoping ( moduleType : Type < any > , ngModule : NgModule ) {
moduleQueue . push ( { moduleType , ngModule } ) ;
}
let flushingModuleQueue = false ;
/ * *
* Loops over queued module definitions , if a given module definition has all of its
* declarations resolved , it dequeues that module definition and sets the scope on
* its declarations .
* /
export function flushModuleScopingQueueAsMuchAsPossible() {
if ( ! flushingModuleQueue ) {
flushingModuleQueue = true ;
2018-12-11 10:43:02 -08:00
try {
for ( let i = moduleQueue . length - 1 ; i >= 0 ; i -- ) {
const { moduleType , ngModule } = moduleQueue [ i ] ;
if ( ngModule . declarations && ngModule . declarations . every ( isResolvedDeclaration ) ) {
// dequeue
moduleQueue . splice ( i , 1 ) ;
setScopeOnDeclaredComponents ( moduleType , ngModule ) ;
}
2018-12-03 13:58:07 -08:00
}
2018-12-11 10:43:02 -08:00
} finally {
flushingModuleQueue = false ;
2018-12-03 13:58:07 -08:00
}
}
}
/ * *
* Returns truthy if a declaration has resolved . If the declaration happens to be
* an array of declarations , it will recurse to check each declaration in that array
* ( which may also be arrays ) .
* /
function isResolvedDeclaration ( declaration : any [ ] | Type < any > ) : boolean {
if ( Array . isArray ( declaration ) ) {
return declaration . every ( isResolvedDeclaration ) ;
}
return ! ! resolveForwardRef ( declaration ) ;
}
2018-08-06 14:09:38 -07:00
/ * *
* Compiles a module in JIT mode .
*
* This function automatically gets called when a class has a ` @NgModule ` decorator .
* /
2018-11-17 22:37:13 -08:00
export function compileNgModule ( moduleType : Type < any > , ngModule : NgModule = { } ) : void {
2018-12-11 10:43:02 -08:00
compileNgModuleDefs ( moduleType as NgModuleType , ngModule ) ;
2018-12-03 13:58:07 -08:00
// Because we don't know if all declarations have resolved yet at the moment the
// NgModule decorator is executing, we're enqueueing the setting of module scope
// on its declarations to be run at a later time when all declarations for the module,
// including forward refs, have resolved.
enqueueModuleForDelayedScoping ( moduleType , ngModule ) ;
2018-08-06 14:09:38 -07:00
}
/ * *
* Compiles and adds the ` ngModuleDef ` and ` ngInjectorDef ` properties to the module class .
* /
2018-12-11 10:43:02 -08:00
export function compileNgModuleDefs ( moduleType : NgModuleType , ngModule : NgModule ) : void {
2018-11-21 21:14:06 -08:00
ngDevMode && assertDefined ( moduleType , 'Required value moduleType' ) ;
ngDevMode && assertDefined ( ngModule , 'Required value ngModule' ) ;
2018-06-06 11:23:38 -07:00
const declarations : Type < any > [ ] = flatten ( ngModule . declarations || EMPTY_ARRAY ) ;
2018-05-21 08:15:19 -07:00
2018-06-19 11:40:29 -07:00
let ngModuleDef : any = null ;
2018-08-06 14:09:38 -07:00
Object . defineProperty ( moduleType , NG_MODULE_DEF , {
2018-10-24 16:02:25 -07:00
configurable : true ,
2018-05-21 08:15:19 -07:00
get : ( ) = > {
2018-06-19 11:40:29 -07:00
if ( ngModuleDef === null ) {
2018-10-24 16:02:25 -07:00
ngModuleDef = getCompilerFacade ( ) . compileNgModule (
angularCoreEnv , ` ng:// ${ moduleType . name } /ngModuleDef.js ` , {
type : moduleType ,
2018-12-18 13:36:19 -08:00
bootstrap : flatten ( ngModule . bootstrap || EMPTY_ARRAY , resolveForwardRef ) ,
declarations : declarations.map ( resolveForwardRef ) ,
imports : flatten ( ngModule . imports || EMPTY_ARRAY , resolveForwardRef )
. map ( expandModuleWithProviders ) ,
exports : flatten ( ngModule . exports || EMPTY_ARRAY , resolveForwardRef )
. map ( expandModuleWithProviders ) ,
2018-10-24 16:02:25 -07:00
emitInline : true ,
} ) ;
2018-05-21 08:15:19 -07:00
}
2018-06-19 11:40:29 -07:00
return ngModuleDef ;
2018-10-24 16:02:25 -07:00
}
2018-06-19 11:40:29 -07:00
} ) ;
2018-12-11 10:43:02 -08:00
if ( ngModule . id ) {
registerNgModuleType ( ngModule . id , moduleType ) ;
}
2018-06-19 11:40:29 -07:00
let ngInjectorDef : any = null ;
2018-08-06 14:09:38 -07:00
Object . defineProperty ( moduleType , NG_INJECTOR_DEF , {
2018-06-19 11:40:29 -07:00
get : ( ) = > {
if ( ngInjectorDef === null ) {
2018-12-11 10:43:02 -08:00
ngDevMode && verifySemanticsOfNgModuleDef ( moduleType as any as NgModuleType ) ;
2018-10-24 16:02:25 -07:00
const meta : R3InjectorMetadataFacade = {
2018-08-06 14:09:38 -07:00
name : moduleType.name ,
2018-10-24 16:02:25 -07:00
type : moduleType ,
2018-08-06 14:09:38 -07:00
deps : reflectDependencies ( moduleType ) ,
2018-10-24 16:02:25 -07:00
providers : ngModule.providers || EMPTY_ARRAY ,
imports : [
2018-12-18 13:36:19 -08:00
( ngModule . imports || EMPTY_ARRAY ) . map ( resolveForwardRef ) ,
( ngModule . exports || EMPTY_ARRAY ) . map ( resolveForwardRef ) ,
2018-10-24 16:02:25 -07:00
] ,
2018-06-19 11:40:29 -07:00
} ;
2018-10-24 16:02:25 -07:00
ngInjectorDef = getCompilerFacade ( ) . compileInjector (
angularCoreEnv , ` ng:// ${ moduleType . name } /ngInjectorDef.js ` , meta ) ;
2018-06-19 11:40:29 -07:00
}
return ngInjectorDef ;
2018-05-21 08:15:19 -07:00
} ,
2018-08-06 14:09:38 -07:00
// Make the property configurable in dev mode to allow overriding in tests
configurable : ! ! ngDevMode ,
2018-05-21 08:15:19 -07:00
} ) ;
2018-08-06 14:09:38 -07:00
}
2018-12-11 10:43:02 -08:00
function verifySemanticsOfNgModuleDef ( moduleType : NgModuleType ) : void {
if ( verifiedNgModule . get ( moduleType ) ) return ;
verifiedNgModule . set ( moduleType , true ) ;
moduleType = resolveForwardRef ( moduleType ) ;
const ngModuleDef = getNgModuleDef ( moduleType , true ) ;
const errors : string [ ] = [ ] ;
ngModuleDef . declarations . forEach ( verifyDeclarationsHaveDefinitions ) ;
const combinedDeclarations : Type < any > [ ] = [
2018-12-18 13:36:19 -08:00
. . . ngModuleDef . declarations . map ( resolveForwardRef ) , //
. . . flatten ( ngModuleDef . imports . map ( computeCombinedExports ) , resolveForwardRef ) ,
2018-12-11 10:43:02 -08:00
] ;
ngModuleDef . exports . forEach ( verifyExportsAreDeclaredOrReExported ) ;
ngModuleDef . declarations . forEach ( verifyDeclarationIsUnique ) ;
ngModuleDef . declarations . forEach ( verifyComponentEntryComponentsIsPartOfNgModule ) ;
const ngModule = getAnnotation < NgModule > ( moduleType , 'NgModule' ) ;
if ( ngModule ) {
ngModule . imports &&
flatten ( ngModule . imports , unwrapModuleWithProvidersImports )
. forEach ( verifySemanticsOfNgModuleDef ) ;
2019-01-25 18:18:19 -08:00
ngModule . bootstrap && ngModule . bootstrap . forEach ( verifyCorrectBootstrapType ) ;
2018-12-11 10:43:02 -08:00
ngModule . bootstrap && ngModule . bootstrap . forEach ( verifyComponentIsPartOfNgModule ) ;
ngModule . entryComponents && ngModule . entryComponents . forEach ( verifyComponentIsPartOfNgModule ) ;
}
// Throw Error if any errors were detected.
if ( errors . length ) {
throw new Error ( errors . join ( '\n' ) ) ;
}
////////////////////////////////////////////////////////////////////////////////////////////////
function verifyDeclarationsHaveDefinitions ( type : Type < any > ) : void {
type = resolveForwardRef ( type ) ;
const def = getComponentDef ( type ) || getDirectiveDef ( type ) || getPipeDef ( type ) ;
if ( ! def ) {
errors . push (
2019-01-12 00:59:48 -08:00
` Unexpected value ' ${ renderStringify ( type ) } ' declared by the module ' ${ renderStringify ( moduleType ) } '. Please add a @Pipe/@Directive/@Component annotation. ` ) ;
2018-12-11 10:43:02 -08:00
}
}
function verifyExportsAreDeclaredOrReExported ( type : Type < any > ) {
type = resolveForwardRef ( type ) ;
const kind = getComponentDef ( type ) && 'component' || getDirectiveDef ( type ) && 'directive' ||
getPipeDef ( type ) && 'pipe' ;
if ( kind ) {
// only checked if we are declared as Component, Directive, or Pipe
// Modules don't need to be declared or imported.
if ( combinedDeclarations . lastIndexOf ( type ) === - 1 ) {
// We are exporting something which we don't explicitly declare or import.
errors . push (
2019-01-12 00:59:48 -08:00
` Can't export ${ kind } ${ renderStringify ( type ) } from ${ renderStringify ( moduleType ) } as it was neither declared nor imported! ` ) ;
2018-12-11 10:43:02 -08:00
}
}
}
function verifyDeclarationIsUnique ( type : Type < any > ) {
type = resolveForwardRef ( type ) ;
const existingModule = ownerNgModule . get ( type ) ;
if ( existingModule && existingModule !== moduleType ) {
2019-01-12 00:59:48 -08:00
const modules = [ existingModule , moduleType ] . map ( renderStringify ) . sort ( ) ;
2018-12-11 10:43:02 -08:00
errors . push (
2019-01-12 00:59:48 -08:00
` Type ${ renderStringify ( type ) } is part of the declarations of 2 modules: ${ modules [ 0 ] } and ${ modules [ 1 ] } ! ` +
` Please consider moving ${ renderStringify ( type ) } to a higher module that imports ${ modules [ 0 ] } and ${ modules [ 1 ] } . ` +
` You can also create a new NgModule that exports and includes ${ renderStringify ( type ) } then import that NgModule in ${ modules [ 0 ] } and ${ modules [ 1 ] } . ` ) ;
2018-12-11 10:43:02 -08:00
} else {
// Mark type as having owner.
ownerNgModule . set ( type , moduleType ) ;
}
}
function verifyComponentIsPartOfNgModule ( type : Type < any > ) {
type = resolveForwardRef ( type ) ;
const existingModule = ownerNgModule . get ( type ) ;
if ( ! existingModule ) {
errors . push (
2019-01-12 00:59:48 -08:00
` Component ${ renderStringify ( type ) } is not part of any NgModule or the module has not been imported into your module. ` ) ;
2018-12-11 10:43:02 -08:00
}
}
2019-01-25 18:18:19 -08:00
function verifyCorrectBootstrapType ( type : Type < any > ) {
type = resolveForwardRef ( type ) ;
if ( ! getComponentDef ( type ) ) {
errors . push ( ` ${ renderStringify ( type ) } cannot be used as an entry component. ` ) ;
}
}
2018-12-11 10:43:02 -08:00
function verifyComponentEntryComponentsIsPartOfNgModule ( type : Type < any > ) {
type = resolveForwardRef ( type ) ;
if ( getComponentDef ( type ) ) {
// We know we are component
const component = getAnnotation < Component > ( type , 'Component' ) ;
if ( component && component . entryComponents ) {
component . entryComponents . forEach ( verifyComponentIsPartOfNgModule ) ;
}
}
}
}
function unwrapModuleWithProvidersImports (
typeOrWithProviders : NgModuleType < any > | { ngModule : NgModuleType < any > } ) : NgModuleType < any > {
typeOrWithProviders = resolveForwardRef ( typeOrWithProviders ) ;
return ( typeOrWithProviders as any ) . ngModule || typeOrWithProviders ;
}
function getAnnotation < T > ( type : any , name : string ) : T | null {
let annotation : T | null = null ;
collect ( type . __annotations__ ) ;
collect ( type . decorators ) ;
return annotation ;
function collect ( annotations : any [ ] | null ) {
if ( annotations ) {
annotations . forEach ( readAnnotation ) ;
}
}
function readAnnotation (
decorator : { type : { prototype : { ngMetadataName : string } , args : any [ ] } , args : any } ) : void {
if ( ! annotation ) {
const proto = Object . getPrototypeOf ( decorator ) ;
if ( proto . ngMetadataName == name ) {
annotation = decorator as any ;
} else if ( decorator . type ) {
const proto = Object . getPrototypeOf ( decorator . type ) ;
if ( proto . ngMetadataName == name ) {
annotation = decorator . args [ 0 ] ;
}
}
}
}
}
/ * *
* Keep track of compiled components . This is needed because in tests we often want to compile the
* same component with more than one NgModule . This would cause an error unless we reset which
* NgModule the component belongs to . We keep the list of compiled components here so that the
* TestBed can reset it later .
* /
let ownerNgModule = new Map < Type < any > , NgModuleType < any > > ( ) ;
let verifiedNgModule = new Map < NgModuleType < any > , boolean > ( ) ;
export function resetCompiledComponents ( ) : void {
ownerNgModule = new Map < Type < any > , NgModuleType < any > > ( ) ;
verifiedNgModule = new Map < NgModuleType < any > , boolean > ( ) ;
moduleQueue . length = 0 ;
}
/ * *
* Computes the combined declarations of explicit declarations , as well as declarations inherited
* by
* traversing the exports of imported modules .
* @param type
* /
function computeCombinedExports ( type : Type < any > ) : Type < any > [ ] {
type = resolveForwardRef ( type ) ;
const ngModuleDef = getNgModuleDef ( type , true ) ;
return [ . . . flatten ( ngModuleDef . exports . map ( ( type ) = > {
const ngModuleDef = getNgModuleDef ( type ) ;
if ( ngModuleDef ) {
verifySemanticsOfNgModuleDef ( type as any as NgModuleType ) ;
return computeCombinedExports ( type ) ;
} else {
return type ;
}
} ) ) ] ;
}
2018-08-06 14:09:38 -07:00
/ * *
* Some declared components may be compiled asynchronously , and thus may not have their
* ngComponentDef set yet . If this is the case , then a reference to the module is written into
* the ` ngSelectorScope ` property of the declared type .
* /
function setScopeOnDeclaredComponents ( moduleType : Type < any > , ngModule : NgModule ) {
const declarations : Type < any > [ ] = flatten ( ngModule . declarations || EMPTY_ARRAY ) ;
const transitiveScopes = transitiveScopesFor ( moduleType ) ;
2018-06-06 11:23:38 -07:00
declarations . forEach ( declaration = > {
if ( declaration . hasOwnProperty ( NG_COMPONENT_DEF ) ) {
// An `ngComponentDef` field exists - go ahead and patch the component directly.
2018-09-21 12:12:06 -07:00
const component = declaration as Type < any > & { ngComponentDef : ComponentDef < any > } ;
2018-08-29 16:34:44 -07:00
const componentDef = getComponentDef ( component ) ! ;
2018-08-06 14:09:38 -07:00
patchComponentDefWithScope ( componentDef , transitiveScopes ) ;
2018-06-06 11:23:38 -07:00
} else if (
! declaration . hasOwnProperty ( NG_DIRECTIVE_DEF ) && ! declaration . hasOwnProperty ( NG_PIPE_DEF ) ) {
// Set `ngSelectorScope` for future reference when the component compilation finishes.
2018-08-06 14:09:38 -07:00
( declaration as Type < any > & { ngSelectorScope? : any } ) . ngSelectorScope = moduleType ;
2018-06-06 11:23:38 -07:00
}
} ) ;
}
/ * *
* Patch the definition of a component with directives and pipes from the compilation scope of
* a given module .
* /
2018-08-06 14:09:38 -07:00
export function patchComponentDefWithScope < C > (
2018-09-21 12:12:06 -07:00
componentDef : ComponentDef < C > , transitiveScopes : NgModuleTransitiveScopes ) {
2018-08-06 14:09:38 -07:00
componentDef . directiveDefs = ( ) = > Array . from ( transitiveScopes . compilation . directives )
2018-08-29 16:34:44 -07:00
. map ( dir = > getDirectiveDef ( dir ) || getComponentDef ( dir ) ! )
2018-06-06 11:23:38 -07:00
. filter ( def = > ! ! def ) ;
componentDef . pipeDefs = ( ) = >
2018-08-29 16:34:44 -07:00
Array . from ( transitiveScopes . compilation . pipes ) . map ( pipe = > getPipeDef ( pipe ) ! ) ;
2018-05-09 08:35:25 -07:00
}
2018-06-06 11:23:38 -07:00
/ * *
* Compute the pair of transitive scopes ( compilation scope and exported scope ) for a given module .
*
* This operation is memoized and the result is cached on the module ' s definition . It can be called
* on modules with components that have not fully compiled yet , but the result should not be used
* until they have .
* /
2018-12-28 11:03:25 -08:00
export function transitiveScopesFor < T > (
moduleType : Type < T > ,
processNgModuleFn ? : ( ngModule : NgModuleType ) = > void ) : NgModuleTransitiveScopes {
2018-06-06 11:23:38 -07:00
if ( ! isNgModule ( moduleType ) ) {
throw new Error ( ` ${ moduleType . name } does not have an ngModuleDef ` ) ;
}
2018-08-29 16:34:44 -07:00
const def = getNgModuleDef ( moduleType ) ! ;
2018-06-06 11:23:38 -07:00
if ( def . transitiveCompileScopes !== null ) {
return def . transitiveCompileScopes ;
}
const scopes : NgModuleTransitiveScopes = {
compilation : {
directives : new Set < any > ( ) ,
pipes : new Set < any > ( ) ,
} ,
exported : {
directives : new Set < any > ( ) ,
pipes : new Set < any > ( ) ,
} ,
} ;
def . declarations . forEach ( declared = > {
const declaredWithDefs = declared as Type < any > & { ngPipeDef? : any ; } ;
2018-08-29 16:34:44 -07:00
if ( getPipeDef ( declaredWithDefs ) ) {
2018-06-06 11:23:38 -07:00
scopes . compilation . pipes . add ( declared ) ;
} else {
// Either declared has an ngComponentDef or ngDirectiveDef, or it's a component which hasn't
// had its template compiled yet. In either case, it gets added to the compilation's
// directives.
scopes . compilation . directives . add ( declared ) ;
}
} ) ;
def . imports . forEach ( < I > ( imported : Type < I > ) = > {
2018-12-28 11:03:25 -08:00
const importedType = imported as Type < I > & {
2018-06-06 11:23:38 -07:00
// If imported is an @NgModule:
2018-09-21 12:12:06 -07:00
ngModuleDef? : NgModuleDef < I > ;
2018-06-06 11:23:38 -07:00
} ;
2018-12-28 11:03:25 -08:00
if ( ! isNgModule < I > ( importedType ) ) {
throw new Error ( ` Importing ${ importedType . name } which does not have an ngModuleDef ` ) ;
}
if ( processNgModuleFn ) {
processNgModuleFn ( importedType as NgModuleType ) ;
2018-06-06 11:23:38 -07:00
}
// When this module imports another, the imported module's exported directives and pipes are
// added to the compilation scope of this module.
2018-12-28 11:03:25 -08:00
const importedScope = transitiveScopesFor ( importedType , processNgModuleFn ) ;
2018-06-06 11:23:38 -07:00
importedScope . exported . directives . forEach ( entry = > scopes . compilation . directives . add ( entry ) ) ;
importedScope . exported . pipes . forEach ( entry = > scopes . compilation . pipes . add ( entry ) ) ;
} ) ;
def . exports . forEach ( < E > ( exported : Type < E > ) = > {
2018-12-28 11:03:25 -08:00
const exportedType = exported as Type < E > & {
2018-06-06 11:23:38 -07:00
// Components, Directives, NgModules, and Pipes can all be exported.
ngComponentDef? : any ;
ngDirectiveDef? : any ;
2018-09-21 12:12:06 -07:00
ngModuleDef? : NgModuleDef < E > ;
2018-06-06 11:23:38 -07:00
ngPipeDef? : any ;
} ;
// Either the type is a module, a pipe, or a component/directive (which may not have an
// ngComponentDef as it might be compiled asynchronously).
2018-12-28 11:03:25 -08:00
if ( isNgModule ( exportedType ) ) {
2018-06-06 11:23:38 -07:00
// When this module exports another, the exported module's exported directives and pipes are
// added to both the compilation and exported scopes of this module.
2018-12-28 11:03:25 -08:00
const exportedScope = transitiveScopesFor ( exportedType , processNgModuleFn ) ;
2018-06-06 11:23:38 -07:00
exportedScope . exported . directives . forEach ( entry = > {
scopes . compilation . directives . add ( entry ) ;
scopes . exported . directives . add ( entry ) ;
} ) ;
exportedScope . exported . pipes . forEach ( entry = > {
scopes . compilation . pipes . add ( entry ) ;
scopes . exported . pipes . add ( entry ) ;
} ) ;
2018-12-28 11:03:25 -08:00
} else if ( getPipeDef ( exportedType ) ) {
scopes . exported . pipes . add ( exportedType ) ;
2018-06-06 11:23:38 -07:00
} else {
2018-12-28 11:03:25 -08:00
scopes . exported . directives . add ( exportedType ) ;
2018-06-06 11:23:38 -07:00
}
} ) ;
def . transitiveCompileScopes = scopes ;
return scopes ;
}
2018-12-11 10:43:02 -08:00
function flatten < T > ( values : any [ ] , mapFn ? : ( value : T ) = > any ) : Type < T > [ ] {
const out : Type < T > [ ] = [ ] ;
2018-06-06 11:23:38 -07:00
values . forEach ( value = > {
if ( Array . isArray ( value ) ) {
2018-12-11 10:43:02 -08:00
out . push ( . . . flatten < T > ( value , mapFn ) ) ;
2018-06-06 11:23:38 -07:00
} else {
2018-12-11 10:43:02 -08:00
out . push ( mapFn ? mapFn ( value ) : value ) ;
2018-06-06 11:23:38 -07:00
}
} ) ;
return out ;
2018-05-09 08:35:25 -07:00
}
2018-09-14 14:55:16 -07:00
function expandModuleWithProviders ( value : Type < any > | ModuleWithProviders < { } > ) : Type < any > {
2018-05-09 08:35:25 -07:00
if ( isModuleWithProviders ( value ) ) {
return value . ngModule ;
}
return value ;
}
2018-09-14 14:55:16 -07:00
function isModuleWithProviders ( value : any ) : value is ModuleWithProviders < { } > {
2018-06-06 11:23:38 -07:00
return ( value as { ngModule? : any } ) . ngModule !== undefined ;
2018-05-09 08:35:25 -07:00
}
2018-09-21 12:12:06 -07:00
function isNgModule < T > ( value : Type < T > ) : value is Type < T > & { ngModuleDef : NgModuleDef < T > } {
2018-08-29 16:34:44 -07:00
return ! ! getNgModuleDef ( value ) ;
2018-05-09 08:35:25 -07:00
}