Skip to content

Commit

Permalink
feat(runtime): making resources into an explicit concept again
Browse files Browse the repository at this point in the history
  • Loading branch information
EisenbergEffect committed Jul 3, 2018
1 parent 3d62b7a commit 559e008
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 34 deletions.
22 changes: 15 additions & 7 deletions src/runtime/binding/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IServiceLocator } from '../../kernel/di';
import { IScope, BindingContext } from './binding-context';
import { ISignaler } from './signaler';
import { BindingFlags } from './binding-flags';
import { Resource } from '../resource';

export type IsPrimary = AccessThis | AccessScope | ArrayLiteral | ObjectLiteral | PrimitiveLiteral | Template;
export type IsUnary = IsPrimary | Unary;
Expand Down Expand Up @@ -42,12 +43,13 @@ export class BindingBehavior implements IExpression {
(<any>this.expression).bind(binding, scope, flags);
}

let behavior = binding.locator.get(this.name);
const behaviorKey = Resource.bindingBehavior.key(this.name);
const behavior = binding.locator.get(behaviorKey);

if (!behavior) {
throw new Error(`No BindingBehavior named "${this.name}" was found!`);
}

let behaviorKey = `behavior-${this.name}`;
if ((binding as any)[behaviorKey]) {
throw new Error(`A binding behavior named "${this.name}" has already been applied to "${this.expression}"`);
}
Expand All @@ -57,7 +59,7 @@ export class BindingBehavior implements IExpression {
}

unbind(binding: IBinding, scope: IScope, flags: BindingFlags) {
let behaviorKey = `behavior-${this.name}`;
const behaviorKey = Resource.bindingBehavior.key(this.name);

(binding as any)[behaviorKey].unbind(binding, scope, flags);
(binding as any)[behaviorKey] = null;
Expand All @@ -75,7 +77,9 @@ export class ValueConverter implements IExpression {
}

evaluate(scope: IScope, locator: IServiceLocator, flags: BindingFlags) {
let converter = locator.get(this.name);
const converterKey = Resource.valueConverter.key(this.name);
const converter = locator.get(converterKey);

if (!converter) {
throw new Error(`No ValueConverter named "${this.name}" was found!`);
}
Expand All @@ -88,7 +92,9 @@ export class ValueConverter implements IExpression {
}

assign(scope: IScope, value: any, locator: IServiceLocator, flags: BindingFlags) {
let converter = locator.get(this.name);
const converterKey = Resource.valueConverter.key(this.name);
const converter = locator.get(converterKey);

if (!converter) {
throw new Error(`No ValueConverter named "${this.name}" was found!`);
}
Expand All @@ -108,7 +114,8 @@ export class ValueConverter implements IExpression {
expressions[i].connect(binding, scope, flags);
}

const converter = binding.locator.get(this.name);
const converterKey = Resource.valueConverter.key(this.name);
const converter = binding.locator.get(converterKey);

if (!converter) {
throw new Error(`No ValueConverter named "${this.name}" was found!`);
Expand All @@ -129,7 +136,8 @@ export class ValueConverter implements IExpression {
}

unbind(binding: IBinding, scope: IScope, flags: BindingFlags) {
const converter = binding.locator.get(this.name);
const converterKey = Resource.valueConverter.key(this.name);
const converter = binding.locator.get(converterKey);
const signals = (converter as any).signals;

if (signals === undefined) {
Expand Down
48 changes: 48 additions & 0 deletions src/runtime/resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Constructable } from "../kernel/interfaces";
import { IRegistry } from "../kernel/di";

function resourceName(this: IResourceKind, name: string) {
return `${this.name}:${name}`;
}

function isType(this: IResourceKind, type: Function) {
return (<any>type).kind === this;
}

export interface IResourceDescription {
name: string;
}

export interface IResourceKind {
name: 'attribute' | 'element' | 'value-converter' | 'binding-behavior';
key(name: string): string;
isType(type: Function): boolean;
}

export interface IResourceType<T = {}> extends Constructable<T>, IRegistry {
kind: IResourceKind;
description: IResourceDescription;
}

export const Resource = {
attribute: {
name: 'attribute',
key: resourceName,
isType: isType
} as IResourceKind,
element: {
name: 'element',
key: resourceName,
isType: isType
} as IResourceKind,
valueConverter: {
name: 'value-converter',
key: resourceName,
isType: isType
} as IResourceKind,
bindingBehavior: {
name: 'binding-behavior',
key: resourceName,
isType: isType
} as IResourceKind
};
35 changes: 16 additions & 19 deletions src/runtime/templating/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ITemplateSource, TemplateDefinition, IHydrateElementInstruction, Attrib
import { INode, DOM } from '../dom';
import { IRenderingEngine } from './rendering-engine';
import { IRuntimeBehavior } from './runtime-behavior';
import { Resource, IResourceKind, IResourceType } from '../resource';

export type IElementHydrationOptions = Immutable<Pick<IHydrateElementInstruction, 'parts' | 'contentOverride'>>;

Expand Down Expand Up @@ -43,37 +44,31 @@ interface IAttributeComponentImplementation extends Writable<IAttributeComponent
$slot: IRenderSlot;
}

export interface IAttributeType extends Constructable<IAttributeComponent>, IRegistry {
readonly type: 'attribute';
export interface IAttributeType extends IResourceType<IAttributeComponent> {
readonly definition: AttributeDefinition;
};

export interface IElementType extends Constructable<IElementComponent>, IRegistry {
readonly type: 'element';
export interface IElementType extends IResourceType<IElementComponent> {
readonly definition: TemplateDefinition;
}

export interface IValueConverterType extends Constructable, IRegistry {
readonly type: 'value-converter';
export interface IValueConverterType extends IResourceType {
readonly definition: ValueConverterDefinition;
}

export interface IBindingBehaviorType extends Constructable, IRegistry {
readonly type: 'binding-behavior';
export interface IBindingBehaviorType extends IResourceType {
readonly definition: BindingBehaviorDefinition;
}

export type ComponentType = IAttributeType | IElementType | IValueConverterType | IBindingBehaviorType;

export const Component = {
valueConverter<T extends Constructable>(nameOrSource: string | IValueConverterSource, ctor: T): T & IValueConverterType {
const definition = createDefinition<IValueConverterSource>(nameOrSource);
const Type: T & IValueConverterType = ctor as any;

(Type as Writable<IValueConverterType>).type = 'value-converter';
(Type as Writable<IValueConverterType>).kind = Resource.valueConverter;
(Type as Writable<IValueConverterType>).definition = definition;
Type.register = function(container: IContainer) {
container.register(Registration.singleton(definition.name, Type));
container.register(Registration.singleton(Type.kind.key(definition.name), Type));
};

return Type;
Expand All @@ -82,10 +77,10 @@ export const Component = {
const definition = createDefinition<IBindingBehaviorSource>(nameOrSource);
const Type: T & IBindingBehaviorType = ctor as any;

(Type as Writable<IBindingBehaviorType>).type = 'binding-behavior';
(Type as Writable<IBindingBehaviorType>).kind = Resource.bindingBehavior;
(Type as Writable<IBindingBehaviorType>).definition = definition;
Type.register = function(container: IContainer) {
container.register(Registration.singleton(definition.name, Type));
container.register(Registration.singleton(Type.kind.key(definition.name), Type));
};

return Type;
Expand All @@ -95,15 +90,17 @@ export const Component = {
const definition = createAttributeDefinition(typeof nameOrSource === 'string' ? { name: nameOrSource } : nameOrSource, Type);
const proto: IAttributeComponent = Type.prototype;

(Type as Writable<IAttributeType>).type = 'attribute';
(Type as Writable<IAttributeType>).kind = Resource.attribute;
(Type as Writable<IAttributeType>).definition = definition;
Type.register = function register(container: IContainer){
container.register(Registration.transient(definition.name, Type));
const resourceKey = Type.kind.key(definition.name);

container.register(Registration.transient(resourceKey, Type));

const aliases = definition.aliases;

for(let i = 0, ii = aliases.length; i < ii; ++i) {
container.register(Registration.alias(definition.name, aliases[i]));
container.register(Registration.alias(resourceKey, aliases[i]));
}
};

Expand Down Expand Up @@ -198,10 +195,10 @@ export const Component = {
const definition = createTemplateDefinition(typeof nameOrSource === 'string' ? { name: nameOrSource } : nameOrSource, Type);
const proto: IElementComponent = Type.prototype;

(Type as Writable<IElementType>).type = 'element';
(Type as Writable<IElementType>).kind = Resource.element;
(Type as Writable<IElementType>).definition = definition;
Type.register = function(container: IContainer){
container.register(Registration.transient(definition.name, Type));
container.register(Registration.transient(Type.kind.key(definition.name), Type));
};

proto.$hydrate = function(this: IElementComponentImplementation, renderingEngine: IRenderingEngine, host: INode, options: IElementHydrationOptions = PLATFORM.emptyObject) {
Expand Down
8 changes: 4 additions & 4 deletions src/runtime/templating/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { IViewOwner } from './view';
import { INode, DOM } from '../dom';
import { IHydrateElementInstruction, TargetedInstructionType, ITextBindingInstruction, IOneWayBindingInstruction, IFromViewBindingInstruction, ITwoWayBindingInstruction, IListenerBindingInstruction, ICallBindingInstruction, IRefBindingInstruction, IStylePropertyBindingInstruction, ISetPropertyInstruction, ISetAttributeInstruction, IHydrateSlotInstruction, IHydrateAttributeInstruction, IHydrateTemplateController, TemplateDefinition, TemplatePartDefinitions } from "./instructions";
import { IElementComponent, IAttributeComponent } from './component';
import { ITaskQueue } from '../task-queue';
import { IObserverLocator } from '../binding/observer-locator';
import { IEventManager } from '../binding/event-manager';
import { IExpressionParser } from '../binding/expression-parser';
Expand All @@ -15,6 +14,7 @@ import { Ref } from '../binding/ref';
import { ShadowDOMEmulation } from './shadow-dom';
import { IRenderContext } from './render-context';
import { Immutable } from '../../kernel/interfaces';
import { Resource } from '../resource';

export interface IRenderer {
render(owner: IViewOwner, targets: ArrayLike<INode>, templateDefinition: TemplateDefinition, host?: INode, parts?: TemplatePartDefinitions): void;
Expand Down Expand Up @@ -136,7 +136,7 @@ export class Renderer implements IRenderer {
[TargetedInstructionType.hydrateElement](owner: IViewOwner, target: any, instruction: Immutable<IHydrateElementInstruction>) {
const context = this.context;
const operation = context.beginComponentOperation(owner, target, instruction, null, null, target, true);
const component = context.get<IElementComponent>(instruction.res);
const component = context.get<IElementComponent>(Resource.element.key(instruction.res));

this.hydrateElementInstance(owner, target, instruction, component);
operation.tryConnectElementToSlot(component);
Expand All @@ -149,7 +149,7 @@ export class Renderer implements IRenderer {
const context = this.context;
const operation = context.beginComponentOperation(owner, target, instruction);

const component = context.get<IAttributeComponent>(instruction.res);
const component = context.get<IAttributeComponent>(Resource.attribute.key(instruction.res));
component.$hydrate(this.renderingEngine);

for (let i = 0, ii = childInstructions.length; i < ii; ++i) {
Expand All @@ -169,7 +169,7 @@ export class Renderer implements IRenderer {
const context = this.context;
const operation = context.beginComponentOperation(owner, target, instruction, factory, parts, DOM.convertToAnchor(target), false);

const component = context.get<IAttributeComponent>(instruction.res);
const component = context.get<IAttributeComponent>(Resource.attribute.key(instruction.res));
component.$hydrate(this.renderingEngine);
operation.tryConnectTemplateControllerToSlot(component);

Expand Down
9 changes: 5 additions & 4 deletions src/runtime/templating/rendering-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { IObserverLocator } from "../binding/observer-locator";
import { IEventManager } from "../binding/event-manager";
import { IExpressionParser } from "../binding/expression-parser";
import { ITemplateCompiler, ICompilationResources } from "./template-compiler";
import { Resource } from "../resource";

export interface IRenderingEngine {
getElementTemplate(definition: TemplateDefinition, componentType: IElementType): ITemplate;
Expand Down Expand Up @@ -239,12 +240,12 @@ class CompiledTemplate implements ITemplate {
class RuntimeCompilationResources implements ICompilationResources {
constructor(private context: ExposedContext) {}

tryGetElement(name: string): IElementDescription | null {
return this.getDescription<IElementDescription>(name);
tryGetElement(elementName: string): IElementDescription | null {
return this.getDescription<IElementDescription>(Resource.element.key(elementName));
}

tryGetAttribute(name: string): IAttributeDescription | null {
return this.getDescription<IAttributeDescription>(name);
tryGetAttribute(attributeName: string): IAttributeDescription | null {
return this.getDescription<IAttributeDescription>(Resource.attribute.key(attributeName));
}

private getDescription<T extends (IElementDescription | IAttributeDescription)>(name: string) {
Expand Down

0 comments on commit 559e008

Please sign in to comment.