angular2
Version:
Angular 2 - a web framework for modern web apps
401 lines (400 loc) • 19.1 kB
JavaScript
import { StringMapWrapper } from 'angular2/src/facade/collection';
import { isArray, isPresent, isPrimitive } from 'angular2/src/facade/lang';
import { AttributeMetadata, DirectiveMetadata, ComponentMetadata, ContentChildrenMetadata, ContentChildMetadata, InputMetadata, HostBindingMetadata, HostListenerMetadata, OutputMetadata, PipeMetadata, ViewMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata, QueryMetadata } from 'angular2/src/core/metadata';
/**
* A token representing the a reference to a static type.
*
* This token is unique for a moduleId and name and can be used as a hash table key.
*/
export class StaticType {
constructor(moduleId, name) {
this.moduleId = moduleId;
this.name = name;
}
}
/**
* A static reflector implements enough of the Reflector API that is necessary to compile
* templates statically.
*/
export class StaticReflector {
constructor(host) {
this.host = host;
this.typeCache = new Map();
this.annotationCache = new Map();
this.propertyCache = new Map();
this.parameterCache = new Map();
this.metadataCache = new Map();
this.conversionMap = new Map();
this.initializeConversionMap();
}
importUri(typeOrFunc) { return typeOrFunc.moduleId; }
/**
* getStaticType produces a Type whose metadata is known but whose implementation is not loaded.
* All types passed to the StaticResolver should be pseudo-types returned by this method.
*
* @param moduleId the module identifier as an absolute path.
* @param name the name of the type.
*/
getStaticType(moduleId, name) {
let key = `"${moduleId}".${name}`;
let result = this.typeCache.get(key);
if (!isPresent(result)) {
result = new StaticType(moduleId, name);
this.typeCache.set(key, result);
}
return result;
}
annotations(type) {
let annotations = this.annotationCache.get(type);
if (!isPresent(annotations)) {
let classMetadata = this.getTypeMetadata(type);
if (isPresent(classMetadata['decorators'])) {
annotations = classMetadata['decorators']
.map(decorator => this.convertKnownDecorator(type.moduleId, decorator))
.filter(decorator => isPresent(decorator));
}
else {
annotations = [];
}
this.annotationCache.set(type, annotations);
}
return annotations;
}
propMetadata(type) {
let propMetadata = this.propertyCache.get(type);
if (!isPresent(propMetadata)) {
let classMetadata = this.getTypeMetadata(type);
propMetadata = this.getPropertyMetadata(type.moduleId, classMetadata['members']);
if (!isPresent(propMetadata)) {
propMetadata = {};
}
this.propertyCache.set(type, propMetadata);
}
return propMetadata;
}
parameters(type) {
let parameters = this.parameterCache.get(type);
if (!isPresent(parameters)) {
let classMetadata = this.getTypeMetadata(type);
if (isPresent(classMetadata)) {
let members = classMetadata['members'];
if (isPresent(members)) {
let ctorData = members['__ctor__'];
if (isPresent(ctorData)) {
let ctor = ctorData.find(a => a['__symbolic'] === 'constructor');
parameters = this.simplify(type.moduleId, ctor['parameters']);
}
}
}
if (!isPresent(parameters)) {
parameters = [];
}
this.parameterCache.set(type, parameters);
}
return parameters;
}
initializeConversionMap() {
let core_metadata = this.host.resolveModule('angular2/src/core/metadata');
let conversionMap = this.conversionMap;
conversionMap.set(this.getStaticType(core_metadata, 'Directive'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
if (!isPresent(p0)) {
p0 = {};
}
return new DirectiveMetadata({
selector: p0['selector'],
inputs: p0['inputs'],
outputs: p0['outputs'],
events: p0['events'],
host: p0['host'],
bindings: p0['bindings'],
providers: p0['providers'],
exportAs: p0['exportAs'],
queries: p0['queries'],
});
});
conversionMap.set(this.getStaticType(core_metadata, 'Component'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
if (!isPresent(p0)) {
p0 = {};
}
return new ComponentMetadata({
selector: p0['selector'],
inputs: p0['inputs'],
outputs: p0['outputs'],
properties: p0['properties'],
events: p0['events'],
host: p0['host'],
exportAs: p0['exportAs'],
moduleId: p0['moduleId'],
bindings: p0['bindings'],
providers: p0['providers'],
viewBindings: p0['viewBindings'],
viewProviders: p0['viewProviders'],
changeDetection: p0['changeDetection'],
queries: p0['queries'],
templateUrl: p0['templateUrl'],
template: p0['template'],
styleUrls: p0['styleUrls'],
styles: p0['styles'],
directives: p0['directives'],
pipes: p0['pipes'],
encapsulation: p0['encapsulation']
});
});
conversionMap.set(this.getStaticType(core_metadata, 'Input'), (moduleContext, expression) => new InputMetadata(this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'Output'), (moduleContext, expression) => new OutputMetadata(this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'View'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
if (!isPresent(p0)) {
p0 = {};
}
return new ViewMetadata({
templateUrl: p0['templateUrl'],
template: p0['template'],
directives: p0['directives'],
pipes: p0['pipes'],
encapsulation: p0['encapsulation'],
styles: p0['styles'],
});
});
conversionMap.set(this.getStaticType(core_metadata, 'Attribute'), (moduleContext, expression) => new AttributeMetadata(this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'Query'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
let p1 = this.getDecoratorParameter(moduleContext, expression, 1);
if (!isPresent(p1)) {
p1 = {};
}
return new QueryMetadata(p0, { descendants: p1.descendants, first: p1.first });
});
conversionMap.set(this.getStaticType(core_metadata, 'ContentChildren'), (moduleContext, expression) => new ContentChildrenMetadata(this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'ContentChild'), (moduleContext, expression) => new ContentChildMetadata(this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'ViewChildren'), (moduleContext, expression) => new ViewChildrenMetadata(this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'ViewChild'), (moduleContext, expression) => new ViewChildMetadata(this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'ViewQuery'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
let p1 = this.getDecoratorParameter(moduleContext, expression, 1);
if (!isPresent(p1)) {
p1 = {};
}
return new ViewQueryMetadata(p0, {
descendants: p1['descendants'],
first: p1['first'],
});
});
conversionMap.set(this.getStaticType(core_metadata, 'Pipe'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
if (!isPresent(p0)) {
p0 = {};
}
return new PipeMetadata({
name: p0['name'],
pure: p0['pure'],
});
});
conversionMap.set(this.getStaticType(core_metadata, 'HostBinding'), (moduleContext, expression) => new HostBindingMetadata(this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'HostListener'), (moduleContext, expression) => new HostListenerMetadata(this.getDecoratorParameter(moduleContext, expression, 0), this.getDecoratorParameter(moduleContext, expression, 1)));
return null;
}
convertKnownDecorator(moduleContext, expression) {
let converter = this.conversionMap.get(this.getDecoratorType(moduleContext, expression));
if (isPresent(converter))
return converter(moduleContext, expression);
return null;
}
getDecoratorType(moduleContext, expression) {
if (isMetadataSymbolicCallExpression(expression)) {
let target = expression['expression'];
if (isMetadataSymbolicReferenceExpression(target)) {
let moduleId = this.host.resolveModule(target['module'], moduleContext);
return this.getStaticType(moduleId, target['name']);
}
}
return null;
}
getDecoratorParameter(moduleContext, expression, index) {
if (isMetadataSymbolicCallExpression(expression) && isPresent(expression['arguments']) &&
expression['arguments'].length <= index + 1) {
return this.simplify(moduleContext, expression['arguments'][index]);
}
return null;
}
getPropertyMetadata(moduleContext, value) {
if (isPresent(value)) {
let result = {};
StringMapWrapper.forEach(value, (value, name) => {
let data = this.getMemberData(moduleContext, value);
if (isPresent(data)) {
let propertyData = data.filter(d => d['kind'] == "property")
.map(d => d['directives'])
.reduce((p, c) => p.concat(c), []);
if (propertyData.length != 0) {
StringMapWrapper.set(result, name, propertyData);
}
}
});
return result;
}
return {};
}
// clang-format off
getMemberData(moduleContext, member) {
// clang-format on
let result = [];
if (isPresent(member)) {
for (let item of member) {
result.push({
kind: item['__symbolic'],
directives: isPresent(item['decorators']) ?
item['decorators']
.map(decorator => this.convertKnownDecorator(moduleContext, decorator))
.filter(d => isPresent(d)) :
null
});
}
}
return result;
}
/** @internal */
simplify(moduleContext, value) {
let _this = this;
function simplify(expression) {
if (isPrimitive(expression)) {
return expression;
}
if (isArray(expression)) {
let result = [];
for (let item of expression) {
result.push(simplify(item));
}
return result;
}
if (isPresent(expression)) {
if (isPresent(expression['__symbolic'])) {
switch (expression['__symbolic']) {
case "binop":
let left = simplify(expression['left']);
let right = simplify(expression['right']);
switch (expression['operator']) {
case '&&':
return left && right;
case '||':
return left || right;
case '|':
return left | right;
case '^':
return left ^ right;
case '&':
return left & right;
case '==':
return left == right;
case '!=':
return left != right;
case '===':
return left === right;
case '!==':
return left !== right;
case '<':
return left < right;
case '>':
return left > right;
case '<=':
return left <= right;
case '>=':
return left >= right;
case '<<':
return left << right;
case '>>':
return left >> right;
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
case '%':
return left % right;
}
return null;
case "pre":
let operand = simplify(expression['operand']);
switch (expression['operator']) {
case '+':
return operand;
case '-':
return -operand;
case '!':
return !operand;
case '~':
return ~operand;
}
return null;
case "index":
let indexTarget = simplify(expression['expression']);
let index = simplify(expression['index']);
if (isPresent(indexTarget) && isPrimitive(index))
return indexTarget[index];
return null;
case "select":
let selectTarget = simplify(expression['expression']);
let member = simplify(expression['member']);
if (isPresent(selectTarget) && isPrimitive(member))
return selectTarget[member];
return null;
case "reference":
let referenceModuleName = _this.host.resolveModule(expression['module'], moduleContext);
let referenceModule = _this.getModuleMetadata(referenceModuleName);
let referenceValue = referenceModule['metadata'][expression['name']];
if (isClassMetadata(referenceValue)) {
// Convert to a pseudo type
return _this.getStaticType(referenceModuleName, expression['name']);
}
return _this.simplify(referenceModuleName, referenceValue);
case "call":
return null;
}
return null;
}
let result = {};
StringMapWrapper.forEach(expression, (value, name) => { result[name] = simplify(value); });
return result;
}
return null;
}
return simplify(value);
}
/**
* @param module an absolute path to a module file.
*/
getModuleMetadata(module) {
let moduleMetadata = this.metadataCache.get(module);
if (!isPresent(moduleMetadata)) {
moduleMetadata = this.host.getMetadataFor(module);
if (!isPresent(moduleMetadata)) {
moduleMetadata = { __symbolic: "module", module: module, metadata: {} };
}
this.metadataCache.set(module, moduleMetadata);
}
return moduleMetadata;
}
getTypeMetadata(type) {
let moduleMetadata = this.getModuleMetadata(type.moduleId);
let result = moduleMetadata['metadata'][type.name];
if (!isPresent(result)) {
result = { __symbolic: "class" };
}
return result;
}
}
function isMetadataSymbolicCallExpression(expression) {
return !isPrimitive(expression) && !isArray(expression) && expression['__symbolic'] == 'call';
}
function isMetadataSymbolicReferenceExpression(expression) {
return !isPrimitive(expression) && !isArray(expression) &&
expression['__symbolic'] == 'reference';
}
function isClassMetadata(expression) {
return !isPrimitive(expression) && !isArray(expression) && expression['__symbolic'] == 'class';
}