import {resolveForwardRef} from 'angular2/src/core/di'; import { Type, isBlank, isPresent, isArray, stringify, RegExpWrapper } from 'angular2/src/facade/lang'; import {BaseException} from 'angular2/src/facade/exceptions'; import * as cpl from './directive_metadata'; import * as md from 'angular2/src/core/metadata/directives'; import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver'; import {PipeResolver} from 'angular2/src/core/linker/pipe_resolver'; import {ViewResolver} from 'angular2/src/core/linker/view_resolver'; import {ViewMetadata} from 'angular2/src/core/metadata/view'; import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector'; import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces'; import {reflector} from 'angular2/src/core/reflection/reflection'; import {Injectable, Inject, Optional} from 'angular2/src/core/di'; import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes'; import {MODULE_SUFFIX} from './util'; import {assertArrayOfStrings} from './assertions'; import {getUrlScheme} from 'angular2/src/compiler/url_resolver'; @Injectable() export class RuntimeMetadataResolver { private _directiveCache = new Map(); private _pipeCache = new Map(); constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver, private _viewResolver: ViewResolver, @Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[], @Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Type[]) {} getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata { var meta = this._directiveCache.get(directiveType); if (isBlank(meta)) { var dirMeta = this._directiveResolver.resolve(directiveType); var moduleUrl = null; var templateMeta = null; var changeDetectionStrategy = null; if (dirMeta instanceof md.ComponentMetadata) { assertArrayOfStrings('styles', dirMeta.styles); var cmpMeta = dirMeta; moduleUrl = calcModuleUrl(directiveType, cmpMeta); var viewMeta = this._viewResolver.resolve(directiveType); assertArrayOfStrings('styles', viewMeta.styles); templateMeta = new cpl.CompileTemplateMetadata({ encapsulation: viewMeta.encapsulation, template: viewMeta.template, templateUrl: viewMeta.templateUrl, styles: viewMeta.styles, styleUrls: viewMeta.styleUrls }); changeDetectionStrategy = cmpMeta.changeDetection; } meta = cpl.CompileDirectiveMetadata.create({ selector: dirMeta.selector, exportAs: dirMeta.exportAs, isComponent: isPresent(templateMeta), dynamicLoadable: true, type: new cpl.CompileTypeMetadata( {name: stringify(directiveType), moduleUrl: moduleUrl, runtime: directiveType}), template: templateMeta, changeDetection: changeDetectionStrategy, inputs: dirMeta.inputs, outputs: dirMeta.outputs, host: dirMeta.host, lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType)) }); this._directiveCache.set(directiveType, meta); } return meta; } getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata { var meta = this._pipeCache.get(pipeType); if (isBlank(meta)) { var pipeMeta = this._pipeResolver.resolve(pipeType); var moduleUrl = reflector.importUri(pipeType); meta = new cpl.CompilePipeMetadata({ type: new cpl.CompileTypeMetadata( {name: stringify(pipeType), moduleUrl: moduleUrl, runtime: pipeType}), name: pipeMeta.name, pure: pipeMeta.pure }); this._pipeCache.set(pipeType, meta); } return meta; } getViewDirectivesMetadata(component: Type): cpl.CompileDirectiveMetadata[] { var view = this._viewResolver.resolve(component); var directives = flattenDirectives(view, this._platformDirectives); for (var i = 0; i < directives.length; i++) { if (!isValidType(directives[i])) { throw new BaseException( `Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`); } } return directives.map(type => this.getDirectiveMetadata(type)); } getViewPipesMetadata(component: Type): cpl.CompilePipeMetadata[] { var view = this._viewResolver.resolve(component); var pipes = flattenPipes(view, this._platformPipes); for (var i = 0; i < pipes.length; i++) { if (!isValidType(pipes[i])) { throw new BaseException( `Unexpected piped value '${stringify(pipes[i])}' on the View of component '${stringify(component)}'`); } } return pipes.map(type => this.getPipeMetadata(type)); } } function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[] { let directives = []; if (isPresent(platformDirectives)) { flattenArray(platformDirectives, directives); } if (isPresent(view.directives)) { flattenArray(view.directives, directives); } return directives; } function flattenPipes(view: ViewMetadata, platformPipes: any[]): Type[] { let pipes = []; if (isPresent(platformPipes)) { flattenArray(platformPipes, pipes); } if (isPresent(view.pipes)) { flattenArray(view.pipes, pipes); } return pipes; } function flattenArray(tree: any[], out: Array): void { for (var i = 0; i < tree.length; i++) { var item = resolveForwardRef(tree[i]); if (isArray(item)) { flattenArray(item, out); } else { out.push(item); } } } function isValidType(value: Type): boolean { return isPresent(value) && (value instanceof Type); } function calcModuleUrl(type: Type, cmpMetadata: md.ComponentMetadata): string { var moduleId = cmpMetadata.moduleId; if (isPresent(moduleId)) { var scheme = getUrlScheme(moduleId); return isPresent(scheme) && scheme.length > 0 ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`; } else { return reflector.importUri(type); } }