packages/template-compiler/src/instructions.ts
import {
type ForOfStatement,
type Interpolation,
type IsBindingBehavior,
} from '@aurelia/expression-parser';
import { IAttributeComponentDefinition, IElementComponentDefinition } from './interfaces-template-compiler';
import { createInterface, isString, objectFreeze } from './utilities';
import { AttrSyntax } from './attribute-pattern';
import { BindingMode } from './binding-mode';
/** @internal */ export const hydrateElement = 'ra';
/** @internal */ export const hydrateAttribute = 'rb';
/** @internal */ export const hydrateTemplateController = 'rc';
/** @internal */ export const hydrateLetElement = 'rd';
/** @internal */ export const setProperty = 're';
/** @internal */ export const interpolation = 'rf';
/** @internal */ export const propertyBinding = 'rg';
/** @internal */ export const letBinding = 'ri';
/** @internal */ export const refBinding = 'rj';
/** @internal */ export const iteratorBinding = 'rk';
/** @internal */ export const multiAttr = 'rl';
/** @internal */ export const textBinding = 'ha';
/** @internal */ export const listenerBinding = 'hb';
/** @internal */ export const attributeBinding = 'hc';
/** @internal */ export const stylePropertyBinding = 'hd';
/** @internal */ export const setAttribute = 'he';
/** @internal */ export const setClassAttribute = 'hf';
/** @internal */ export const setStyleAttribute = 'hg';
/** @internal */ export const spreadTransferedBinding = 'hs';
/** @internal */ export const spreadElementProp = 'hp';
/** @internal */ export const spreadValueBinding = 'svb';
export const InstructionType = /*@__PURE__*/ objectFreeze({
hydrateElement,
hydrateAttribute,
hydrateTemplateController,
hydrateLetElement,
setProperty,
interpolation,
propertyBinding,
letBinding,
refBinding,
iteratorBinding,
multiAttr,
textBinding,
listenerBinding,
attributeBinding,
stylePropertyBinding,
setAttribute,
setClassAttribute,
setStyleAttribute,
spreadTransferedBinding,
spreadElementProp,
spreadValueBinding,
});
export type InstructionType = typeof InstructionType[keyof typeof InstructionType];
export interface IInstruction {
readonly type: string;
}
export const IInstruction = /*@__PURE__*/createInterface<IInstruction>('Instruction');
export function isInstruction(value: unknown): value is IInstruction {
const type = (value as { type?: string }).type;
return isString(type) && type.length === 2;
}
export class InterpolationInstruction {
public readonly type = interpolation;
public constructor(
public from: string | Interpolation,
public to: string,
) {}
}
export class PropertyBindingInstruction {
public readonly type = propertyBinding;
public constructor(
public from: string | IsBindingBehavior,
public to: string,
public mode: BindingMode,
) {}
}
export class IteratorBindingInstruction {
public readonly type = iteratorBinding;
public constructor(
public forOf: string | ForOfStatement,
public to: string,
public props: MultiAttrInstruction[],
) {}
}
export class RefBindingInstruction {
public readonly type = refBinding;
public constructor(
public readonly from: string | IsBindingBehavior,
public readonly to: string
) {}
}
export class SetPropertyInstruction {
public readonly type = setProperty;
public constructor(
public value: unknown,
public to: string,
) {}
}
export class MultiAttrInstruction {
public readonly type = multiAttr;
public constructor(
public value: string,
public to: string,
public command: string | null,
) {}
}
export class HydrateElementInstruction<
T extends Record<PropertyKey, unknown> = Record<PropertyKey, unknown>,
TDef extends IElementComponentDefinition = IElementComponentDefinition,
> {
public readonly type = hydrateElement;
public constructor(
/**
* The name of the custom element this instruction is associated with
*/
// in theory, Constructor of resources should be accepted too
// though it would be unnecessary right now
public res: string | /* Constructable | */TDef,
/**
* Bindable instructions for the custom element instance
*/
public props: IInstruction[],
/**
* Indicates what projections are associated with the element usage
*/
public projections: Record<string, TDef> | null,
/**
* Indicates whether the usage of the custom element was with a containerless attribute or not
*/
public containerless: boolean,
/**
* A list of captured attr syntaxes
*/
public captures: AttrSyntax[] | undefined,
/**
* Any data associated with this instruction
*/
public readonly data: T,
) {
}
}
// the template type gives an opportunity for implementor of resources resolver to provide a more specific type
export class HydrateAttributeInstruction<T extends IAttributeComponentDefinition = IAttributeComponentDefinition> {
public readonly type = hydrateAttribute;
public constructor(
// in theory, Constructor of resources should be accepted too
// though it would be unnecessary right now
public res: string | /* Constructable | */T,
public alias: string | undefined,
/**
* Bindable instructions for the custom attribute instance
*/
public props: IInstruction[],
) {}
}
// the template type gives an opportunity for implementor of resources resolver to provide a more specific type
export class HydrateTemplateController<T extends IAttributeComponentDefinition = IAttributeComponentDefinition> {
public readonly type = hydrateTemplateController;
public constructor(
public def: IElementComponentDefinition,
// in theory, Constructor of resources should be accepted too
// though it would be unnecessary right now
public res: string | /* Constructable | */T,
public alias: string | undefined,
/**
* Bindable instructions for the template controller instance
*/
public props: IInstruction[],
) {}
}
export class HydrateLetElementInstruction {
public readonly type = hydrateLetElement;
public constructor(
public instructions: LetBindingInstruction[],
public toBindingContext: boolean,
) {}
}
export class LetBindingInstruction {
public readonly type = letBinding;
public constructor(
public from: string | IsBindingBehavior | Interpolation,
public to: string,
) {}
}
export class TextBindingInstruction {
public readonly type = textBinding;
public constructor(
public from: string | IsBindingBehavior,
) {}
}
export class ListenerBindingInstruction {
public readonly type = listenerBinding;
public constructor(
public from: string | IsBindingBehavior,
public to: string,
public capture: boolean,
public modifier: string | null,
) {}
}
export class StylePropertyBindingInstruction {
public readonly type = stylePropertyBinding;
public constructor(
public from: string | IsBindingBehavior,
public to: string,
) {}
}
export class SetAttributeInstruction {
public readonly type = setAttribute;
public constructor(
public value: string,
public to: string,
) {}
}
export class SetClassAttributeInstruction {
public readonly type: typeof InstructionType.setClassAttribute = setClassAttribute;
public constructor(
public readonly value: string,
) {}
}
export class SetStyleAttributeInstruction {
public readonly type: typeof InstructionType.setStyleAttribute = setStyleAttribute;
public constructor(
public readonly value: string,
) {}
}
export class AttributeBindingInstruction {
public readonly type = attributeBinding;
public constructor(
/**
* `attr` and `to` have the same value on a normal attribute
* Will be different on `class` and `style`
* on `class`: attr = `class` (from binding command), to = attribute name
* on `style`: attr = `style` (from binding command), to = attribute name
*/
public attr: string,
public from: string | IsBindingBehavior,
public to: string,
) {}
}
export class SpreadTransferedBindingInstruction {
public readonly type = spreadTransferedBinding;
}
/**
* When spreading any attribute bindings onto an element,
* it's possible that some attributes will be targeting the bindable properties of a custom element
* This instruction is used to express that
*/
export class SpreadElementPropBindingInstruction {
public readonly type = spreadElementProp;
public constructor(
public readonly instruction: IInstruction,
) {}
}
export class SpreadValueBindingInstruction {
public readonly type = spreadValueBinding;
public constructor(
public target: '$bindables' | '$element',
public from: string,
) {}
}