UNPKG

10.7 kBTypeScriptView Raw
1import type { ComponentInstanceState, PreparedArguments } from '../../components.js';
2import type { Destroyable, Nullable } from '../../core.js';
3import type { Bounds } from '../../dom/bounds.js';
4import type { SimpleElement } from '../../dom/simple.js';
5import type { Reference } from '../../references.js';
6import type { Owner } from '../../runtime.js';
7import type { CapturedArguments, VMArguments } from '../../runtime/arguments.js';
8import type { RenderNode } from '../../runtime/debug-render-tree.js';
9import type { ElementOperations } from '../../runtime/element.js';
10import type { Environment } from '../../runtime/environment.js';
11import type { DynamicScope } from '../../runtime/scope.js';
12import type { RuntimeResolver } from '../../serialize.js';
13import type { CompilableProgram } from '../../template.js';
14import type { ProgramSymbolTable } from '../../tier1/symbol-table.js';
15
16/**
17 * Describes the capabilities of a particular component. The capabilities are
18 * provided to the Glimmer compiler and VM via the ComponentDefinition, which
19 * includes a ComponentCapabilities record.
20 *
21 * Certain features in the VM come with some overhead, so the compiler and
22 * runtime use this information to skip unnecessary work for component types
23 * that don't need it.
24 *
25 * For example, a component that is template-only (i.e., it does not have an
26 * associated JavaScript class to instantiate) can skip invoking component
27 * manager hooks related to lifecycle events by setting the `elementHook`
28 * capability to `false`.
29 */
30export interface InternalComponentCapabilities {
31 /**
32 * Whether a component's template is static across all instances of that
33 * component, or can vary per instance. This should usually be `false` except
34 * for cases of backwards-compatibility.
35 */
36 dynamicLayout: boolean;
37
38 /**
39 * Whether a "wrapped" component's root element can change after being
40 * rendered. This flag is only used by the WrappedBuilder and should be
41 * `false` except for cases of backwards-compatibility.
42 */
43 dynamicTag: boolean;
44
45 wrapped: boolean;
46
47 /**
48 * Setting the `prepareArgs` flag to true enables the `prepareArgs` hook on
49 * the component manager, which would otherwise not be called.
50 *
51 * The component manager's `prepareArgs` hook allows it to programmatically
52 * add or remove positional and named arguments for a component before the
53 * component is invoked. This flag should usually be `false` except for cases
54 * of backwards-compatibility.
55 */
56 prepareArgs: boolean;
57
58 /**
59 * Whether a reified `Arguments` object will get passed to the component
60 * manager's `create` hook. If a particular component does not access passed
61 * arguments from JavaScript (via the `this.args` property in Glimmer.js, for
62 * example), this flag can be set to `false` to avoid the work of
63 * instantiating extra data structures to expose the arguments to JavaScript.
64 */
65 createArgs: boolean;
66
67 /**
68 * Whether the component needs the caller component
69 */
70 createCaller: boolean;
71
72 /**
73 * Whether to call the `didSplatAttributes` hook on the component manager.
74 */
75 attributeHook: boolean;
76
77 /**
78 * Whether to call the `didCreateElement` hook on the component manager.
79 */
80 elementHook: boolean;
81
82 /**
83 * Whether the component manager has an update hook.
84 */
85 updateHook: boolean;
86
87 /**
88 * Whether the component needs an additional dynamic scope frame.
89 */
90 dynamicScope: boolean;
91
92 /**
93 * Whether there is a component instance to create. If this is false,
94 * the component is a "template only component"
95 */
96 createInstance: boolean;
97
98 /**
99 * Whether or not the component has a `willDestroy` hook that should fire
100 * prior to the component being removed from the DOM.
101 */
102 willDestroy: boolean;
103
104 /**
105 * Whether or not the component pushes an owner onto the owner stack. This is
106 * used for engines.
107 */
108 hasSubOwner: boolean;
109}
110
111/**
112 * Enum used for bit flags version of the capabilities, used once the component
113 * has been loaded for the first time
114 */
115export type EmptyCapability = 0b0000000000000;
116export type DynamicLayoutCapability = 0b0000000000001;
117export type DynamicTagCapability = 0b0000000000010;
118export type PrepareArgsCapability = 0b0000000000100;
119export type CreateArgsCapability = 0b0000000001000;
120export type AttributeHookCapability = 0b0000000010000;
121export type ElementHookCapability = 0b0000000100000;
122export type DynamicScopeCapability = 0b0000001000000;
123export type CreateCallerCapability = 0b0000010000000;
124export type UpdateHookCapability = 0b0000100000000;
125export type CreateInstanceCapability = 0b0001000000000;
126export type WrappedCapability = 0b0010000000000;
127export type WillDestroyCapability = 0b0100000000000;
128export type HasSubOwnerCapability = 0b1000000000000;
129
130export type InternalComponentCapability =
131 | EmptyCapability
132 | DynamicLayoutCapability
133 | DynamicTagCapability
134 | PrepareArgsCapability
135 | CreateArgsCapability
136 | AttributeHookCapability
137 | ElementHookCapability
138 | DynamicScopeCapability
139 | CreateCallerCapability
140 | UpdateHookCapability
141 | CreateInstanceCapability
142 | WrappedCapability
143 | WillDestroyCapability
144 | HasSubOwnerCapability;
145
146////////////
147
148export interface InternalComponentManager<
149 TComponentStateBucket = unknown,
150 TComponentDefinition = object,
151> {
152 getCapabilities(state: TComponentDefinition): InternalComponentCapabilities;
153 getSelf(state: TComponentStateBucket): Reference;
154 getDestroyable(state: TComponentStateBucket): Nullable<Destroyable>;
155 getDebugName(state: TComponentDefinition): string;
156}
157
158export interface CustomRenderNode extends RenderNode {
159 bucket: object;
160}
161
162export interface WithCustomDebugRenderTree<
163 ComponentInstanceState = unknown,
164 ComponentDefinitionState = unknown,
165> extends InternalComponentManager<ComponentInstanceState, ComponentDefinitionState> {
166 // APIs for hooking into the debug render tree, used by components that
167 // represent multiple logical components. Specifically, {{mount}} and {{outlet}}
168 getDebugCustomRenderTree(
169 definition: ComponentDefinitionState,
170 state: ComponentInstanceState,
171 args: CapturedArguments,
172 template?: string
173 ): CustomRenderNode[];
174}
175
176export interface WithPrepareArgs<
177 ComponentInstanceState = unknown,
178 ComponentDefinitionState = unknown,
179> extends InternalComponentManager<ComponentInstanceState, ComponentDefinitionState> {
180 // The component manager is asked to prepare the arguments needed
181 // for `create`. This allows for things like closure> components where the
182 // args need to be curried before constructing the instance of the state
183 // bucket.
184 prepareArgs(state: ComponentDefinitionState, args: VMArguments): Nullable<PreparedArguments>;
185}
186
187export interface WithSubOwner<ComponentInstanceState = unknown, ComponentDefinitionState = unknown>
188 extends InternalComponentManager<ComponentInstanceState, ComponentDefinitionState> {
189 getOwner(state: ComponentInstanceState): Owner;
190}
191
192export interface WithCreateInstance<
193 ComponentInstanceState = unknown,
194 ComponentDefinitionState = unknown,
195 O extends Owner = Owner,
196> extends InternalComponentManager<ComponentInstanceState, ComponentDefinitionState> {
197 // The component manager is asked to create a bucket of state for
198 // the supplied arguments. From the perspective of Glimmer, this is
199 // an opaque token, but in practice it is probably a component object.
200 create(
201 owner: O,
202 state: ComponentDefinitionState,
203 args: Nullable<VMArguments>,
204 env: Environment,
205 dynamicScope: Nullable<DynamicScope>,
206 caller: Nullable<Reference>,
207 hasDefaultBlock: boolean
208 ): ComponentInstanceState;
209
210 // This hook is run after the entire layout has been rendered.
211 //
212 // Hosts should use `didCreate`, which runs asynchronously after the rendering
213 // process, to provide hooks for user code.
214 didRenderLayout(state: ComponentInstanceState, bounds: Bounds): void;
215
216 // This hook is run after the entire layout has been updated.
217 //
218 // Hosts should use `didUpdate`, which runs asynchronously after the rendering
219 // process, to provide hooks for user code.
220 didUpdateLayout(state: ComponentInstanceState, bounds: Bounds): void;
221
222 // Once the whole top-down rendering process is complete, Glimmer invokes
223 // the `didCreate` callbacks.
224 didCreate(state: ComponentInstanceState): void;
225
226 // Finally, once top-down revalidation has completed, Glimmer invokes
227 // the `didUpdate` callbacks on components that changed.
228 didUpdate(state: ComponentInstanceState): void;
229}
230
231export interface WithUpdateHook<ComponentInstanceState = unknown>
232 extends InternalComponentManager<ComponentInstanceState> {
233 // When the component's tag has invalidated, the manager's `update` hook is
234 // called.
235 update(state: ComponentInstanceState, dynamicScope: Nullable<DynamicScope>): void;
236}
237
238export interface WithDynamicLayout<
239 I = ComponentInstanceState,
240 R extends RuntimeResolver = RuntimeResolver,
241> extends InternalComponentManager<I> {
242 // Return the compiled layout to use for this component. This is called
243 // *after* the component instance has been created, because you might
244 // want to return a different layout per-instance for optimization reasons
245 // or to implement features like Ember's "late-bound" layouts.
246 getDynamicLayout(component: I, resolver: R): CompilableProgram | null;
247}
248
249export interface WithDynamicTagName<ComponentInstanceState>
250 extends InternalComponentManager<ComponentInstanceState> {
251 // If the component asks for the dynamic tag name capability, ask for
252 // the tag name to use. (Only used in the "WrappedBuilder".)
253 getTagName(component: ComponentInstanceState): Nullable<string>;
254}
255
256export interface WithAttributeHook<ComponentInstanceState>
257 extends InternalComponentManager<ComponentInstanceState> {
258 didSplatAttributes(
259 component: ComponentInstanceState,
260 element: ComponentInstanceState,
261 operations: ElementOperations
262 ): void;
263}
264
265export interface WithElementHook<ComponentInstanceState>
266 extends InternalComponentManager<ComponentInstanceState> {
267 // The `didCreateElement` hook is run for non-tagless components after the
268 // element as been created, but before it has been appended ("flushed") to
269 // the DOM. This hook allows the manager to save off the element, as well as
270 // install other dynamic attributes via the ElementOperations object.
271 //
272 // Hosts should use `didCreate`, which runs asynchronously after the rendering
273 // process, to provide hooks for user code.
274 didCreateElement(
275 component: ComponentInstanceState,
276 element: SimpleElement,
277 operations: ElementOperations
278 ): void;
279}
280
281export interface Invocation {
282 handle: number;
283 symbolTable: ProgramSymbolTable;
284}