import { emptyArray, InstanceProvider, type IContainer, type Constructable, type IResolver, resolve, isString, registrableMetadataKey, } from '@aurelia/kernel'; import { IExpressionParser, type ExpressionType, type AnyBindingExpression, } from '@aurelia/expression-parser'; import { IObserverLocator, type IObservable, } from '@aurelia/runtime'; import { toView } from './binding/interfaces-bindings'; import { AttributeBinding } from './binding/attribute-binding'; import { InterpolationBinding, InterpolationPartBinding } from './binding/interpolation-binding'; import { ContentBinding } from "./binding/content-binding"; import { LetBinding } from './binding/let-binding'; import { PropertyBinding } from './binding/property-binding'; import { RefBinding } from './binding/ref-binding'; import { IEventModifier, ListenerBinding, ListenerBindingOptions } from './binding/listener-binding'; import { CustomElement, CustomElementDefinition, findElementControllerFor } from './resources/custom-element'; import { CustomAttribute, CustomAttributeDefinition, findAttributeControllerFor } from './resources/custom-attribute'; import { convertToRenderLocation, IRenderLocation, ICssClassMapping, registerHostNode } from './dom'; import { INode, refs } from './dom.node'; import { Controller, ICustomElementController, ICustomElementViewModel, IController, ICustomAttributeViewModel, IHydrationContext } from './templating/controller'; import { IPlatform } from './platform'; import { IViewFactory } from './templating/view'; import { IRendering } from './templating/rendering'; import { objectKeys, etIsProperty, etInterpolation, etIsIterator, etIsFunction } from './utilities'; import { createInterface, registerResolver, singletonRegistration } from './utilities-di'; import { IAuSlotsInfo, AuSlotsInfo } from './templating/controller.projection'; import type { IHydratableController } from './templating/controller'; import { ErrorNames, createMappedError } from './errors'; import { type ISSRScope, type ISSRScopeChild, type ISSRTemplateController } from './templating/ssr'; import { SpreadBinding, SpreadValueBinding } from './binding/spread-binding'; import { type AttributeBindingInstruction, type HydrateAttributeInstruction, type HydrateElementInstruction, type HydrateLetElementInstruction, type HydrateTemplateController, IInstruction, ITemplateCompiler, type InterpolationInstruction, type IteratorBindingInstruction, type LetBindingInstruction, type ListenerBindingInstruction, type PropertyBindingInstruction, type RefBindingInstruction, type SetAttributeInstruction, type SetClassAttributeInstruction, type SetPropertyInstruction, type SetStyleAttributeInstruction, type SpreadTransferedBindingInstruction, type SpreadValueBindingInstruction, type StylePropertyBindingInstruction, type TextBindingInstruction, itSetProperty, itHydrateElement, itHydrateAttribute, itHydrateTemplateController, itHydrateLetElement, itRefBinding, itInterpolation, itPropertyBinding, itIteratorBinding, itTextBinding, itListenerBinding, itSetAttribute, itSetClassAttribute, itSetStyleAttribute, itStylePropertyBinding, itAttributeBinding, itSpreadTransferedBinding, itSpreadValueBinding, } from '@aurelia/template-compiler'; import { ClassAttributeAccessor } from './observation/class-attribute-accessor'; import { fromHydrationContext } from './resources/resolvers'; /** * An interface describing an instruction renderer * its target property will be used to match instruction types dynamically at render time */ export interface IRenderer { target: number; render( /** * The controller that is current invoking this renderer */ renderingCtrl: IHydratableController, target: unknown, instruction: IInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, /** * SSR manifest scope entry for child-creating instructions (TCs, CEs). * Passed by Rendering.render() based on tree position. */ ssrScope?: ISSRScopeChild, ): void; } export const IRenderer = /*@__PURE__*/createInterface('IRenderer'); export function renderer>(target: C, context: ClassDecoratorContext): C { const metadata = context?.metadata ?? (target[Symbol.metadata] ??= Object.create(null)); metadata[registrableMetadataKey] = { register(container: IContainer): void { singletonRegistration(IRenderer, target).register(container); } }; return target; } function ensureExpression(parser: IExpressionParser, srcOrExpr: TFrom | string, expressionType: ExpressionType): TFrom { if (isString(srcOrExpr)) { return parser.parse(srcOrExpr, expressionType) as TFrom; } return srcOrExpr; } function getTarget(potentialTarget: object): object { if ((potentialTarget as { viewModel?: object }).viewModel != null) { return (potentialTarget as { viewModel: object }).viewModel; } return potentialTarget; } function getRefTarget(refHost: INode, refTargetName: string): object { if (refTargetName === 'element') { return refHost; } switch (refTargetName) { case 'controller': // this means it supports returning undefined return findElementControllerFor(refHost)!; case 'view': throw createMappedError(ErrorNames.not_supported_view_ref_api); case 'component': // this means it supports returning undefined return findElementControllerFor(refHost)!.viewModel; default: { const caController = findAttributeControllerFor(refHost, refTargetName); if (caController !== void 0) { return caController.viewModel; } const ceController = findElementControllerFor(refHost, { name: refTargetName }); if (ceController === void 0) { throw createMappedError(ErrorNames.ref_not_found, refTargetName); } return ceController.viewModel; } } } export const SetPropertyRenderer = /*@__PURE__*/ renderer(class SetPropertyRenderer implements IRenderer { public readonly target = itSetProperty; public render( renderingCtrl: IHydratableController, target: IController, instruction: SetPropertyInstruction, ): void { const obj = getTarget(target) as IObservable; if (obj.$observers?.[instruction.to] !== void 0) { obj.$observers[instruction.to].setValue(instruction.value); } else { obj[instruction.to] = instruction.value; } } }, null!); export const CustomElementRenderer = /*@__PURE__*/ renderer(class CustomElementRenderer implements IRenderer { /** @internal */ public readonly _rendering = resolve(IRendering); public readonly target = itHydrateElement; public render( renderingCtrl: IHydratableController, target: HTMLElement, instruction: HydrateElementInstruction, CustomElementDefinition>, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ssrScope?: ISSRScopeChild, ): void { /* eslint-disable prefer-const */ let def: CustomElementDefinition | null; let component: ICustomElementViewModel; let childCtrl: ICustomElementController; const res = instruction.res; const projections = instruction.projections; const ctxContainer = renderingCtrl.container; switch (typeof res) { case 'string': def = CustomElement.find(ctxContainer, res); if (def == null) { throw createMappedError(ErrorNames.element_res_not_found, instruction, renderingCtrl); } break; // constructor based instruction // will be enabled later if needed. // As both AOT + runtime based can use definition for perf // ----------------- // case 'function': // def = CustomElement.getDefinition(res); // break; default: def = res; } const containerless = instruction.containerless || def.containerless; const location = containerless ? convertToRenderLocation(target) : null; const container = createElementContainer( /* platform */platform, /* parentController */renderingCtrl, /* host */target, /* instruction */instruction, /* location */location, /* SlotsInfo */projections == null ? void 0 : new AuSlotsInfo(objectKeys(projections)), ); component = container.invoke(def.Type); childCtrl = Controller.$el( /* own container */container, /* viewModel */component, /* host */target, /* instruction */instruction, /* definition */def, /* location */location, /* ssrScope */ssrScope as ISSRScope | undefined, ); const renderers = this._rendering.renderers; const props = instruction.props; const ii = props.length; let i = 0; let propInst: IInstruction; while (ii > i) { propInst = props[i]; renderers[propInst.type].render(renderingCtrl, childCtrl, propInst, platform, exprParser, observerLocator); ++i; } renderingCtrl.addChild(childCtrl); /* eslint-enable prefer-const */ } }, null!); export const CustomAttributeRenderer = /*@__PURE__*/ renderer(class CustomAttributeRenderer implements IRenderer { /** @internal */ public readonly _rendering = resolve(IRendering); public readonly target = itHydrateAttribute; public render( /** * The cotroller that is currently invoking this renderer */ renderingCtrl: IHydratableController, target: HTMLElement, // as we assume it's always used with the default resources resolver instruction: HydrateAttributeInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { /* eslint-disable prefer-const */ let ctxContainer = renderingCtrl.container; let def: CustomAttributeDefinition | null; switch (typeof instruction.res) { case 'string': def = CustomAttribute.find(ctxContainer, instruction.res); if (def == null) { throw createMappedError(ErrorNames.attribute_res_not_found, instruction, renderingCtrl); } break; // constructor based instruction // will be enabled later if needed. // As both AOT + runtime based can use definition for perf // ----------------- // case 'function': // def = CustomAttribute.getDefinition(instruction.res); // break; default: def = instruction.res; } const results = invokeAttribute( /* platform */platform, /* attr definition */def, /* parentController */renderingCtrl, /* host */target, /* instruction */instruction, /* viewFactory */void 0, /* location */void 0, ); const childController = Controller.$attr( /* context ct */results.ctn, /* viewModel */results.vm, /* host */target, /* definition */def, ); refs.set(target, def.key, childController); const renderers = this._rendering.renderers; const props = instruction.props; const ii = props.length; let i = 0; let propInst: IInstruction; while (ii > i) { propInst = props[i]; renderers[propInst.type].render(renderingCtrl, childController, propInst, platform, exprParser, observerLocator); ++i; } renderingCtrl.addChild(childController); /* eslint-enable prefer-const */ } }, null!); export const TemplateControllerRenderer = /*@__PURE__*/ renderer(class TemplateControllerRenderer implements IRenderer { /** @internal */ public readonly _rendering = resolve(IRendering); public readonly target = itHydrateTemplateController; public render( renderingCtrl: IHydratableController, target: HTMLElement, // as we assume it's always used with the default resources resolver instruction: HydrateTemplateController, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ssrScope?: ISSRScopeChild, ): void { /* eslint-disable prefer-const */ let ctxContainer = renderingCtrl.container; let def: CustomAttributeDefinition | null; switch (typeof instruction.res) { case 'string': def = CustomAttribute.find(ctxContainer, instruction.res); if (def == null) { throw createMappedError(ErrorNames.attribute_tc_res_not_found, instruction, renderingCtrl); } break; // constructor based instruction // will be enabled later if needed. // As both AOT + runtime based can use definition for perf // ----------------- // case 'function': // def = CustomAttribute.getDefinition(instruction.res); // break; default: def = instruction.res; } // const viewFactory = this._rendering.getViewFactory( // instruction.def, // ctxContainer // ); const viewFactory = this._rendering.getViewFactory( instruction.def, def.containerStrategy === 'new' ? ctxContainer.createChild({ inheritParentResources: true }) : ctxContainer ); const renderLocation = convertToRenderLocation(target); const results = invokeAttribute( /* platform */platform, /* attr definition */def, /* parentController */renderingCtrl, /* host */target, /* instruction */instruction, /* viewFactory */viewFactory, /* location */renderLocation, ); const childController = Controller.$attr( /* container ct */results.ctn, /* viewModel */results.vm, /* host */target, /* definition */def, /* ssrScope */ssrScope as ISSRTemplateController | undefined, ); refs.set(renderLocation, def.key, childController); results.vm.link?.(renderingCtrl, childController, target, instruction); const renderers = this._rendering.renderers; const props = instruction.props; const ii = props.length; let i = 0; let propInst: IInstruction; while (ii > i) { propInst = props[i]; renderers[propInst.type].render(renderingCtrl, childController, propInst, platform, exprParser, observerLocator); ++i; } renderingCtrl.addChild(childController); /* eslint-enable prefer-const */ } }, null!); export const LetElementRenderer = /*@__PURE__*/ renderer(class LetElementRenderer implements IRenderer { public readonly target = itHydrateLetElement; public constructor() { LetBinding.mix(); } public render( renderingCtrl: IHydratableController, target: Node & ChildNode, instruction: HydrateLetElementInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { target.remove(); const childInstructions = instruction.instructions; const toBindingContext = instruction.toBindingContext; const container = renderingCtrl.container; const ii = childInstructions.length; let childInstruction: LetBindingInstruction; let expr: AnyBindingExpression; let i = 0; while (ii > i) { childInstruction = childInstructions[i]; expr = ensureExpression(exprParser, childInstruction.from, etIsProperty); renderingCtrl.addBinding(new LetBinding( renderingCtrl, container, observerLocator, expr, childInstruction.to, toBindingContext, renderingCtrl.strict ?? false, )); ++i; } } }, null!); export const RefBindingRenderer = /*@__PURE__*/ renderer(class RefBindingRenderer implements IRenderer { public readonly target = itRefBinding; public constructor() { RefBinding.mix(); } public render( renderingCtrl: IHydratableController, target: INode, instruction: RefBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator ): void { renderingCtrl.addBinding(new RefBinding( renderingCtrl.container, observerLocator, ensureExpression(exprParser, instruction.from, etIsProperty), getRefTarget(target, instruction.to), renderingCtrl.strict ?? false, )); } }, null!); export const InterpolationBindingRenderer = /*@__PURE__*/ renderer(class InterpolationBindingRenderer implements IRenderer { public readonly target = itInterpolation; public constructor() { InterpolationPartBinding.mix(); } public render( renderingCtrl: IHydratableController, target: IController | HTMLElement, instruction: InterpolationInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { const container = renderingCtrl.container; const binding = new InterpolationBinding( renderingCtrl, container, observerLocator, ensureExpression(exprParser, instruction.from, etInterpolation), getTarget(target), instruction.to, toView, renderingCtrl.strict ?? false, ); if (instruction.to === 'class' && (binding.target as Node).nodeType > 0) { const cssMapping = container.get(fromHydrationContext(ICssClassMapping)); binding.useAccessor(new ClassAttributeAccessor(binding.target as HTMLElement, cssMapping)); } renderingCtrl.addBinding(binding); } }, null!); export const PropertyBindingRenderer = /*@__PURE__*/ renderer(class PropertyBindingRenderer implements IRenderer { public readonly target = itPropertyBinding; public constructor() { PropertyBinding.mix(); } public render( renderingCtrl: IHydratableController, target: IController, instruction: PropertyBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { const container = renderingCtrl.container; const binding = new PropertyBinding( renderingCtrl, container, observerLocator, ensureExpression(exprParser, instruction.from, etIsProperty), getTarget(target), instruction.to, instruction.mode, renderingCtrl.strict ?? false, ); if (instruction.to === 'class' && (binding.target as Node).nodeType > 0) { const cssMapping = container.get(fromHydrationContext(ICssClassMapping)); binding.useTargetObserver(new ClassAttributeAccessor(binding.target as HTMLElement, cssMapping)); } renderingCtrl.addBinding(binding); } }, null!); export const IteratorBindingRenderer = /*@__PURE__*/ renderer(class IteratorBindingRenderer implements IRenderer { public readonly target = itIteratorBinding; public constructor() { PropertyBinding.mix(); } public render( renderingCtrl: IHydratableController, target: IController, instruction: IteratorBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { renderingCtrl.addBinding(new PropertyBinding( renderingCtrl, renderingCtrl.container, observerLocator, ensureExpression(exprParser, instruction.forOf, etIsIterator), getTarget(target), instruction.to, toView, renderingCtrl.strict ?? false, )); } }, null!); export const TextBindingRenderer = /*@__PURE__*/ renderer(class TextBindingRenderer implements IRenderer { public readonly target = itTextBinding; public constructor() { ContentBinding.mix(); } public render( renderingCtrl: IHydratableController, target: ChildNode, instruction: TextBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { renderingCtrl.addBinding(new ContentBinding( renderingCtrl, renderingCtrl.container, observerLocator, platform, ensureExpression(exprParser, instruction.from, etIsProperty), target as Text, renderingCtrl.strict ?? false, )); } }, null!); /** * An interface describing configuration for listener bindings */ export interface IListenerBindingOptions { /** * Indicate whether listener should by default call preventDefault on all the events */ prevent: boolean; /** * The error handler for listener bindings, by default, it will dispatch an event `au-event-error` on the window object * and if the event is not prevented, it will throw the errors caught by the listener bindings */ onError: (event: Event, error: unknown) => void; } export const IListenerBindingOptions = createInterface('IListenerBindingOptions', x => x.singleton(class { /** @internal */ private readonly p = resolve(IPlatform); public prevent = false; public onError = (event: Event, error: unknown) => { const errorEvent = new this.p.CustomEvent('au-event-error', { cancelable: true, detail: { event, error } }); this.p.window.dispatchEvent(errorEvent); if (errorEvent.defaultPrevented) { return; } throw error; }; })); export const ListenerBindingRenderer = /*@__PURE__*/ renderer(class ListenerBindingRenderer implements IRenderer { public readonly target = itListenerBinding; /** @internal */ public readonly _modifierHandler = resolve(IEventModifier); /** @internal */ public readonly _defaultOptions = resolve(IListenerBindingOptions); public constructor() { ListenerBinding.mix(); } public render( renderingCtrl: IHydratableController, target: HTMLElement, instruction: ListenerBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, ): void { renderingCtrl.addBinding(new ListenerBinding( renderingCtrl.container, ensureExpression(exprParser, instruction.from, etIsFunction), target, instruction.to, new ListenerBindingOptions(this._defaultOptions.prevent, instruction.capture, this._defaultOptions.onError), this._modifierHandler.getHandler(instruction.to, instruction.modifier), renderingCtrl.strict ?? false, )); } }, null!); export const SetAttributeRenderer = /*@__PURE__*/ renderer(class SetAttributeRenderer implements IRenderer { public readonly target = itSetAttribute; public render( _: IHydratableController, target: HTMLElement, instruction: SetAttributeInstruction, ): void { target.setAttribute(instruction.to, instruction.value); } }, null!); export const SetClassAttributeRenderer = /*@__PURE__*/ renderer(class SetClassAttributeRenderer implements IRenderer { public readonly target = itSetClassAttribute; public render( _: IHydratableController, target: HTMLElement, instruction: SetClassAttributeInstruction, ): void { addClasses(target.classList, instruction.value); } }, null!); export const SetStyleAttributeRenderer = /*@__PURE__*/ renderer(class SetStyleAttributeRenderer implements IRenderer { public readonly target = itSetStyleAttribute; public render( _: IHydratableController, target: HTMLElement, instruction: SetStyleAttributeInstruction, ): void { target.style.cssText += instruction.value; } }, null!); /* istanbul ignore next */ const ambiguousStyles = [ 'height', 'width', 'border-width', 'padding', 'padding-left', 'padding-right', 'padding-top', 'padding-right', 'padding-inline', 'padding-block', 'margin', 'margin-left', 'margin-right', 'margin-top', 'margin-bottom', 'margin-inline', 'margin-block', 'top', 'right', 'bottom', 'left', ]; export const StylePropertyBindingRenderer = /*@__PURE__*/ renderer(class StylePropertyBindingRenderer implements IRenderer { public readonly target = itStylePropertyBinding; public constructor() { PropertyBinding.mix(); } public render( renderingCtrl: IHydratableController, target: HTMLElement, instruction: StylePropertyBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { if (__DEV__) { /* istanbul ignore next */ if (ambiguousStyles.includes(instruction.to)) { renderingCtrl.addBinding(new DevStylePropertyBinding( renderingCtrl, renderingCtrl.container, observerLocator, ensureExpression(exprParser, instruction.from, etIsProperty), target.style, instruction.to, toView, renderingCtrl.strict ?? false, )); return; } } renderingCtrl.addBinding(new PropertyBinding( renderingCtrl, renderingCtrl.container, observerLocator, ensureExpression(exprParser, instruction.from, etIsProperty), target.style, instruction.to, toView, renderingCtrl.strict ?? false, )); } }, null!); /* istanbul ignore next */ class DevStylePropertyBinding extends PropertyBinding { public updateTarget(value: unknown): void { if (typeof value === 'number' && value > 0) { // eslint-disable-next-line no-console console.warn(`[DEV]: Setting number ${value} as value for style.${this.targetProperty}. Did you meant "${value}px"?`); } return super.updateTarget(value); } } export const AttributeBindingRenderer = /*@__PURE__*/ renderer(class AttributeBindingRenderer implements IRenderer { public readonly target = itAttributeBinding; public constructor() { AttributeBinding.mix(); } public render( renderingCtrl: IHydratableController, target: HTMLElement, instruction: AttributeBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { const container = renderingCtrl.container; const classMapping = container.has(ICssClassMapping, false) ? container.get(ICssClassMapping) : null; renderingCtrl.addBinding(new AttributeBinding( renderingCtrl, container, observerLocator, ensureExpression(exprParser, instruction.from, etIsProperty), target, instruction.attr/* targetAttribute */, classMapping == null ? instruction.to/* targetKey */ : instruction.to.split(/\s/g).map(c => classMapping[c] ?? c).join(' '), toView, renderingCtrl.strict ?? false, )); } }, null!); export const SpreadRenderer = /*@__PURE__*/ renderer(class SpreadRenderer implements IRenderer { /** @internal */ public readonly _compiler = resolve(ITemplateCompiler); /** @internal */ public readonly _rendering = resolve(IRendering); public readonly target = itSpreadTransferedBinding; public render( renderingCtrl: IHydratableController, target: HTMLElement, instruction: SpreadTransferedBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { SpreadBinding.create( renderingCtrl.container.get(IHydrationContext), target, void 0, this._rendering, this._compiler, platform, exprParser, observerLocator ) .forEach(b => renderingCtrl.addBinding(b)); } }, null!); export const SpreadValueRenderer = /*@__PURE__*/ renderer(class SpreadValueRenderer implements IRenderer { public readonly target = itSpreadValueBinding; public constructor() { SpreadValueBinding.mix(); } public render( renderingCtrl: IHydratableController, target: ICustomElementController, instruction: SpreadValueBindingInstruction, platform: IPlatform, exprParser: IExpressionParser, observerLocator: IObserverLocator, ): void { const instructionTarget = instruction.target; if (instructionTarget === '$bindables') { renderingCtrl.addBinding(new SpreadValueBinding( renderingCtrl, target.viewModel, objectKeys(target.definition.bindables), exprParser.parse(instruction.from, etIsProperty), observerLocator, renderingCtrl.container, renderingCtrl.strict ?? false, )); } else { throw createMappedError(ErrorNames.spreading_invalid_target, instructionTarget); } } }, null!); // http://jsben.ch/7n5Kt function addClasses(classList: DOMTokenList, className: string): void { const len = className.length; let start = 0; for (let i = 0; i < len; ++i) { if (className.charCodeAt(i) === 0x20) { if (i !== start) { classList.add(className.slice(start, i)); } start = i + 1; } else if (i + 1 === len) { classList.add(className.slice(start)); } } } // const createSurrogateBinding = (context: IHydrationContext) => // new SpreadBinding([], context) as SpreadBinding & IHydratableController; const controllerProviderName = 'IController'; const instructionProviderName = 'IInstruction'; const locationProviderName = 'IRenderLocation'; const slotInfoProviderName = 'ISlotsInfo'; function createElementContainer( p: IPlatform, renderingCtrl: IController, host: HTMLElement, instruction: HydrateElementInstruction, location: IRenderLocation | null, auSlotsInfo?: IAuSlotsInfo, ): IContainer { const ctn = renderingCtrl.container.createChild(); registerHostNode(ctn, host, p); registerResolver(ctn, IController, new InstanceProvider(controllerProviderName, renderingCtrl)); registerResolver(ctn, IInstruction, new InstanceProvider(instructionProviderName, instruction)); registerResolver(ctn, IRenderLocation, location == null ? noLocationProvider : new RenderLocationProvider(location)); registerResolver(ctn, IViewFactory, noViewFactoryProvider); registerResolver(ctn, IAuSlotsInfo, auSlotsInfo == null ? noAuSlotProvider : new InstanceProvider(slotInfoProviderName, auSlotsInfo) ); return ctn; } class ViewFactoryProvider implements IResolver { private readonly f: IViewFactory | null; public get $isResolver(): true { return true; } public constructor( /** * The factory instance that this provider will resolves to, * until explicitly overridden by prepare call */ factory: IViewFactory | null ) { this.f = factory; } public resolve(): IViewFactory { const f = this.f; if (f === null) { throw createMappedError(ErrorNames.view_factory_provider_not_ready); } if (!isString(f.name) || f.name.length === 0) { throw createMappedError(ErrorNames.view_factory_invalid_name); } return f; } } /** @internal */ export interface IHasController { $controller: IController; } function invokeAttribute( p: IPlatform, definition: CustomAttributeDefinition, $renderingCtrl: IController | IHasController, host: HTMLElement, instruction: HydrateAttributeInstruction | HydrateTemplateController, viewFactory?: IViewFactory, location?: IRenderLocation, auSlotsInfo?: IAuSlotsInfo, ): { vm: ICustomAttributeViewModel; ctn: IContainer } { const renderingCtrl = $renderingCtrl instanceof Controller ? $renderingCtrl : ($renderingCtrl as IHasController).$controller; const ctn = renderingCtrl.container.createChild(); registerHostNode(ctn, host, p); registerResolver(ctn, IController, new InstanceProvider(controllerProviderName, renderingCtrl)); registerResolver(ctn, IInstruction, new InstanceProvider(instructionProviderName, instruction)); registerResolver(ctn, IRenderLocation, location == null ? noLocationProvider : new InstanceProvider(locationProviderName, location)); registerResolver(ctn, IViewFactory, viewFactory == null ? noViewFactoryProvider : new ViewFactoryProvider(viewFactory)); registerResolver(ctn, IAuSlotsInfo, auSlotsInfo == null ? noAuSlotProvider : new InstanceProvider(slotInfoProviderName, auSlotsInfo)); return { vm: ctn.invoke(definition.Type), ctn }; } class RenderLocationProvider implements IResolver { public get name() { return 'IRenderLocation'; } public get $isResolver(): true { return true; } public constructor( private readonly _location: IRenderLocation | null ) {} public resolve(): IRenderLocation | null { return this._location; } } const noLocationProvider = new RenderLocationProvider(null); const noViewFactoryProvider = new ViewFactoryProvider(null); const noAuSlotProvider = new InstanceProvider(slotInfoProviderName, new AuSlotsInfo(emptyArray));