111 lines
3.3 KiB
TypeScript
111 lines
3.3 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {Component, Directive, NgModule, Pipe, Type, ɵReflectionCapabilities as ReflectionCapabilities} from '@angular/core';
|
|
|
|
import {MetadataOverride} from './metadata_override';
|
|
import {MetadataOverrider} from './metadata_overrider';
|
|
|
|
const reflection = new ReflectionCapabilities();
|
|
|
|
/**
|
|
* Base interface to resolve `@Component`, `@Directive`, `@Pipe` and `@NgModule`.
|
|
*/
|
|
export interface Resolver<T> {
|
|
addOverride(type: Type<any>, override: MetadataOverride<T>): void;
|
|
setOverrides(overrides: Array<[Type<any>, MetadataOverride<T>]>): void;
|
|
resolve(type: Type<any>): T|null;
|
|
}
|
|
|
|
/**
|
|
* Allows to override ivy metadata for tests (via the `TestBed`).
|
|
*/
|
|
abstract class OverrideResolver<T> implements Resolver<T> {
|
|
private overrides = new Map<Type<any>, MetadataOverride<T>[]>();
|
|
private resolved = new Map<Type<any>, T|null>();
|
|
|
|
abstract get type(): any;
|
|
|
|
addOverride(type: Type<any>, override: MetadataOverride<T>) {
|
|
const overrides = this.overrides.get(type) || [];
|
|
overrides.push(override);
|
|
this.overrides.set(type, overrides);
|
|
this.resolved.delete(type);
|
|
}
|
|
|
|
setOverrides(overrides: Array<[Type<any>, MetadataOverride<T>]>) {
|
|
this.overrides.clear();
|
|
overrides.forEach(([type, override]) => {
|
|
this.addOverride(type, override);
|
|
});
|
|
}
|
|
|
|
getAnnotation(type: Type<any>): T|null {
|
|
const annotations = reflection.annotations(type);
|
|
// Try to find the nearest known Type annotation and make sure that this annotation is an
|
|
// instance of the type we are looking for, so we can use it for resolution. Note: there might
|
|
// be multiple known annotations found due to the fact that Components can extend Directives (so
|
|
// both Directive and Component annotations would be present), so we always check if the known
|
|
// annotation has the right type.
|
|
for (let i = annotations.length - 1; i >= 0; i--) {
|
|
const annotation = annotations[i];
|
|
const isKnownType = annotation instanceof Directive || annotation instanceof Component ||
|
|
annotation instanceof Pipe || annotation instanceof NgModule;
|
|
if (isKnownType) {
|
|
return annotation instanceof this.type ? annotation : null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
resolve(type: Type<any>): T|null {
|
|
let resolved = this.resolved.get(type) || null;
|
|
|
|
if (!resolved) {
|
|
resolved = this.getAnnotation(type);
|
|
if (resolved) {
|
|
const overrides = this.overrides.get(type);
|
|
if (overrides) {
|
|
const overrider = new MetadataOverrider();
|
|
overrides.forEach(override => {
|
|
resolved = overrider.overrideMetadata(this.type, resolved!, override);
|
|
});
|
|
}
|
|
}
|
|
this.resolved.set(type, resolved);
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
}
|
|
|
|
|
|
export class DirectiveResolver extends OverrideResolver<Directive> {
|
|
get type() {
|
|
return Directive;
|
|
}
|
|
}
|
|
|
|
export class ComponentResolver extends OverrideResolver<Component> {
|
|
get type() {
|
|
return Component;
|
|
}
|
|
}
|
|
|
|
export class PipeResolver extends OverrideResolver<Pipe> {
|
|
get type() {
|
|
return Pipe;
|
|
}
|
|
}
|
|
|
|
export class NgModuleResolver extends OverrideResolver<NgModule> {
|
|
get type() {
|
|
return NgModule;
|
|
}
|
|
}
|