UNPKG

angular2

Version:

Angular 2 - a web framework for modern web apps

401 lines (400 loc) 19.1 kB
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'; }