2015-09-14 18:59:09 -04:00
|
|
|
import {resolveForwardRef} from 'angular2/src/core/di';
|
|
|
|
import {
|
|
|
|
Type,
|
|
|
|
isBlank,
|
|
|
|
isPresent,
|
|
|
|
isArray,
|
|
|
|
stringify,
|
|
|
|
RegExpWrapper
|
|
|
|
} from 'angular2/src/core/facade/lang';
|
|
|
|
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
2015-09-18 13:33:23 -04:00
|
|
|
import {MapWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
2015-09-14 18:59:09 -04:00
|
|
|
import * as cpl from './directive_metadata';
|
|
|
|
import * as dirAnn from 'angular2/src/core/metadata/directives';
|
|
|
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
|
|
|
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
|
|
|
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
|
|
|
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
|
2015-09-18 13:33:23 -04:00
|
|
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler/interfaces';
|
2015-09-14 18:59:09 -04:00
|
|
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
|
|
|
import {Injectable} from 'angular2/src/core/di';
|
|
|
|
|
|
|
|
// group 1: "property" from "[property]"
|
|
|
|
// group 2: "event" from "(event)"
|
|
|
|
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class RuntimeMetadataResolver {
|
|
|
|
private _directiveCounter = 0;
|
2015-09-18 13:33:23 -04:00
|
|
|
private _cache: Map<Type, cpl.CompileDirectiveMetadata> = new Map();
|
2015-09-14 18:59:09 -04:00
|
|
|
|
|
|
|
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {}
|
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
getMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
2015-09-14 18:59:09 -04:00
|
|
|
var meta = this._cache.get(directiveType);
|
|
|
|
if (isBlank(meta)) {
|
|
|
|
var directiveAnnotation = this._directiveResolver.resolve(directiveType);
|
|
|
|
var moduleId = calcModuleId(directiveType, directiveAnnotation);
|
|
|
|
var templateMeta = null;
|
|
|
|
var changeDetectionStrategy = null;
|
|
|
|
|
|
|
|
if (directiveAnnotation instanceof dirAnn.ComponentMetadata) {
|
|
|
|
var compAnnotation = <dirAnn.ComponentMetadata>directiveAnnotation;
|
|
|
|
var viewAnnotation = this._viewResolver.resolve(directiveType);
|
2015-09-18 13:33:23 -04:00
|
|
|
templateMeta = new cpl.CompileTemplateMetadata({
|
2015-09-14 18:59:09 -04:00
|
|
|
encapsulation: viewAnnotation.encapsulation,
|
|
|
|
template: viewAnnotation.template,
|
|
|
|
templateUrl: viewAnnotation.templateUrl,
|
|
|
|
styles: viewAnnotation.styles,
|
2015-09-18 13:33:23 -04:00
|
|
|
styleUrls: viewAnnotation.styleUrls
|
2015-09-14 18:59:09 -04:00
|
|
|
});
|
|
|
|
changeDetectionStrategy = compAnnotation.changeDetection;
|
|
|
|
}
|
2015-09-18 13:33:23 -04:00
|
|
|
meta = cpl.CompileDirectiveMetadata.create({
|
2015-09-14 18:59:09 -04:00
|
|
|
selector: directiveAnnotation.selector,
|
2015-09-18 13:33:23 -04:00
|
|
|
exportAs: directiveAnnotation.exportAs,
|
2015-09-14 18:59:09 -04:00
|
|
|
isComponent: isPresent(templateMeta),
|
2015-09-18 13:33:23 -04:00
|
|
|
dynamicLoadable: true,
|
|
|
|
type: new cpl.CompileTypeMetadata({
|
2015-09-14 18:59:09 -04:00
|
|
|
id: this._directiveCounter++,
|
|
|
|
name: stringify(directiveType),
|
|
|
|
moduleId: moduleId,
|
|
|
|
runtime: directiveType
|
|
|
|
}),
|
|
|
|
template: templateMeta,
|
2015-09-18 13:33:23 -04:00
|
|
|
changeDetection: changeDetectionStrategy,
|
|
|
|
properties: directiveAnnotation.properties,
|
|
|
|
events: directiveAnnotation.events,
|
|
|
|
host: directiveAnnotation.host,
|
|
|
|
lifecycleHooks: ListWrapper.filter(LIFECYCLE_HOOKS_VALUES,
|
|
|
|
hook => hasLifecycleHook(hook, directiveType))
|
2015-09-14 18:59:09 -04:00
|
|
|
});
|
|
|
|
this._cache.set(directiveType, meta);
|
|
|
|
}
|
|
|
|
return meta;
|
|
|
|
}
|
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
getViewDirectivesMetadata(component: Type): cpl.CompileDirectiveMetadata[] {
|
2015-09-14 18:59:09 -04:00
|
|
|
var view = this._viewResolver.resolve(component);
|
|
|
|
var directives = flattenDirectives(view);
|
|
|
|
for (var i = 0; i < directives.length; i++) {
|
|
|
|
if (!isValidDirective(directives[i])) {
|
|
|
|
throw new BaseException(
|
|
|
|
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return removeDuplicatedDirectives(directives.map(type => this.getMetadata(type)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-18 13:33:23 -04:00
|
|
|
function removeDuplicatedDirectives(directives: cpl.CompileDirectiveMetadata[]):
|
|
|
|
cpl.CompileDirectiveMetadata[] {
|
|
|
|
var directivesMap: Map<number, cpl.CompileDirectiveMetadata> = new Map();
|
2015-09-14 18:59:09 -04:00
|
|
|
directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); });
|
|
|
|
return MapWrapper.values(directivesMap);
|
|
|
|
}
|
|
|
|
|
|
|
|
function flattenDirectives(view: ViewMetadata): Type[] {
|
|
|
|
if (isBlank(view.directives)) return [];
|
|
|
|
var directives = [];
|
|
|
|
flattenList(view.directives, directives);
|
|
|
|
return directives;
|
|
|
|
}
|
|
|
|
|
|
|
|
function flattenList(tree: any[], out: Array<Type | any[]>): void {
|
|
|
|
for (var i = 0; i < tree.length; i++) {
|
|
|
|
var item = resolveForwardRef(tree[i]);
|
|
|
|
if (isArray(item)) {
|
|
|
|
flattenList(item, out);
|
|
|
|
} else {
|
|
|
|
out.push(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function isValidDirective(value: Type): boolean {
|
|
|
|
return isPresent(value) && (value instanceof Type);
|
|
|
|
}
|
|
|
|
|
|
|
|
function calcModuleId(type: Type, directiveAnnotation: dirAnn.DirectiveMetadata): string {
|
|
|
|
if (isPresent(directiveAnnotation.moduleId)) {
|
|
|
|
return directiveAnnotation.moduleId;
|
|
|
|
} else {
|
|
|
|
return reflector.moduleId(type);
|
|
|
|
}
|
|
|
|
}
|