2015-09-14 18:59:09 -04:00
|
|
|
import {resolveForwardRef} from 'angular2/src/core/di';
|
2016-04-07 20:17:50 -04:00
|
|
|
import {Type, isBlank, isPresent, isArray, stringify, RegExpWrapper} from 'angular2/src/facade/lang';
|
2015-11-06 20:34:07 -05:00
|
|
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
2015-09-14 18:59:09 -04:00
|
|
|
import * as cpl from './directive_metadata';
|
2015-10-23 18:55:48 -04:00
|
|
|
import * as md from 'angular2/src/core/metadata/directives';
|
2015-10-02 10:37:23 -04:00
|
|
|
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
2015-12-02 13:35:51 -05:00
|
|
|
import {PipeResolver} from 'angular2/src/core/linker/pipe_resolver';
|
2015-10-02 10:37:23 -04:00
|
|
|
import {ViewResolver} from 'angular2/src/core/linker/view_resolver';
|
2015-09-14 18:59:09 -04:00
|
|
|
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
2015-10-02 10:37:23 -04:00
|
|
|
import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector';
|
|
|
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
|
2015-09-14 18:59:09 -04:00
|
|
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
2015-11-02 19:03:42 -05:00
|
|
|
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
|
2015-12-02 13:35:51 -05:00
|
|
|
import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
2015-10-01 13:07:49 -04:00
|
|
|
import {MODULE_SUFFIX} from './util';
|
2016-03-09 17:55:27 -05:00
|
|
|
import {assertArrayOfStrings} from './assertions';
|
2015-12-05 05:21:38 -05:00
|
|
|
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
2015-09-14 18:59:09 -04:00
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class RuntimeMetadataResolver {
|
2015-12-02 13:35:51 -05:00
|
|
|
private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>();
|
|
|
|
private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>();
|
2016-03-29 20:15:07 -04:00
|
|
|
private _anonymousTypes = new Map<Object, number>();
|
|
|
|
private _anonymousTypeIndex = 0;
|
2015-09-14 18:59:09 -04:00
|
|
|
|
2016-04-07 20:17:50 -04:00
|
|
|
constructor(
|
|
|
|
private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver,
|
|
|
|
private _viewResolver: ViewResolver,
|
|
|
|
@Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[],
|
|
|
|
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Type[]) {}
|
2015-09-14 18:59:09 -04:00
|
|
|
|
2016-03-29 20:15:07 -04:00
|
|
|
/**
|
|
|
|
* Wrap the stringify method to avoid naming things `function (arg1...) {`
|
|
|
|
*/
|
|
|
|
private sanitizeName(obj: any): string {
|
|
|
|
let result = stringify(obj);
|
|
|
|
if (result.indexOf('(') < 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
let found = this._anonymousTypes.get(obj);
|
|
|
|
if (!found) {
|
|
|
|
this._anonymousTypes.set(obj, this._anonymousTypeIndex++);
|
|
|
|
found = this._anonymousTypes.get(obj);
|
|
|
|
}
|
|
|
|
return `anonymous_type_${found}_`;
|
|
|
|
}
|
|
|
|
|
2015-12-02 13:35:51 -05:00
|
|
|
getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
|
|
|
var meta = this._directiveCache.get(directiveType);
|
2015-09-14 18:59:09 -04:00
|
|
|
if (isBlank(meta)) {
|
2015-10-23 18:55:48 -04:00
|
|
|
var dirMeta = this._directiveResolver.resolve(directiveType);
|
2015-12-13 20:35:33 -05:00
|
|
|
var moduleUrl = null;
|
2015-09-14 18:59:09 -04:00
|
|
|
var templateMeta = null;
|
|
|
|
var changeDetectionStrategy = null;
|
|
|
|
|
2015-10-23 18:55:48 -04:00
|
|
|
if (dirMeta instanceof md.ComponentMetadata) {
|
2016-03-09 17:55:27 -05:00
|
|
|
assertArrayOfStrings('styles', dirMeta.styles);
|
2015-10-23 18:55:48 -04:00
|
|
|
var cmpMeta = <md.ComponentMetadata>dirMeta;
|
2015-12-13 20:35:33 -05:00
|
|
|
moduleUrl = calcModuleUrl(directiveType, cmpMeta);
|
2015-10-23 18:55:48 -04:00
|
|
|
var viewMeta = this._viewResolver.resolve(directiveType);
|
2016-03-09 17:55:27 -05:00
|
|
|
assertArrayOfStrings('styles', viewMeta.styles);
|
2015-09-18 13:33:23 -04:00
|
|
|
templateMeta = new cpl.CompileTemplateMetadata({
|
2015-10-23 18:55:48 -04:00
|
|
|
encapsulation: viewMeta.encapsulation,
|
|
|
|
template: viewMeta.template,
|
|
|
|
templateUrl: viewMeta.templateUrl,
|
|
|
|
styles: viewMeta.styles,
|
|
|
|
styleUrls: viewMeta.styleUrls
|
2015-09-14 18:59:09 -04:00
|
|
|
});
|
2015-10-23 18:55:48 -04:00
|
|
|
changeDetectionStrategy = cmpMeta.changeDetection;
|
2015-09-14 18:59:09 -04:00
|
|
|
}
|
2015-09-18 13:33:23 -04:00
|
|
|
meta = cpl.CompileDirectiveMetadata.create({
|
2015-10-23 18:55:48 -04:00
|
|
|
selector: dirMeta.selector,
|
|
|
|
exportAs: dirMeta.exportAs,
|
2015-09-14 18:59:09 -04:00
|
|
|
isComponent: isPresent(templateMeta),
|
2015-09-18 13:33:23 -04:00
|
|
|
dynamicLoadable: true,
|
2015-09-28 13:30:33 -04:00
|
|
|
type: new cpl.CompileTypeMetadata(
|
2016-03-29 20:15:07 -04:00
|
|
|
{name: this.sanitizeName(directiveType), moduleUrl: moduleUrl, runtime: directiveType}),
|
2015-09-14 18:59:09 -04:00
|
|
|
template: templateMeta,
|
2015-09-18 13:33:23 -04:00
|
|
|
changeDetection: changeDetectionStrategy,
|
2015-10-23 18:55:48 -04:00
|
|
|
inputs: dirMeta.inputs,
|
|
|
|
outputs: dirMeta.outputs,
|
|
|
|
host: dirMeta.host,
|
2015-10-09 12:07:58 -04:00
|
|
|
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType))
|
2015-09-14 18:59:09 -04:00
|
|
|
});
|
2015-12-02 13:35:51 -05:00
|
|
|
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(
|
2016-03-29 20:15:07 -04:00
|
|
|
{name: this.sanitizeName(pipeType), moduleUrl: moduleUrl, runtime: pipeType}),
|
2015-12-02 13:35:51 -05:00
|
|
|
name: pipeMeta.name,
|
|
|
|
pure: pipeMeta.pure
|
|
|
|
});
|
|
|
|
this._pipeCache.set(pipeType, meta);
|
2015-09-14 18:59:09 -04:00
|
|
|
}
|
|
|
|
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);
|
2015-11-09 17:33:22 -05:00
|
|
|
var directives = flattenDirectives(view, this._platformDirectives);
|
2015-09-14 18:59:09 -04:00
|
|
|
for (var i = 0; i < directives.length; i++) {
|
2015-12-02 13:35:51 -05:00
|
|
|
if (!isValidType(directives[i])) {
|
2015-09-14 18:59:09 -04:00
|
|
|
throw new BaseException(
|
|
|
|
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
|
|
|
}
|
|
|
|
}
|
2015-10-23 19:39:35 -04:00
|
|
|
|
2015-12-02 13:35:51 -05:00
|
|
|
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));
|
2015-09-14 18:59:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 17:33:22 -05:00
|
|
|
function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[] {
|
2015-11-02 19:03:42 -05:00
|
|
|
let directives = [];
|
2015-11-09 17:33:22 -05:00
|
|
|
if (isPresent(platformDirectives)) {
|
|
|
|
flattenArray(platformDirectives, directives);
|
2015-11-02 19:03:42 -05:00
|
|
|
}
|
|
|
|
if (isPresent(view.directives)) {
|
|
|
|
flattenArray(view.directives, directives);
|
|
|
|
}
|
2015-09-14 18:59:09 -04:00
|
|
|
return directives;
|
|
|
|
}
|
|
|
|
|
2015-12-02 13:35:51 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-04-07 20:17:50 -04:00
|
|
|
function flattenArray(tree: any[], out: Array<Type|any[]>): void {
|
2015-09-14 18:59:09 -04:00
|
|
|
for (var i = 0; i < tree.length; i++) {
|
|
|
|
var item = resolveForwardRef(tree[i]);
|
|
|
|
if (isArray(item)) {
|
2015-11-02 19:03:42 -05:00
|
|
|
flattenArray(item, out);
|
2015-09-14 18:59:09 -04:00
|
|
|
} else {
|
|
|
|
out.push(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-02 13:35:51 -05:00
|
|
|
function isValidType(value: Type): boolean {
|
2015-09-14 18:59:09 -04:00
|
|
|
return isPresent(value) && (value instanceof Type);
|
|
|
|
}
|
|
|
|
|
2015-12-13 20:35:33 -05:00
|
|
|
function calcModuleUrl(type: Type, cmpMetadata: md.ComponentMetadata): string {
|
|
|
|
var moduleId = cmpMetadata.moduleId;
|
2015-12-05 05:21:38 -05:00
|
|
|
if (isPresent(moduleId)) {
|
|
|
|
var scheme = getUrlScheme(moduleId);
|
|
|
|
return isPresent(scheme) && scheme.length > 0 ? moduleId :
|
|
|
|
`package:${moduleId}${MODULE_SUFFIX}`;
|
2015-09-14 18:59:09 -04:00
|
|
|
} else {
|
2015-10-01 13:07:49 -04:00
|
|
|
return reflector.importUri(type);
|
2015-09-14 18:59:09 -04:00
|
|
|
}
|
|
|
|
}
|