2016-06-23 09:47:54 -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
*/
2016-08-10 15:55:18 -07:00
import { AnimationAnimateMetadata , AnimationEntryMetadata , AnimationGroupMetadata , AnimationKeyframesSequenceMetadata , AnimationMetadata , AnimationStateDeclarationMetadata , AnimationStateMetadata , AnimationStateTransitionMetadata , AnimationStyleMetadata , AnimationWithStepsMetadata , AttributeMetadata , BaseException , ChangeDetectionStrategy , ComponentMetadata , HostMetadata , Inject , InjectMetadata , Injectable , ModuleWithProviders , NgModule , NgModuleMetadata , Optional , OptionalMetadata , Provider , QueryMetadata , SchemaMetadata , SelfMetadata , SkipSelfMetadata , ViewMetadata , ViewQueryMetadata , resolveForwardRef } from '@angular/core' ;
2016-06-08 16:38:52 -07:00
2016-07-18 03:50:31 -07:00
import { Console , LIFECYCLE_HOOKS_VALUES , ReflectorReader , createProvider , isProviderLiteral , reflector } from '../core_private' ;
2016-07-21 13:56:58 -07:00
import { StringMapWrapper } from '../src/facade/collection' ;
2016-06-08 16:38:52 -07:00
2016-06-20 09:52:41 -07:00
import { assertArrayOfStrings , assertInterpolationSymbols } from './assertions' ;
2016-01-06 14:13:44 -08:00
import * as cpl from './compile_metadata' ;
2016-06-13 08:35:31 -07:00
import { CompilerConfig } from './config' ;
2016-01-06 14:13:44 -08:00
import { DirectiveResolver } from './directive_resolver' ;
2016-07-21 13:56:58 -07:00
import { Type , isArray , isBlank , isPresent , isString , stringify } from './facade/lang' ;
2016-07-07 10:05:55 -07:00
import { Identifiers , identifierToken } from './identifiers' ;
2016-08-02 01:37:42 -07:00
import { hasLifecycleHook } from './lifecycle_reflector' ;
2016-07-18 03:50:31 -07:00
import { NgModuleResolver } from './ng_module_resolver' ;
2016-01-06 14:13:44 -08:00
import { PipeResolver } from './pipe_resolver' ;
2016-07-28 19:39:10 +02:00
import { ElementSchemaRegistry } from './schema/element_schema_registry' ;
2016-04-28 17:50:03 -07:00
import { getUrlScheme } from './url_resolver' ;
2016-06-08 16:38:52 -07:00
import { MODULE_SUFFIX , ValueTransformer , sanitizeIdentifier , visitValue } from './util' ;
2016-04-28 17:50:03 -07:00
2015-09-14 15:59:09 -07:00
@Injectable ()
2016-02-18 10:53:21 -08:00
export class CompileMetadataResolver {
2015-12-02 10:35:51 -08:00
private _directiveCache = new Map < Type , cpl.CompileDirectiveMetadata >();
private _pipeCache = new Map < Type , cpl.CompilePipeMetadata >();
2016-07-18 03:50:31 -07:00
private _ngModuleCache = new Map < Type , cpl.CompileNgModuleMetadata >();
private _ngModuleOfTypes = new Map < Type , Type >();
2016-03-29 17:15:07 -07:00
private _anonymousTypes = new Map < Object , number >();
private _anonymousTypeIndex = 0 ;
2015-09-14 15:59:09 -07:00
2016-06-08 16:38:52 -07:00
constructor (
2016-07-18 03:50:31 -07:00
private _ngModuleResolver : NgModuleResolver , private _directiveResolver : DirectiveResolver ,
2016-07-28 06:31:26 -07:00
private _pipeResolver : PipeResolver , private _config : CompilerConfig ,
2016-07-29 02:10:30 -07:00
private _console : Console , private _schemaRegistry : ElementSchemaRegistry ,
private _reflector : ReflectorReader = reflector ) {}
2015-09-14 15:59:09 -07:00
2016-04-20 18:10:19 -07:00
private sanitizeTokenName ( token : any ) : string {
let identifier = stringify ( token );
if ( identifier . indexOf ( '(' ) >= 0 ) {
// case: anonymous functions!
let found = this . _anonymousTypes . get ( token );
if ( isBlank ( found )) {
this . _anonymousTypes . set ( token , this . _anonymousTypeIndex ++ );
found = this . _anonymousTypes . get ( token );
}
identifier = `anonymous_token_ ${ found } _` ;
2016-03-29 17:15:07 -07:00
}
2016-04-20 18:10:19 -07:00
return sanitizeIdentifier ( identifier );
2016-03-29 17:15:07 -07:00
}
2016-06-28 09:54:42 -07:00
clearCacheFor ( type : Type ) {
this . _directiveCache . delete ( type );
this . _pipeCache . delete ( type );
2016-07-18 03:50:31 -07:00
this . _ngModuleOfTypes . delete ( type );
// Clear all of the NgModuleMetadata as they contain transitive information!
this . _ngModuleCache . clear ();
2016-06-24 08:46:43 -07:00
}
clearCache() {
this . _directiveCache . clear ();
this . _pipeCache . clear ();
2016-07-18 03:50:31 -07:00
this . _ngModuleCache . clear ();
this . _ngModuleOfTypes . clear ();
2016-06-24 08:46:43 -07:00
}
2016-05-26 15:07:51 -07:00
getAnimationEntryMetadata ( entry : AnimationEntryMetadata ) : cpl . CompileAnimationEntryMetadata {
2016-05-25 12:46:22 -07:00
var defs = entry . definitions . map ( def => this . getAnimationStateMetadata ( def ));
return new cpl . CompileAnimationEntryMetadata ( entry . name , defs );
}
2016-05-26 15:07:51 -07:00
getAnimationStateMetadata ( value : AnimationStateMetadata ) : cpl . CompileAnimationStateMetadata {
if ( value instanceof AnimationStateDeclarationMetadata ) {
2016-05-25 12:46:22 -07:00
var styles = this . getAnimationStyleMetadata ( value . styles );
return new cpl . CompileAnimationStateDeclarationMetadata ( value . stateNameExpr , styles );
2016-05-26 15:07:51 -07:00
} else if ( value instanceof AnimationStateTransitionMetadata ) {
2016-06-08 16:38:52 -07:00
return new cpl . CompileAnimationStateTransitionMetadata (
value . stateChangeExpr , this . getAnimationMetadata ( value . steps ));
2016-05-25 12:46:22 -07:00
}
return null ;
}
2016-05-26 15:07:51 -07:00
getAnimationStyleMetadata ( value : AnimationStyleMetadata ) : cpl . CompileAnimationStyleMetadata {
2016-05-25 12:46:22 -07:00
return new cpl . CompileAnimationStyleMetadata ( value . offset , value . styles );
}
2016-05-26 15:07:51 -07:00
getAnimationMetadata ( value : AnimationMetadata ) : cpl . CompileAnimationMetadata {
if ( value instanceof AnimationStyleMetadata ) {
2016-05-25 12:46:22 -07:00
return this . getAnimationStyleMetadata ( value );
2016-05-26 15:07:51 -07:00
} else if ( value instanceof AnimationKeyframesSequenceMetadata ) {
2016-06-08 16:38:52 -07:00
return new cpl . CompileAnimationKeyframesSequenceMetadata (
value . steps . map ( entry => this . getAnimationStyleMetadata ( entry )));
2016-05-26 15:07:51 -07:00
} else if ( value instanceof AnimationAnimateMetadata ) {
2016-06-08 16:38:52 -07:00
let animateData =
< cpl.CompileAnimationStyleMetadata | cpl.CompileAnimationKeyframesSequenceMetadata > this
. getAnimationMetadata ( value . styles );
2016-05-25 12:46:22 -07:00
return new cpl . CompileAnimationAnimateMetadata ( value . timings , animateData );
2016-05-26 15:07:51 -07:00
} else if ( value instanceof AnimationWithStepsMetadata ) {
2016-05-25 12:46:22 -07:00
var steps = value . steps . map ( step => this . getAnimationMetadata ( step ));
2016-05-26 15:07:51 -07:00
if ( value instanceof AnimationGroupMetadata ) {
2016-05-25 12:46:22 -07:00
return new cpl . CompileAnimationGroupMetadata ( steps );
} else {
return new cpl . CompileAnimationSequenceMetadata ( steps );
}
}
return null ;
}
2016-07-18 03:50:31 -07:00
getDirectiveMetadata ( directiveType : Type , throwIfNotFound = true ) : cpl . CompileDirectiveMetadata {
2016-06-28 09:54:42 -07:00
directiveType = resolveForwardRef ( directiveType );
2015-12-02 10:35:51 -08:00
var meta = this . _directiveCache . get ( directiveType );
2015-09-14 15:59:09 -07:00
if ( isBlank ( meta )) {
2016-07-18 03:50:31 -07:00
var dirMeta = this . _directiveResolver . resolve ( directiveType , throwIfNotFound );
if ( ! dirMeta ) {
return null ;
}
2016-06-11 21:23:37 -07:00
var templateMeta : cpl.CompileTemplateMetadata = null ;
2016-06-22 17:25:42 -07:00
var changeDetectionStrategy : ChangeDetectionStrategy = null ;
var viewProviders : Array < cpl.CompileProviderMetadata | cpl.CompileTypeMetadata | any [] > = [];
2016-05-02 16:45:43 -07:00
var moduleUrl = staticTypeModuleUrl ( directiveType );
2016-07-28 06:31:26 -07:00
var viewDirectiveTypes : cpl.CompileTypeMetadata [] = [];
var viewPipeTypes : cpl.CompileTypeMetadata [] = [];
2016-07-25 00:36:30 -07:00
var entryComponentTypes : cpl.CompileTypeMetadata [] = [];
2016-07-28 19:39:10 +02:00
let selector = dirMeta . selector ;
2016-04-28 17:50:03 -07:00
if ( dirMeta instanceof ComponentMetadata ) {
var cmpMeta = < ComponentMetadata > dirMeta ;
2016-07-28 06:31:26 -07:00
assertArrayOfStrings ( 'styles' , cmpMeta . styles );
assertInterpolationSymbols ( 'interpolation' , cmpMeta . interpolation );
var animations = isPresent ( cmpMeta . animations ) ?
cmpMeta . animations . map ( e => this . getAnimationEntryMetadata ( e )) :
2016-06-08 16:38:52 -07:00
null ;
2016-07-28 06:31:26 -07:00
assertArrayOfStrings ( 'styles' , cmpMeta . styles );
assertArrayOfStrings ( 'styleUrls' , cmpMeta . styleUrls );
2016-05-25 12:46:22 -07:00
2015-09-18 10:33:23 -07:00
templateMeta = new cpl . CompileTemplateMetadata ({
2016-07-28 06:31:26 -07:00
encapsulation : cmpMeta.encapsulation ,
template : cmpMeta.template ,
templateUrl : cmpMeta.templateUrl ,
styles : cmpMeta.styles ,
styleUrls : cmpMeta.styleUrls ,
2016-06-20 09:52:41 -07:00
animations : animations ,
2016-07-28 06:31:26 -07:00
interpolation : cmpMeta.interpolation
2015-09-14 15:59:09 -07:00
});
2015-10-23 15:55:48 -07:00
changeDetectionStrategy = cmpMeta . changeDetection ;
2016-01-06 14:13:44 -08:00
if ( isPresent ( dirMeta . viewProviders )) {
2016-06-22 02:27:27 +02:00
viewProviders = this . getProvidersMetadata (
2016-07-07 10:05:55 -07:00
verifyNonBlankProviders ( directiveType , dirMeta . viewProviders , 'viewProviders' ), []);
2016-01-06 14:13:44 -08:00
}
2016-05-02 16:45:43 -07:00
moduleUrl = componentModuleUrl ( this . _reflector , directiveType , cmpMeta );
2016-07-25 00:36:30 -07:00
if ( cmpMeta . entryComponents ) {
entryComponentTypes =
flattenArray ( cmpMeta . entryComponents )
2016-07-28 06:31:26 -07:00
. map (( type ) => this . getTypeMetadata ( type , staticTypeModuleUrl ( type )));
}
if ( cmpMeta . directives ) {
viewDirectiveTypes = flattenArray ( cmpMeta . directives ). map (( type ) => {
if ( ! type ) {
throw new BaseException (
`Unexpected directive value ' ${ type } ' on the View of component ' ${ stringify ( directiveType ) } '` );
}
return this . getTypeMetadata ( type , staticTypeModuleUrl ( type ));
});
}
if ( cmpMeta . pipes ) {
viewPipeTypes = flattenArray ( cmpMeta . pipes ). map (( type ) => {
if ( ! type ) {
throw new BaseException (
`Unexpected pipe value ' ${ type } ' on the View of component ' ${ stringify ( directiveType ) } '` );
}
return this . getTypeMetadata ( type , staticTypeModuleUrl ( type ));
});
2016-06-22 14:06:23 -07:00
}
2016-07-28 19:39:10 +02:00
if ( ! selector ) {
selector = this . _schemaRegistry . getDefaultComponentElementName ();
}
} else {
if ( ! selector ) {
throw new BaseException (
`Directive ${ stringify ( directiveType ) } has no selector, please add it!` );
}
2016-01-06 14:13:44 -08:00
}
2016-06-22 17:25:42 -07:00
var providers : Array < cpl.CompileProviderMetadata | cpl.CompileTypeMetadata | any [] > = [];
2016-01-06 14:13:44 -08:00
if ( isPresent ( dirMeta . providers )) {
2016-06-22 02:27:27 +02:00
providers = this . getProvidersMetadata (
2016-07-07 10:05:55 -07:00
verifyNonBlankProviders ( directiveType , dirMeta . providers , 'providers' ),
2016-07-25 00:36:30 -07:00
entryComponentTypes );
2016-01-06 14:13:44 -08:00
}
2016-07-07 10:05:55 -07:00
var queries : cpl.CompileQueryMetadata [] = [];
var viewQueries : cpl.CompileQueryMetadata [] = [];
2016-01-06 14:13:44 -08:00
if ( isPresent ( dirMeta . queries )) {
2016-06-05 04:46:03 +02:00
queries = this . getQueriesMetadata ( dirMeta . queries , false , directiveType );
viewQueries = this . getQueriesMetadata ( dirMeta . queries , true , directiveType );
2015-09-14 15:59:09 -07:00
}
2015-09-18 10:33:23 -07:00
meta = cpl . CompileDirectiveMetadata . create ({
2016-07-28 19:39:10 +02:00
selector : selector ,
2015-10-23 15:55:48 -07:00
exportAs : dirMeta.exportAs ,
2015-09-14 15:59:09 -07:00
isComponent : isPresent ( templateMeta ),
2016-05-02 16:45:43 -07:00
type : this . getTypeMetadata ( directiveType , moduleUrl ),
2015-09-14 15:59:09 -07:00
template : templateMeta ,
2015-09-18 10:33:23 -07:00
changeDetection : changeDetectionStrategy ,
2015-10-23 15:55:48 -07:00
inputs : dirMeta.inputs ,
outputs : dirMeta.outputs ,
host : dirMeta.host ,
2016-01-06 14:13:44 -08:00
providers : providers ,
viewProviders : viewProviders ,
queries : queries ,
2016-06-22 14:06:23 -07:00
viewQueries : viewQueries ,
2016-07-28 06:31:26 -07:00
viewDirectives : viewDirectiveTypes ,
viewPipes : viewPipeTypes ,
2016-07-25 00:36:30 -07:00
entryComponents : entryComponentTypes
2015-09-14 15:59:09 -07:00
});
2015-12-02 10:35:51 -08:00
this . _directiveCache . set ( directiveType , meta );
}
return meta ;
}
2016-07-18 03:50:31 -07:00
getNgModuleMetadata ( moduleType : any , throwIfNotFound = true ) : cpl . CompileNgModuleMetadata {
2016-06-28 09:54:42 -07:00
moduleType = resolveForwardRef ( moduleType );
2016-07-18 03:50:31 -07:00
var compileMeta = this . _ngModuleCache . get ( moduleType );
if ( ! compileMeta ) {
const meta = this . _ngModuleResolver . resolve ( moduleType , throwIfNotFound );
2016-06-28 09:54:42 -07:00
if ( ! meta ) {
2016-07-18 03:50:31 -07:00
return null ;
2016-06-28 09:54:42 -07:00
}
2016-07-18 03:50:31 -07:00
const declaredDirectives : cpl.CompileDirectiveMetadata [] = [];
const exportedDirectives : cpl.CompileDirectiveMetadata [] = [];
const declaredPipes : cpl.CompilePipeMetadata [] = [];
const exportedPipes : cpl.CompilePipeMetadata [] = [];
const importedModules : cpl.CompileNgModuleMetadata [] = [];
const exportedModules : cpl.CompileNgModuleMetadata [] = [];
2016-07-25 01:39:50 -07:00
const providers : any [] = [];
const entryComponents : cpl.CompileTypeMetadata [] = [];
2016-08-02 06:54:08 -07:00
const bootstrapComponents : cpl.CompileTypeMetadata [] = [];
2016-07-25 03:02:57 -07:00
const schemas : SchemaMetadata [] = [];
2016-07-18 03:50:31 -07:00
if ( meta . imports ) {
flattenArray ( meta . imports ). forEach (( importedType ) => {
2016-07-25 01:39:50 -07:00
let importedModuleType : Type ;
if ( isValidType ( importedType )) {
importedModuleType = importedType ;
} else if ( importedType && importedType . ngModule ) {
const moduleWithProviders : ModuleWithProviders = importedType ;
importedModuleType = moduleWithProviders . ngModule ;
if ( moduleWithProviders . providers ) {
providers . push (
... this . getProvidersMetadata ( moduleWithProviders . providers , entryComponents ));
}
2016-07-18 03:50:31 -07:00
}
2016-07-25 01:39:50 -07:00
if ( importedModuleType ) {
importedModules . push ( this . getNgModuleMetadata ( importedModuleType , false ));
2016-07-18 03:50:31 -07:00
} else {
throw new BaseException (
`Unexpected value ' ${ stringify ( importedType ) } ' imported by the module ' ${ stringify ( moduleType ) } '` );
}
2016-06-28 09:54:42 -07:00
});
}
2016-07-18 03:50:31 -07:00
if ( meta . exports ) {
flattenArray ( meta . exports ). forEach (( exportedType ) => {
if ( ! isValidType ( exportedType )) {
throw new BaseException (
`Unexpected value ' ${ stringify ( exportedType ) } ' exported by the module ' ${ stringify ( moduleType ) } '` );
}
let exportedDirMeta : cpl.CompileDirectiveMetadata ;
let exportedPipeMeta : cpl.CompilePipeMetadata ;
let exportedModuleMeta : cpl.CompileNgModuleMetadata ;
if ( exportedDirMeta = this . getDirectiveMetadata ( exportedType , false )) {
exportedDirectives . push ( exportedDirMeta );
} else if ( exportedPipeMeta = this . getPipeMetadata ( exportedType , false )) {
exportedPipes . push ( exportedPipeMeta );
} else if ( exportedModuleMeta = this . getNgModuleMetadata ( exportedType , false )) {
exportedModules . push ( exportedModuleMeta );
} else {
throw new BaseException (
`Unexpected value ' ${ stringify ( exportedType ) } ' exported by the module ' ${ stringify ( moduleType ) } '` );
}
});
}
// Note: This will be modified later, so we rely on
// getting a new instance every time!
const transitiveModule =
this . _getTransitiveNgModuleMetadata ( importedModules , exportedModules );
if ( meta . declarations ) {
flattenArray ( meta . declarations ). forEach (( declaredType ) => {
if ( ! isValidType ( declaredType )) {
throw new BaseException (
`Unexpected value ' ${ stringify ( declaredType ) } ' declared by the module ' ${ stringify ( moduleType ) } '` );
}
let declaredDirMeta : cpl.CompileDirectiveMetadata ;
let declaredPipeMeta : cpl.CompilePipeMetadata ;
if ( declaredDirMeta = this . getDirectiveMetadata ( declaredType , false )) {
this . _addDirectiveToModule (
declaredDirMeta , moduleType , transitiveModule , declaredDirectives , true );
} else if ( declaredPipeMeta = this . getPipeMetadata ( declaredType , false )) {
this . _addPipeToModule (
declaredPipeMeta , moduleType , transitiveModule , declaredPipes , true );
} else {
throw new BaseException (
`Unexpected value ' ${ stringify ( declaredType ) } ' declared by the module ' ${ stringify ( moduleType ) } '` );
}
});
}
2016-07-27 01:52:31 -07:00
// The providers of the module have to go last
// so that they overwrite any other provider we already added.
if ( meta . providers ) {
providers . push (... this . getProvidersMetadata ( meta . providers , entryComponents ));
}
if ( meta . entryComponents ) {
entryComponents . push (
... flattenArray ( meta . entryComponents )
. map ( type => this . getTypeMetadata ( type , staticTypeModuleUrl ( type ))));
}
2016-08-02 06:54:08 -07:00
if ( meta . bootstrap ) {
bootstrapComponents . push (
... flattenArray ( meta . bootstrap )
. map ( type => this . getTypeMetadata ( type , staticTypeModuleUrl ( type ))));
}
entryComponents . push (... bootstrapComponents );
2016-07-27 01:52:31 -07:00
if ( meta . schemas ) {
schemas . push (... flattenArray ( meta . schemas ));
}
2016-07-25 00:36:30 -07:00
transitiveModule . entryComponents . push (... entryComponents );
2016-07-18 03:50:31 -07:00
transitiveModule . providers . push (... providers );
compileMeta = new cpl . CompileNgModuleMetadata ({
2016-06-28 09:54:42 -07:00
type : this . getTypeMetadata ( moduleType , staticTypeModuleUrl ( moduleType )),
providers : providers ,
2016-07-25 00:36:30 -07:00
entryComponents : entryComponents ,
2016-08-02 06:54:08 -07:00
bootstrapComponents : bootstrapComponents ,
2016-07-25 03:02:57 -07:00
schemas : schemas ,
2016-07-18 03:50:31 -07:00
declaredDirectives : declaredDirectives ,
exportedDirectives : exportedDirectives ,
declaredPipes : declaredPipes ,
exportedPipes : exportedPipes ,
importedModules : importedModules ,
exportedModules : exportedModules ,
transitiveModule : transitiveModule
2016-06-28 09:54:42 -07:00
});
2016-07-18 03:50:31 -07:00
transitiveModule . modules . push ( compileMeta );
this . _verifyModule ( compileMeta );
this . _ngModuleCache . set ( moduleType , compileMeta );
2016-06-28 09:54:42 -07:00
}
return compileMeta ;
}
2016-07-18 03:50:31 -07:00
addComponentToModule ( moduleType : Type , compType : Type ) {
const moduleMeta = this . getNgModuleMetadata ( moduleType );
2016-07-25 00:36:30 -07:00
// Collect @Component.directives/pipes/entryComponents into our declared directives/pipes.
2016-07-18 03:50:31 -07:00
const compMeta = this . getDirectiveMetadata ( compType , false );
this . _addDirectiveToModule (
compMeta , moduleMeta . type . runtime , moduleMeta . transitiveModule ,
moduleMeta . declaredDirectives );
2016-07-25 00:36:30 -07:00
moduleMeta . transitiveModule . entryComponents . push ( compMeta . type );
moduleMeta . entryComponents . push ( compMeta . type );
2016-07-18 03:50:31 -07:00
this . _verifyModule ( moduleMeta );
}
private _verifyModule ( moduleMeta : cpl.CompileNgModuleMetadata ) {
moduleMeta . exportedDirectives . forEach (( dirMeta ) => {
if ( ! moduleMeta . transitiveModule . directivesSet . has ( dirMeta . type . runtime )) {
throw new BaseException (
`Can't export directive ${ stringify ( dirMeta . type . runtime ) } from ${ stringify ( moduleMeta . type . runtime ) } as it was neither declared nor imported!` );
2016-02-18 10:53:21 -08:00
}
2016-07-18 03:50:31 -07:00
});
moduleMeta . exportedPipes . forEach (( pipeMeta ) => {
if ( ! moduleMeta . transitiveModule . pipesSet . has ( pipeMeta . type . runtime )) {
throw new BaseException (
`Can't export pipe ${ stringify ( pipeMeta . type . runtime ) } from ${ stringify ( moduleMeta . type . runtime ) } as it was neither declared nor imported!` );
}
});
2016-07-25 00:36:30 -07:00
moduleMeta . entryComponents . forEach (( entryComponentType ) => {
if ( ! moduleMeta . transitiveModule . directivesSet . has ( entryComponentType . runtime )) {
2016-07-27 01:45:20 -07:00
this . _addDirectiveToModule (
this . getDirectiveMetadata ( entryComponentType . runtime ), moduleMeta . type . runtime ,
moduleMeta . transitiveModule , moduleMeta . declaredDirectives );
this . _console . warn (
`NgModule ${ stringify ( moduleMeta . type . runtime ) } uses ${ stringify ( entryComponentType . runtime ) } via "entryComponents" but it was neither declared nor imported! This warning will become an error after final.` );
2016-07-18 03:50:31 -07:00
}
});
2016-07-28 02:50:50 -07:00
// Collect @Component.directives/pipes/entryComponents into our declared
// directives/pipes. Do this last so that directives added by previous steps
// are considered as well!
moduleMeta . declaredDirectives . forEach (
( dirMeta ) => { this . _getTransitiveViewDirectivesAndPipes ( dirMeta , moduleMeta ); });
2016-07-18 03:50:31 -07:00
}
private _addTypeToModule ( type : Type , moduleType : Type ) {
const oldModule = this . _ngModuleOfTypes . get ( type );
if ( oldModule && oldModule !== moduleType ) {
throw new BaseException (
`Type ${ stringify ( type ) } is part of the declarations of 2 modules: ${ stringify ( oldModule ) } and ${ stringify ( moduleType ) } !` );
2016-02-18 10:53:21 -08:00
}
2016-07-18 03:50:31 -07:00
this . _ngModuleOfTypes . set ( type , moduleType );
}
private _getTransitiveViewDirectivesAndPipes (
2016-07-28 02:50:50 -07:00
compMeta : cpl.CompileDirectiveMetadata , moduleMeta : cpl.CompileNgModuleMetadata ) {
2016-07-18 03:50:31 -07:00
if ( ! compMeta . isComponent ) {
return ;
}
const addPipe = ( pipeType : Type ) => {
const pipeMeta = this . getPipeMetadata ( pipeType );
2016-07-28 02:50:50 -07:00
this . _addPipeToModule (
pipeMeta , moduleMeta . type . runtime , moduleMeta . transitiveModule , moduleMeta . declaredPipes );
2016-07-18 03:50:31 -07:00
};
const addDirective = ( dirType : Type ) => {
const dirMeta = this . getDirectiveMetadata ( dirType );
2016-07-28 02:50:50 -07:00
if ( this . _addDirectiveToModule (
dirMeta , moduleMeta . type . runtime , moduleMeta . transitiveModule ,
moduleMeta . declaredDirectives )) {
this . _getTransitiveViewDirectivesAndPipes ( dirMeta , moduleMeta );
2016-07-18 03:50:31 -07:00
}
};
2016-07-28 06:31:26 -07:00
if ( compMeta . viewPipes ) {
compMeta . viewPipes . forEach (( cplType ) => addPipe ( cplType . runtime ));
2016-07-18 03:50:31 -07:00
}
2016-07-28 06:31:26 -07:00
if ( compMeta . viewDirectives ) {
compMeta . viewDirectives . forEach (( cplType ) => addDirective ( cplType . runtime ));
2016-07-18 03:50:31 -07:00
}
2016-07-28 02:50:50 -07:00
compMeta . entryComponents . forEach (( entryComponentType ) => {
if ( ! moduleMeta . transitiveModule . directivesSet . has ( entryComponentType . runtime )) {
this . _console . warn (
`Component ${ stringify ( compMeta . type . runtime ) } in NgModule ${ stringify ( moduleMeta . type . runtime ) } uses ${ stringify ( entryComponentType . runtime ) } via "entryComponents" but it was neither declared nor imported into the module! This warning will become an error after final.` );
addDirective ( entryComponentType . runtime );
}
});
2016-07-18 03:50:31 -07:00
}
private _getTransitiveNgModuleMetadata (
importedModules : cpl.CompileNgModuleMetadata [],
exportedModules : cpl.CompileNgModuleMetadata []) : cpl . TransitiveCompileNgModuleMetadata {
2016-07-25 00:36:30 -07:00
// collect `providers` / `entryComponents` from all imported and all exported modules
2016-07-18 03:50:31 -07:00
const transitiveModules = getTransitiveModules ( importedModules . concat ( exportedModules ), true );
const providers = flattenArray ( transitiveModules . map (( ngModule ) => ngModule . providers ));
2016-07-25 00:36:30 -07:00
const entryComponents =
flattenArray ( transitiveModules . map (( ngModule ) => ngModule . entryComponents ));
2016-07-18 03:50:31 -07:00
const transitiveExportedModules = getTransitiveModules ( importedModules , false );
const directives =
flattenArray ( transitiveExportedModules . map (( ngModule ) => ngModule . exportedDirectives ));
const pipes = flattenArray ( transitiveExportedModules . map (( ngModule ) => ngModule . exportedPipes ));
return new cpl . TransitiveCompileNgModuleMetadata (
2016-07-25 00:36:30 -07:00
transitiveModules , providers , entryComponents , directives , pipes );
2016-07-18 03:50:31 -07:00
}
private _addDirectiveToModule (
dirMeta : cpl.CompileDirectiveMetadata , moduleType : any ,
transitiveModule : cpl.TransitiveCompileNgModuleMetadata ,
declaredDirectives : cpl.CompileDirectiveMetadata [], force : boolean = false ) : boolean {
if ( force || ! transitiveModule . directivesSet . has ( dirMeta . type . runtime )) {
transitiveModule . directivesSet . add ( dirMeta . type . runtime );
transitiveModule . directives . push ( dirMeta );
declaredDirectives . push ( dirMeta );
this . _addTypeToModule ( dirMeta . type . runtime , moduleType );
return true ;
}
return false ;
}
private _addPipeToModule (
pipeMeta : cpl.CompilePipeMetadata , moduleType : any ,
transitiveModule : cpl.TransitiveCompileNgModuleMetadata ,
declaredPipes : cpl.CompilePipeMetadata [], force : boolean = false ) : boolean {
if ( force || ! transitiveModule . pipesSet . has ( pipeMeta . type . runtime )) {
transitiveModule . pipesSet . add ( pipeMeta . type . runtime );
transitiveModule . pipes . push ( pipeMeta );
declaredPipes . push ( pipeMeta );
this . _addTypeToModule ( pipeMeta . type . runtime , moduleType );
return true ;
}
return false ;
2016-02-18 10:53:21 -08:00
}
2016-06-18 18:42:34 +02:00
getTypeMetadata ( type : Type , moduleUrl : string , dependencies : any [] = null ) :
cpl . CompileTypeMetadata {
2016-06-28 09:54:42 -07:00
type = resolveForwardRef ( type );
2016-01-06 14:13:44 -08:00
return new cpl . CompileTypeMetadata ({
2016-04-20 18:10:19 -07:00
name : this.sanitizeTokenName ( type ),
2016-01-06 14:13:44 -08:00
moduleUrl : moduleUrl ,
runtime : type ,
2016-08-02 01:12:24 -07:00
diDeps : this.getDependenciesMetadata ( type , dependencies ),
lifecycleHooks : LIFECYCLE_HOOKS_VALUES.filter ( hook => hasLifecycleHook ( hook , type )),
2016-01-06 14:13:44 -08:00
});
}
2016-06-18 18:42:34 +02:00
getFactoryMetadata ( factory : Function , moduleUrl : string , dependencies : any [] = null ) :
cpl . CompileFactoryMetadata {
2016-06-28 09:54:42 -07:00
factory = resolveForwardRef ( factory );
2016-01-06 14:13:44 -08:00
return new cpl . CompileFactoryMetadata ({
2016-04-20 18:10:19 -07:00
name : this.sanitizeTokenName ( factory ),
2016-01-06 14:13:44 -08:00
moduleUrl : moduleUrl ,
runtime : factory ,
2016-06-18 18:42:34 +02:00
diDeps : this.getDependenciesMetadata ( factory , dependencies )
2016-01-06 14:13:44 -08:00
});
}
2016-07-18 03:50:31 -07:00
getPipeMetadata ( pipeType : Type , throwIfNotFound = true ) : cpl . CompilePipeMetadata {
2016-06-28 09:54:42 -07:00
pipeType = resolveForwardRef ( pipeType );
2015-12-02 10:35:51 -08:00
var meta = this . _pipeCache . get ( pipeType );
if ( isBlank ( meta )) {
2016-07-18 03:50:31 -07:00
var pipeMeta = this . _pipeResolver . resolve ( pipeType , throwIfNotFound );
if ( ! pipeMeta ) {
return null ;
}
2015-12-02 10:35:51 -08:00
meta = new cpl . CompilePipeMetadata ({
2016-02-18 10:53:21 -08:00
type : this . getTypeMetadata ( pipeType , staticTypeModuleUrl ( pipeType )),
2015-12-02 10:35:51 -08:00
name : pipeMeta.name ,
2016-08-02 01:12:24 -07:00
pure : pipeMeta.pure
2015-12-02 10:35:51 -08:00
});
this . _pipeCache . set ( pipeType , meta );
2015-09-14 15:59:09 -07:00
}
return meta ;
}
2016-06-08 16:38:52 -07:00
getDependenciesMetadata ( typeOrFunc : Type | Function , dependencies : any []) :
cpl . CompileDiDependencyMetadata [] {
2016-06-10 01:07:06 +02:00
let hasUnknownDeps = false ;
2016-02-18 10:53:21 -08:00
let params = isPresent ( dependencies ) ? dependencies : this._reflector.parameters ( typeOrFunc );
if ( isBlank ( params )) {
params = [];
2016-01-06 14:13:44 -08:00
}
2016-06-10 01:07:06 +02:00
let dependenciesMetadata : cpl.CompileDiDependencyMetadata [] = params . map (( param ) => {
2016-02-18 10:53:21 -08:00
let isAttribute = false ;
let isHost = false ;
let isSelf = false ;
let isSkipSelf = false ;
let isOptional = false ;
2016-04-28 17:50:03 -07:00
let query : QueryMetadata = null ;
let viewQuery : ViewQueryMetadata = null ;
2016-06-17 10:57:50 -07:00
var token : any = null ;
2016-02-18 10:53:21 -08:00
if ( isArray ( param )) {
2016-06-08 16:38:52 -07:00
(< any [] > param ). forEach (( paramEntry ) => {
if ( paramEntry instanceof HostMetadata ) {
isHost = true ;
} else if ( paramEntry instanceof SelfMetadata ) {
isSelf = true ;
} else if ( paramEntry instanceof SkipSelfMetadata ) {
isSkipSelf = true ;
} else if ( paramEntry instanceof OptionalMetadata ) {
isOptional = true ;
} else if ( paramEntry instanceof AttributeMetadata ) {
isAttribute = true ;
token = paramEntry . attributeName ;
} else if ( paramEntry instanceof QueryMetadata ) {
if ( paramEntry . isViewQuery ) {
viewQuery = paramEntry ;
} else {
query = paramEntry ;
}
} else if ( paramEntry instanceof InjectMetadata ) {
token = paramEntry . token ;
} else if ( isValidType ( paramEntry ) && isBlank ( token )) {
token = paramEntry ;
}
});
2016-02-18 10:53:21 -08:00
} else {
token = param ;
}
if ( isBlank ( token )) {
2016-06-10 01:07:06 +02:00
hasUnknownDeps = true ;
2016-02-18 10:53:21 -08:00
return null ;
2016-01-06 14:13:44 -08:00
}
return new cpl . CompileDiDependencyMetadata ({
isAttribute : isAttribute ,
2016-02-18 10:53:21 -08:00
isHost : isHost ,
isSelf : isSelf ,
isSkipSelf : isSkipSelf ,
isOptional : isOptional ,
2016-06-05 04:46:03 +02:00
query : isPresent ( query ) ? this . getQueryMetadata ( query , null , typeOrFunc ) : null ,
viewQuery : isPresent ( viewQuery ) ? this . getQueryMetadata ( viewQuery , null , typeOrFunc ) : null ,
2016-02-18 10:53:21 -08:00
token : this.getTokenMetadata ( token )
2016-01-06 14:13:44 -08:00
});
2016-02-18 10:53:21 -08:00
2016-01-06 14:13:44 -08:00
});
2016-06-10 01:07:06 +02:00
if ( hasUnknownDeps ) {
2016-06-08 16:38:52 -07:00
let depsTokens =
dependenciesMetadata . map (( dep ) => { return dep ? stringify ( dep . token ) : '?' ; })
. join ( ', ' );
throw new BaseException (
`Can't resolve all parameters for ${ stringify ( typeOrFunc ) } : ( ${ depsTokens } ).` );
2016-06-10 01:07:06 +02:00
}
return dependenciesMetadata ;
2016-01-06 14:13:44 -08:00
}
getTokenMetadata ( token : any ) : cpl . CompileTokenMetadata {
token = resolveForwardRef ( token );
2016-06-08 15:45:15 -07:00
var compileToken : any /** TODO #9100 */ ;
2016-01-06 14:13:44 -08:00
if ( isString ( token )) {
compileToken = new cpl . CompileTokenMetadata ({ value : token });
} else {
2016-04-20 18:10:19 -07:00
compileToken = new cpl . CompileTokenMetadata ({
2016-02-18 10:53:21 -08:00
identifier : new cpl . CompileIdentifierMetadata ({
runtime : token ,
name : this.sanitizeTokenName ( token ),
moduleUrl : staticTypeModuleUrl ( token )
})
2016-04-20 18:10:19 -07:00
});
2016-01-06 14:13:44 -08:00
}
return compileToken ;
}
2016-07-25 00:36:30 -07:00
getProvidersMetadata ( providers : any [], targetEntryComponents : cpl.CompileTypeMetadata []) :
2016-06-08 16:38:52 -07:00
Array < cpl.CompileProviderMetadata | cpl.CompileTypeMetadata | any [] > {
2016-07-07 10:05:55 -07:00
const compileProviders : Array < cpl.CompileProviderMetadata | cpl.CompileTypeMetadata | any [] > = [];
providers . forEach (( provider ) => {
2016-01-06 14:13:44 -08:00
provider = resolveForwardRef ( provider );
2016-07-07 10:05:55 -07:00
if ( isProviderLiteral ( provider )) {
provider = createProvider ( provider );
}
let compileProvider : cpl.CompileProviderMetadata | cpl . CompileTypeMetadata | any [];
2016-01-06 14:13:44 -08:00
if ( isArray ( provider )) {
2016-07-25 00:36:30 -07:00
compileProvider = this . getProvidersMetadata ( provider , targetEntryComponents );
2016-01-06 14:13:44 -08:00
} else if ( provider instanceof Provider ) {
2016-07-07 10:05:55 -07:00
let tokenMeta = this . getTokenMetadata ( provider . token );
2016-07-25 00:36:30 -07:00
if ( tokenMeta . equalsTo ( identifierToken ( Identifiers . ANALYZE_FOR_ENTRY_COMPONENTS ))) {
targetEntryComponents . push (... this . _getEntryComponentsFromProvider ( provider ));
2016-07-07 10:05:55 -07:00
} else {
compileProvider = this . getProviderMetadata ( provider );
}
2016-06-28 09:54:42 -07:00
} else if ( isValidType ( provider )) {
2016-07-07 10:05:55 -07:00
compileProvider = this . getTypeMetadata ( provider , staticTypeModuleUrl ( provider ));
2016-06-28 09:54:42 -07:00
} else {
throw new BaseException (
`Invalid provider - only instances of Provider and Type are allowed, got: ${ stringify ( provider ) } ` );
2016-01-06 14:13:44 -08:00
}
2016-07-07 10:05:55 -07:00
if ( compileProvider ) {
compileProviders . push ( compileProvider );
}
2016-01-06 14:13:44 -08:00
});
2016-07-07 10:05:55 -07:00
return compileProviders ;
}
2016-07-25 00:36:30 -07:00
private _getEntryComponentsFromProvider ( provider : Provider ) : cpl . CompileTypeMetadata [] {
2016-07-07 10:05:55 -07:00
let components : cpl.CompileTypeMetadata [] = [];
let collectedIdentifiers : cpl.CompileIdentifierMetadata [] = [];
if ( provider . useFactory || provider . useExisting || provider . useClass ) {
2016-07-25 00:36:30 -07:00
throw new BaseException ( `The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!` );
2016-07-07 10:05:55 -07:00
}
if ( ! provider . multi ) {
2016-07-25 00:36:30 -07:00
throw new BaseException (
`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!` );
2016-07-07 10:05:55 -07:00
}
convertToCompileValue ( provider . useValue , collectedIdentifiers );
collectedIdentifiers . forEach (( identifier ) => {
2016-07-18 03:50:31 -07:00
let dirMeta = this . getDirectiveMetadata ( identifier . runtime , false );
2016-07-07 10:05:55 -07:00
if ( dirMeta ) {
components . push ( dirMeta . type );
}
});
return components ;
2016-01-06 14:13:44 -08:00
}
getProviderMetadata ( provider : Provider ) : cpl . CompileProviderMetadata {
2016-06-18 18:42:34 +02:00
var compileDeps : cpl.CompileDiDependencyMetadata [];
var compileTypeMetadata : cpl.CompileTypeMetadata = null ;
var compileFactoryMetadata : cpl.CompileFactoryMetadata = null ;
2016-01-06 14:13:44 -08:00
if ( isPresent ( provider . useClass )) {
2016-06-18 18:42:34 +02:00
compileTypeMetadata = this . getTypeMetadata (
provider . useClass , staticTypeModuleUrl ( provider . useClass ), provider . dependencies );
compileDeps = compileTypeMetadata . diDeps ;
2016-01-06 14:13:44 -08:00
} else if ( isPresent ( provider . useFactory )) {
2016-06-18 18:42:34 +02:00
compileFactoryMetadata = this . getFactoryMetadata (
provider . useFactory , staticTypeModuleUrl ( provider . useFactory ), provider . dependencies );
compileDeps = compileFactoryMetadata . diDeps ;
2016-01-06 14:13:44 -08:00
}
2016-06-18 18:42:34 +02:00
2016-01-06 14:13:44 -08:00
return new cpl . CompileProviderMetadata ({
token : this.getTokenMetadata ( provider . token ),
2016-06-18 18:42:34 +02:00
useClass : compileTypeMetadata ,
2016-07-07 10:05:55 -07:00
useValue : convertToCompileValue ( provider . useValue , []),
2016-06-18 18:42:34 +02:00
useFactory : compileFactoryMetadata ,
2016-01-06 14:13:44 -08:00
useExisting : isPresent ( provider . useExisting ) ? this . getTokenMetadata ( provider . useExisting ) :
null ,
deps : compileDeps ,
multi : provider.multi
});
}
2016-06-08 16:38:52 -07:00
getQueriesMetadata (
queries : {[ key : string ] : QueryMetadata }, isViewQuery : boolean ,
directiveType : Type ) : cpl . CompileQueryMetadata [] {
2016-06-22 17:25:42 -07:00
var res : cpl.CompileQueryMetadata [] = [];
StringMapWrapper . forEach ( queries , ( query : QueryMetadata , propertyName : string ) => {
if ( query . isViewQuery === isViewQuery ) {
res . push ( this . getQueryMetadata ( query , propertyName , directiveType ));
}
});
return res ;
2016-01-06 14:13:44 -08:00
}
2016-06-08 16:38:52 -07:00
getQueryMetadata ( q : QueryMetadata , propertyName : string , typeOrFunc : Type | Function ) :
cpl . CompileQueryMetadata {
2016-06-17 10:57:50 -07:00
var selectors : cpl.CompileTokenMetadata [];
2016-01-06 14:13:44 -08:00
if ( q . isVarBindingQuery ) {
selectors = q . varBindings . map ( varName => this . getTokenMetadata ( varName ));
} else {
2016-06-05 04:46:03 +02:00
if ( ! isPresent ( q . selector )) {
2016-06-08 16:38:52 -07:00
throw new BaseException (
`Can't construct a query for the property " ${ propertyName } " of " ${ stringify ( typeOrFunc ) } " since the query selector wasn't defined.` );
2016-06-05 04:46:03 +02:00
}
2016-01-06 14:13:44 -08:00
selectors = [ this . getTokenMetadata ( q . selector )];
}
return new cpl . CompileQueryMetadata ({
selectors : selectors ,
first : q.first ,
descendants : q.descendants ,
2016-04-18 13:24:42 -07:00
propertyName : propertyName ,
read : isPresent ( q . read ) ? this . getTokenMetadata ( q . read ) : null
2016-01-06 14:13:44 -08:00
});
}
2015-09-14 15:59:09 -07:00
}
2016-07-18 03:50:31 -07:00
function getTransitiveModules (
modules : cpl.CompileNgModuleMetadata [], includeImports : boolean ,
targetModules : cpl.CompileNgModuleMetadata [] = [],
visitedModules = new Set < Type >()) : cpl . CompileNgModuleMetadata [] {
modules . forEach (( ngModule ) => {
if ( ! visitedModules . has ( ngModule . type . runtime )) {
visitedModules . add ( ngModule . type . runtime );
const nestedModules = includeImports ?
ngModule . importedModules . concat ( ngModule . exportedModules ) :
ngModule . exportedModules ;
getTransitiveModules ( nestedModules , includeImports , targetModules , visitedModules );
// Add after recursing so imported/exported modules are before the module itself.
// This is important for overwriting providers of imported modules!
targetModules . push ( ngModule );
}
});
return targetModules ;
2015-09-14 15:59:09 -07:00
}
2015-12-02 10:35:51 -08:00
2016-07-18 03:50:31 -07:00
function flattenArray ( tree : any [], out : Array < any > = []) : Array < any > {
if ( tree ) {
for ( var i = 0 ; i < tree . length ; i ++ ) {
var item = resolveForwardRef ( tree [ i ]);
if ( isArray ( item )) {
flattenArray ( item , out );
} else {
out . push ( item );
}
2015-09-14 15:59:09 -07:00
}
}
2016-06-22 14:06:23 -07:00
return out ;
2015-09-14 15:59:09 -07:00
}
2016-06-22 02:27:27 +02:00
function verifyNonBlankProviders (
directiveType : Type , providersTree : any [], providersType : string ) : any [] {
var flat : any [] = [];
var errMsg : string ;
flattenArray ( providersTree , flat );
for ( var i = 0 ; i < flat . length ; i ++ ) {
if ( isBlank ( flat [ i ])) {
errMsg = flat . map ( provider => isBlank ( provider ) ? '?' : stringify ( provider )). join ( ', ' );
throw new BaseException (
`One or more of ${ providersType } for " ${ stringify ( directiveType ) } " were not defined: [ ${ errMsg } ].` );
}
}
return providersTree ;
}
2016-02-18 10:53:21 -08:00
function isValidType ( value : any ) : boolean {
2016-06-28 09:54:42 -07:00
return cpl . isStaticSymbol ( value ) || ( value instanceof Type );
2016-02-18 10:53:21 -08:00
}
function staticTypeModuleUrl ( value : any ) : string {
2016-06-28 09:54:42 -07:00
return cpl . isStaticSymbol ( value ) ? value.filePath : null ;
2015-09-14 15:59:09 -07:00
}
2016-06-08 16:38:52 -07:00
function componentModuleUrl (
reflector : ReflectorReader , type : any , cmpMetadata : ComponentMetadata ) : string {
2016-06-28 09:54:42 -07:00
if ( cpl . isStaticSymbol ( type )) {
2016-05-02 09:38:46 -07:00
return staticTypeModuleUrl ( type );
2016-04-28 21:54:02 -07:00
}
if ( isPresent ( cmpMetadata . moduleId )) {
var moduleId = cmpMetadata . moduleId ;
2015-12-05 02:21:38 -08:00
var scheme = getUrlScheme ( moduleId );
return isPresent ( scheme ) && scheme . length > 0 ? moduleId :
2016-05-01 22:50:37 -07:00
`package: ${ moduleId }${ MODULE_SUFFIX } ` ;
2015-09-14 15:59:09 -07:00
}
2016-04-28 21:54:02 -07:00
return reflector . importUri ( type );
2015-09-14 15:59:09 -07:00
}
2016-04-30 16:13:03 -07:00
2016-07-07 10:05:55 -07:00
function convertToCompileValue (
value : any , targetIdentifiers : cpl.CompileIdentifierMetadata []) : any {
return visitValue ( value , new _CompileValueConverter (), targetIdentifiers );
2016-04-30 16:13:03 -07:00
}
class _CompileValueConverter extends ValueTransformer {
2016-07-07 10:05:55 -07:00
visitOther ( value : any , targetIdentifiers : cpl.CompileIdentifierMetadata []) : any {
let identifier : cpl.CompileIdentifierMetadata ;
2016-06-28 09:54:42 -07:00
if ( cpl . isStaticSymbol ( value )) {
2016-07-07 10:05:55 -07:00
identifier = new cpl . CompileIdentifierMetadata (
{ name : value.name , moduleUrl : value.filePath , runtime : value });
2016-04-30 16:13:03 -07:00
} else {
2016-07-07 10:05:55 -07:00
identifier = new cpl . CompileIdentifierMetadata ({ runtime : value });
2016-04-30 16:13:03 -07:00
}
2016-07-07 10:05:55 -07:00
targetIdentifiers . push ( identifier );
return identifier ;
2016-04-30 16:13:03 -07:00
}
}