UNPKG

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