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-07-18 03:50:31 -07:00
import { AnimationAnimateMetadata , AnimationEntryMetadata , AnimationGroupMetadata , AnimationKeyframesSequenceMetadata , AnimationMetadata , AnimationStateDeclarationMetadata , AnimationStateMetadata , AnimationStateTransitionMetadata , AnimationStyleMetadata , AnimationWithStepsMetadata , AttributeMetadata , ChangeDetectionStrategy , ComponentMetadata , HostMetadata , Inject , InjectMetadata , Injectable , NgModule , NgModuleMetadata , Optional , OptionalMetadata , Provider , QueryMetadata , 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' ;
import { MapWrapper , StringMapWrapper } from '../src/facade/collection' ;
2016-04-28 17:50:03 -07:00
import { BaseException } from '../src/facade/exceptions' ;
2016-06-08 16:38:52 -07:00
import { Type , isArray , isBlank , isPresent , isString , isStringMap , stringify } from '../src/facade/lang' ;
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-06-08 16:38:52 -07:00
import { hasLifecycleHook } from './directive_lifecycle_reflector' ;
2016-01-06 14:13:44 -08:00
import { DirectiveResolver } from './directive_resolver' ;
2016-07-07 10:05:55 -07:00
import { Identifiers , identifierToken } from './identifiers' ;
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-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' ;
import { ViewResolver } from './view_resolver' ;
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 ,
private _pipeResolver : PipeResolver , private _viewResolver : ViewResolver ,
private _config : CompilerConfig , private _console : Console ,
2016-06-17 10:57:50 -07:00
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-06-22 14:06:23 -07:00
var precompileTypes : cpl.CompileTypeMetadata [] = [];
2016-04-28 17:50:03 -07:00
if ( dirMeta instanceof ComponentMetadata ) {
var cmpMeta = < ComponentMetadata > dirMeta ;
2015-10-23 15:55:48 -07:00
var viewMeta = this . _viewResolver . resolve ( directiveType );
2016-03-09 14:55:27 -08:00
assertArrayOfStrings ( 'styles' , viewMeta . styles );
2016-06-20 09:52:41 -07:00
assertInterpolationSymbols ( 'interpolation' , viewMeta . interpolation );
2016-06-08 16:38:52 -07:00
var animations = isPresent ( viewMeta . animations ) ?
viewMeta . animations . map ( e => this . getAnimationEntryMetadata ( e )) :
null ;
2016-06-24 08:46:43 -07:00
assertArrayOfStrings ( 'styles' , viewMeta . styles );
assertArrayOfStrings ( 'styleUrls' , viewMeta . styleUrls );
2016-05-25 12:46:22 -07:00
2015-09-18 10:33:23 -07:00
templateMeta = new cpl . CompileTemplateMetadata ({
2015-10-23 15:55:48 -07:00
encapsulation : viewMeta.encapsulation ,
template : viewMeta.template ,
templateUrl : viewMeta.templateUrl ,
styles : viewMeta.styles ,
2016-05-25 12:46:22 -07:00
styleUrls : viewMeta.styleUrls ,
2016-06-20 09:52:41 -07:00
animations : animations ,
interpolation : viewMeta.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-06-22 14:06:23 -07:00
if ( cmpMeta . precompile ) {
precompileTypes = flattenArray ( cmpMeta . precompile )
. map (( cmp ) => this . getTypeMetadata ( cmp , staticTypeModuleUrl ( cmp )));
}
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' ),
precompileTypes );
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 ({
2015-10-23 15:55:48 -07:00
selector : dirMeta.selector ,
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
lifecycleHooks :
LIFECYCLE_HOOKS_VALUES.filter ( hook => hasLifecycleHook ( hook , directiveType )),
providers : providers ,
viewProviders : viewProviders ,
queries : queries ,
2016-06-22 14:06:23 -07:00
viewQueries : viewQueries ,
precompile : precompileTypes
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 [] = [];
if ( meta . imports ) {
flattenArray ( meta . imports ). forEach (( importedType ) => {
if ( ! isValidType ( importedType )) {
throw new BaseException (
`Unexpected value ' ${ stringify ( importedType ) } ' imported by the module ' ${ stringify ( moduleType ) } '` );
}
let importedModuleMeta : cpl.CompileNgModuleMetadata ;
if ( importedModuleMeta = this . getNgModuleMetadata ( importedType , false )) {
importedModules . push ( importedModuleMeta );
} 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 );
// Collect @Component.directives/pipes/precompile into our declared directives/pipes.
this . _getTransitiveViewDirectivesAndPipes (
declaredDirMeta , moduleType , transitiveModule , declaredDirectives , declaredPipes );
} 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 ) } '` );
}
});
}
const providers : any [] = [];
const precompile : cpl.CompileTypeMetadata [] = [];
2016-06-30 13:07:17 -07:00
if ( meta . providers ) {
2016-07-07 10:05:55 -07:00
providers . push (... this . getProvidersMetadata ( meta . providers , precompile ));
2016-06-30 13:07:17 -07:00
}
if ( meta . precompile ) {
precompile . push (... flattenArray ( meta . precompile )
. map ( type => this . getTypeMetadata ( type , staticTypeModuleUrl ( type ))));
}
2016-07-18 03:50:31 -07:00
transitiveModule . precompile . push (... precompile );
transitiveModule . providers . push (... providers );
compileMeta = new cpl . CompileNgModuleMetadata ({
2016-06-28 09:54:42 -07:00
type : this . getTypeMetadata ( moduleType , staticTypeModuleUrl ( moduleType )),
providers : providers ,
precompile : precompile ,
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 );
// Collect @Component.directives/pipes/precompile into our declared directives/pipes.
const compMeta = this . getDirectiveMetadata ( compType , false );
this . _addDirectiveToModule (
compMeta , moduleMeta . type . runtime , moduleMeta . transitiveModule ,
moduleMeta . declaredDirectives );
this . _getTransitiveViewDirectivesAndPipes (
compMeta , moduleMeta . type . runtime , moduleMeta . transitiveModule ,
moduleMeta . declaredDirectives , moduleMeta . declaredPipes );
moduleMeta . transitiveModule . precompile . push ( compMeta . type );
moduleMeta . precompile . push ( compMeta . type );
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!` );
}
});
moduleMeta . declaredDirectives . forEach (( dirMeta ) => {
dirMeta . precompile . forEach (( precompileComp ) => {
if ( ! moduleMeta . transitiveModule . directivesSet . has ( precompileComp . runtime )) {
throw new BaseException (
`Component ${ stringify ( dirMeta . type . runtime ) } in NgModule ${ stringify ( moduleMeta . type . runtime ) } uses ${ stringify ( precompileComp . runtime ) } via "precompile" but it was neither declared nor imported into the module!` );
}
});
});
moduleMeta . precompile . forEach (( precompileType ) => {
if ( ! moduleMeta . transitiveModule . directivesSet . has ( precompileType . runtime )) {
throw new BaseException (
`NgModule ${ stringify ( moduleMeta . type . runtime ) } uses ${ stringify ( precompileType . runtime ) } via "precompile" but it was neither declared nor imported!` );
}
});
}
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 (
compMeta : cpl.CompileDirectiveMetadata , moduleType : any ,
transitiveModule : cpl.TransitiveCompileNgModuleMetadata ,
declaredDirectives : cpl.CompileDirectiveMetadata [],
declaredPipes : cpl.CompilePipeMetadata []) {
if ( ! compMeta . isComponent ) {
return ;
}
const addPipe = ( pipeType : Type ) => {
if ( ! pipeType ) {
throw new BaseException (
`Unexpected pipe value ' ${ pipeType } ' on the View of component ' ${ stringify ( compMeta . type . runtime ) } '` );
}
const pipeMeta = this . getPipeMetadata ( pipeType );
this . _addPipeToModule ( pipeMeta , moduleType , transitiveModule , declaredPipes );
};
const addDirective = ( dirType : Type ) => {
if ( ! dirType ) {
throw new BaseException (
`Unexpected directive value ' ${ dirType } ' on the View of component ' ${ stringify ( compMeta . type . runtime ) } '` );
}
const dirMeta = this . getDirectiveMetadata ( dirType );
if ( this . _addDirectiveToModule ( dirMeta , moduleType , transitiveModule , declaredDirectives )) {
this . _getTransitiveViewDirectivesAndPipes (
dirMeta , moduleType , transitiveModule , declaredDirectives , declaredPipes );
}
};
const view = this . _viewResolver . resolve ( compMeta . type . runtime );
if ( view . pipes ) {
flattenArray ( view . pipes ). forEach ( addPipe );
}
if ( view . directives ) {
flattenArray ( view . directives ). forEach ( addDirective );
}
}
private _getTransitiveNgModuleMetadata (
importedModules : cpl.CompileNgModuleMetadata [],
exportedModules : cpl.CompileNgModuleMetadata []) : cpl . TransitiveCompileNgModuleMetadata {
// collect `providers` / `precompile` from all imported and all exported modules
const transitiveModules = getTransitiveModules ( importedModules . concat ( exportedModules ), true );
const providers = flattenArray ( transitiveModules . map (( ngModule ) => ngModule . providers ));
const precompile = flattenArray ( transitiveModules . map (( ngModule ) => ngModule . precompile ));
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 (
transitiveModules , providers , precompile , directives , pipes );
}
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-06-18 18:42:34 +02:00
diDeps : this.getDependenciesMetadata ( type , dependencies )
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-01-06 14:13:44 -08:00
pure : pipeMeta.pure ,
lifecycleHooks : LIFECYCLE_HOOKS_VALUES.filter ( hook => hasLifecycleHook ( hook , pipeType )),
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-07 10:05:55 -07:00
getProvidersMetadata ( providers : any [], targetPrecompileComponents : 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-07 10:05:55 -07:00
compileProvider = this . getProvidersMetadata ( provider , targetPrecompileComponents );
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 );
if ( tokenMeta . equalsTo ( identifierToken ( Identifiers . ANALYZE_FOR_PRECOMPILE ))) {
targetPrecompileComponents . push (... this . getPrecompileComponentsFromProvider ( provider ));
} 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 ;
}
getPrecompileComponentsFromProvider ( provider : Provider ) : cpl . CompileTypeMetadata [] {
let components : cpl.CompileTypeMetadata [] = [];
let collectedIdentifiers : cpl.CompileIdentifierMetadata [] = [];
if ( provider . useFactory || provider . useExisting || provider . useClass ) {
throw new BaseException ( `The ANALYZE_FOR_PRECOMPILE token only supports useValue!` );
}
if ( ! provider . multi ) {
throw new BaseException ( `The ANALYZE_FOR_PRECOMPILE token only supports 'multi = true'!` );
}
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
}
}