2015-09-14 18:59:09 -04:00
|
|
|
import {resolveForwardRef} from 'angular2/src/core/di';
|
2016-04-12 12:40:37 -04:00
|
|
|
import {
|
|
|
|
Type,
|
|
|
|
isBlank,
|
|
|
|
isPresent,
|
|
|
|
isArray,
|
|
|
|
stringify,
|
2016-01-06 17:13:44 -05:00
|
|
|
isString,
|
|
|
|
RegExpWrapper,
|
|
|
|
StringWrapper
|
2016-04-12 12:40:37 -04:00
|
|
|
} from 'angular2/src/facade/lang';
|
2016-01-06 17:13:44 -05:00
|
|
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
2015-11-06 20:34:07 -05:00
|
|
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
2016-01-06 17:13:44 -05:00
|
|
|
import {NoAnnotationError} from 'angular2/src/core/di/exceptions';
|
|
|
|
import * as cpl from './compile_metadata';
|
2015-10-23 18:55:48 -04:00
|
|
|
import * as md from 'angular2/src/core/metadata/directives';
|
2016-01-06 17:13:44 -05:00
|
|
|
import * as dimd from 'angular2/src/core/metadata/di';
|
|
|
|
import {DirectiveResolver} from './directive_resolver';
|
|
|
|
import {PipeResolver} from './pipe_resolver';
|
|
|
|
import {ViewResolver} from './view_resolver';
|
2015-09-14 18:59:09 -04:00
|
|
|
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
2016-01-06 17:13:44 -05:00
|
|
|
import {hasLifecycleHook} from './directive_lifecycle_reflector';
|
|
|
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/metadata/lifecycle_hooks';
|
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';
|
2016-01-06 17:13:44 -05:00
|
|
|
import {Provider, constructDependencies, Dependency} from 'angular2/src/core/di/provider';
|
|
|
|
import {
|
|
|
|
OptionalMetadata,
|
|
|
|
SelfMetadata,
|
|
|
|
HostMetadata,
|
|
|
|
SkipSelfMetadata
|
|
|
|
} from 'angular2/src/core/di/metadata';
|
|
|
|
import {AttributeMetadata} from 'angular2/src/core/metadata/di';
|
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-12 12:40:37 -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 {
|
2016-01-06 17:13:44 -05:00
|
|
|
let result = StringWrapper.replaceAll(stringify(obj), /[\s-]/g, '_');
|
2016-03-29 20:15:07 -04:00
|
|
|
if (result.indexOf('(') < 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
let found = this._anonymousTypes.get(obj);
|
2016-01-06 17:13:44 -05:00
|
|
|
if (isBlank(found)) {
|
2016-03-29 20:15:07 -04:00
|
|
|
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;
|
2016-01-06 17:13:44 -05:00
|
|
|
var viewProviders = [];
|
2015-09-14 18:59:09 -04:00
|
|
|
|
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;
|
2016-01-06 17:13:44 -05:00
|
|
|
if (isPresent(dirMeta.viewProviders)) {
|
|
|
|
viewProviders = this.getProvidersMetadata(dirMeta.viewProviders);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var providers = [];
|
|
|
|
if (isPresent(dirMeta.providers)) {
|
|
|
|
providers = this.getProvidersMetadata(dirMeta.providers);
|
|
|
|
}
|
|
|
|
var queries = [];
|
|
|
|
var viewQueries = [];
|
|
|
|
if (isPresent(dirMeta.queries)) {
|
|
|
|
queries = this.getQueriesMetadata(dirMeta.queries, false);
|
|
|
|
viewQueries = this.getQueriesMetadata(dirMeta.queries, true);
|
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),
|
2016-01-06 17:13:44 -05:00
|
|
|
type: this.getTypeMetadata(directiveType, moduleUrl),
|
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,
|
2016-01-06 17:13:44 -05:00
|
|
|
lifecycleHooks:
|
|
|
|
LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType)),
|
|
|
|
providers: providers,
|
|
|
|
viewProviders: viewProviders,
|
|
|
|
queries: queries,
|
|
|
|
viewQueries: viewQueries
|
2015-09-14 18:59:09 -04:00
|
|
|
});
|
2015-12-02 13:35:51 -05:00
|
|
|
this._directiveCache.set(directiveType, meta);
|
|
|
|
}
|
|
|
|
return meta;
|
|
|
|
}
|
|
|
|
|
2016-01-06 17:13:44 -05:00
|
|
|
getTypeMetadata(type: Type, moduleUrl: string): cpl.CompileTypeMetadata {
|
|
|
|
return new cpl.CompileTypeMetadata({
|
|
|
|
name: this.sanitizeName(type),
|
|
|
|
moduleUrl: moduleUrl,
|
|
|
|
runtime: type,
|
|
|
|
diDeps: this.getDependenciesMetadata(type, null)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
getFactoryMetadata(factory: Function, moduleUrl: string): cpl.CompileFactoryMetadata {
|
|
|
|
return new cpl.CompileFactoryMetadata({
|
|
|
|
name: this.sanitizeName(factory),
|
|
|
|
moduleUrl: moduleUrl,
|
|
|
|
runtime: factory,
|
|
|
|
diDeps: this.getDependenciesMetadata(factory, null)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-12-02 13:35:51 -05:00
|
|
|
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({
|
2016-01-06 17:13:44 -05:00
|
|
|
type: this.getTypeMetadata(pipeType, moduleUrl),
|
2015-12-02 13:35:51 -05:00
|
|
|
name: pipeMeta.name,
|
2016-01-06 17:13:44 -05:00
|
|
|
pure: pipeMeta.pure,
|
|
|
|
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, pipeType)),
|
2015-12-02 13:35:51 -05:00
|
|
|
});
|
|
|
|
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
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
|
|
|
|
getDependenciesMetadata(typeOrFunc: Type | Function,
|
|
|
|
dependencies: any[]): cpl.CompileDiDependencyMetadata[] {
|
|
|
|
var deps: Dependency[];
|
|
|
|
try {
|
|
|
|
deps = constructDependencies(typeOrFunc, dependencies);
|
|
|
|
} catch (e) {
|
|
|
|
if (e instanceof NoAnnotationError) {
|
|
|
|
deps = [];
|
|
|
|
} else {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return deps.map((dep) => {
|
|
|
|
var compileToken;
|
|
|
|
var p = <AttributeMetadata>dep.properties.find(p => p instanceof AttributeMetadata);
|
|
|
|
var isAttribute = false;
|
|
|
|
if (isPresent(p)) {
|
|
|
|
compileToken = this.getTokenMetadata(p.attributeName);
|
|
|
|
isAttribute = true;
|
|
|
|
} else {
|
|
|
|
compileToken = this.getTokenMetadata(dep.key.token);
|
|
|
|
}
|
|
|
|
var compileQuery = null;
|
|
|
|
var q = <dimd.QueryMetadata>dep.properties.find(p => p instanceof dimd.QueryMetadata);
|
|
|
|
if (isPresent(q)) {
|
|
|
|
compileQuery = this.getQueryMetadata(q, null);
|
|
|
|
}
|
|
|
|
return new cpl.CompileDiDependencyMetadata({
|
|
|
|
isAttribute: isAttribute,
|
|
|
|
isHost: dep.upperBoundVisibility instanceof HostMetadata,
|
|
|
|
isSelf: dep.upperBoundVisibility instanceof SelfMetadata,
|
|
|
|
isSkipSelf: dep.lowerBoundVisibility instanceof SkipSelfMetadata,
|
|
|
|
isOptional: dep.optional,
|
|
|
|
query: isPresent(q) && !q.isViewQuery ? compileQuery : null,
|
|
|
|
viewQuery: isPresent(q) && q.isViewQuery ? compileQuery : null,
|
|
|
|
token: compileToken
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
getRuntimeIdentifier(value: any): cpl.CompileIdentifierMetadata {
|
|
|
|
return new cpl.CompileIdentifierMetadata({runtime: value, name: this.sanitizeName(value)});
|
|
|
|
}
|
|
|
|
|
|
|
|
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
|
|
|
|
token = resolveForwardRef(token);
|
|
|
|
var compileToken;
|
|
|
|
if (isString(token)) {
|
|
|
|
compileToken = new cpl.CompileTokenMetadata({value: token});
|
|
|
|
} else {
|
|
|
|
compileToken = new cpl.CompileTokenMetadata({identifier: this.getRuntimeIdentifier(token)});
|
|
|
|
}
|
|
|
|
return compileToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
getProvidersMetadata(providers: any[]):
|
|
|
|
Array<cpl.CompileProviderMetadata | cpl.CompileTypeMetadata | any[]> {
|
|
|
|
return providers.map((provider) => {
|
|
|
|
provider = resolveForwardRef(provider);
|
|
|
|
if (isArray(provider)) {
|
|
|
|
return this.getProvidersMetadata(provider);
|
|
|
|
} else if (provider instanceof Provider) {
|
|
|
|
return this.getProviderMetadata(provider);
|
|
|
|
} else {
|
|
|
|
return this.getTypeMetadata(provider, null);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
getProviderMetadata(provider: Provider): cpl.CompileProviderMetadata {
|
|
|
|
var compileDeps;
|
|
|
|
if (isPresent(provider.useClass)) {
|
|
|
|
compileDeps = this.getDependenciesMetadata(provider.useClass, provider.dependencies);
|
|
|
|
} else if (isPresent(provider.useFactory)) {
|
|
|
|
compileDeps = this.getDependenciesMetadata(provider.useFactory, provider.dependencies);
|
|
|
|
}
|
|
|
|
return new cpl.CompileProviderMetadata({
|
|
|
|
token: this.getTokenMetadata(provider.token),
|
|
|
|
useClass: isPresent(provider.useClass) ? this.getTypeMetadata(provider.useClass, null) : null,
|
|
|
|
useValue: isPresent(provider.useValue) ? this.getRuntimeIdentifier(provider.useValue) : null,
|
|
|
|
useFactory: isPresent(provider.useFactory) ?
|
|
|
|
this.getFactoryMetadata(provider.useFactory, null) :
|
|
|
|
null,
|
|
|
|
useExisting: isPresent(provider.useExisting) ? this.getTokenMetadata(provider.useExisting) :
|
|
|
|
null,
|
|
|
|
deps: compileDeps,
|
|
|
|
multi: provider.multi
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
getQueriesMetadata(queries: {[key: string]: dimd.QueryMetadata},
|
|
|
|
isViewQuery: boolean): cpl.CompileQueryMetadata[] {
|
|
|
|
var compileQueries = [];
|
|
|
|
StringMapWrapper.forEach(queries, (query, propertyName) => {
|
|
|
|
if (query.isViewQuery === isViewQuery) {
|
|
|
|
compileQueries.push(this.getQueryMetadata(query, propertyName));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return compileQueries;
|
|
|
|
}
|
|
|
|
|
|
|
|
getQueryMetadata(q: dimd.QueryMetadata, propertyName: string): cpl.CompileQueryMetadata {
|
|
|
|
var selectors;
|
|
|
|
if (q.isVarBindingQuery) {
|
|
|
|
selectors = q.varBindings.map(varName => this.getTokenMetadata(varName));
|
|
|
|
} else {
|
|
|
|
selectors = [this.getTokenMetadata(q.selector)];
|
|
|
|
}
|
|
|
|
return new cpl.CompileQueryMetadata({
|
|
|
|
selectors: selectors,
|
|
|
|
first: q.first,
|
|
|
|
descendants: q.descendants,
|
2016-04-18 16:24:42 -04:00
|
|
|
propertyName: propertyName,
|
|
|
|
read: isPresent(q.read) ? this.getTokenMetadata(q.read) : null
|
2016-01-06 17:13:44 -05:00
|
|
|
});
|
|
|
|
}
|
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-12 12:40:37 -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
|
|
|
}
|
|
|
|
}
|