forked from angular/angular
-
Notifications
You must be signed in to change notification settings - Fork 0
/
node.ts
360 lines (309 loc) · 11.6 KB
/
node.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {LContainer, TContainer} from './container';
import {LInjector} from './injector';
import {LProjection} from './projection';
import {LQueries} from './query';
import {RElement, RNode, RText} from './renderer';
import {LView, TData, TView} from './view';
/**
* LNodeType corresponds to the LNode.type property. It contains information
* on how to map a particular set of bits in LNode.flags to the node type.
*/
export const enum LNodeType {
Container = 0b00,
Projection = 0b01,
View = 0b10,
Element = 0b11,
ViewOrElement = 0b10,
}
/**
* Corresponds to the TNode.flags property.
*/
export const enum TNodeFlags {
/** The number of directives on this node is encoded on the least significant bits */
DirectiveCountMask = 0b00000000000000000000111111111111,
/** Then this bit is set when the node is a component */
isComponent = 0b1000000000000,
/** The index of the first directive on this node is encoded on the most significant bits */
DirectiveStartingIndexShift = 13,
}
/**
* LNode is an internal data structure which is used for the incremental DOM algorithm.
* The "L" stands for "Logical" to differentiate between `RNodes` (actual rendered DOM
* node) and our logical representation of DOM nodes, `LNodes`.
*
* The data structure is optimized for speed and size.
*
* In order to be fast, all subtypes of `LNode` should have the same shape.
* Because size of the `LNode` matters, many fields have multiple roles depending
* on the `LNode` subtype.
*
* See: https://en.wikipedia.org/wiki/Inline_caching#Monomorphic_inline_caching
*
* NOTE: This is a private data structure and should not be exported by any of the
* instructions.
*/
export interface LNode {
/** The type of the node (see LNodeFlags) */
type: LNodeType;
/**
* The associated DOM node. Storing this allows us to:
* - append children to their element parents in the DOM (e.g. `parent.native.appendChild(...)`)
* - retrieve the sibling elements of text nodes whose creation / insertion has been delayed
*/
readonly native: RElement|RText|null|undefined;
/**
* We need a reference to a node's parent so we can append the node to its parent's native
* element at the appropriate time.
*/
readonly parent: LNode|null;
/**
* First child of the current node.
*/
child: LNode|null;
/**
* The next sibling node. Necessary so we can propagate through the root nodes of a view
* to insert them or remove them from the DOM.
*/
next: LNode|null;
/**
* If regular LElementNode, then `data` will be null.
* If LElementNode with component, then `data` contains LView.
* If LViewNode, then `data` contains the LView.
* If LContainerNode, then `data` contains LContainer.
* If LProjectionNode, then `data` contains LProjection.
*/
readonly data: LView|LContainer|LProjection|null;
/**
* Each node belongs to a view.
*
* When the injector is walking up a tree, it needs access to the `directives` (part of view).
*/
readonly view: LView;
/** The injector associated with this node. Necessary for DI. */
nodeInjector: LInjector|null;
/**
* Optional set of queries that track query-related events for this node.
*
* If present the node creation/updates are reported to the `LQueries`.
*/
queries: LQueries|null;
/**
* If this node is projected, pointer to the next node in the same projection parent
* (which is a container, an element, or a text node), or to the parent projection node
* if this is the last node in the projection.
* If this node is not projected, this field is null.
*/
pNextOrParent: LNode|null;
/**
* Pointer to the corresponding TNode object, which stores static
* data about this node.
*/
tNode: TNode|null;
/**
* A pointer to a LContainerNode created by directives requesting ViewContainerRef
*/
dynamicLContainerNode: LContainerNode|null;
}
/** LNode representing an element. */
export interface LElementNode extends LNode {
/** The DOM element associated with this node. */
readonly native: RElement;
child: LContainerNode|LElementNode|LTextNode|LProjectionNode|null;
next: LContainerNode|LElementNode|LTextNode|LProjectionNode|null;
/** If Component then data has LView (light DOM) */
readonly data: LView|null;
/** LElementNodes can be inside other LElementNodes or inside LViewNodes. */
readonly parent: LElementNode|LViewNode;
}
/** LNode representing a #text node. */
export interface LTextNode extends LNode {
/** The text node associated with this node. */
native: RText;
child: null;
next: LContainerNode|LElementNode|LTextNode|LProjectionNode|null;
/** LTextNodes can be inside LElementNodes or inside LViewNodes. */
readonly parent: LElementNode|LViewNode;
readonly data: null;
dynamicLContainerNode: null;
}
/** Abstract node which contains root nodes of a view. */
export interface LViewNode extends LNode {
readonly native: null;
child: LContainerNode|LElementNode|LTextNode|LProjectionNode|null;
next: LViewNode|null;
/** LViewNodes can only be added to LContainerNodes. */
readonly parent: LContainerNode|null;
readonly data: LView;
dynamicLContainerNode: null;
}
/** Abstract node container which contains other views. */
export interface LContainerNode extends LNode {
/*
* Caches the reference of the first native node following this container in the same native
* parent.
* This is reset to undefined in containerRefreshEnd.
* When it is undefined, it means the value has not been computed yet.
* Otherwise, it contains the result of findBeforeNode(container, null).
*/
native: RElement|RText|null|undefined;
readonly data: LContainer;
child: null;
next: LContainerNode|LElementNode|LTextNode|LProjectionNode|null;
/** Containers can be added to elements or views. */
readonly parent: LElementNode|LViewNode|null;
}
export interface LProjectionNode extends LNode {
readonly native: null;
child: null;
next: LContainerNode|LElementNode|LTextNode|LProjectionNode|null;
readonly data: LProjection;
/** Projections can be added to elements or views. */
readonly parent: LElementNode|LViewNode;
dynamicLContainerNode: null;
}
/**
* LNode binding data (flyweight) for a particular node that is shared between all templates
* of a specific type.
*
* If a property is:
* - PropertyAliases: that property's data was generated and this is it
* - Null: that property's data was already generated and nothing was found.
* - Undefined: that property's data has not yet been generated
*
* see: https://en.wikipedia.org/wiki/Flyweight_pattern for more on the Flyweight pattern
*/
export interface TNode {
/**
* This number stores two values using its bits:
*
* - the number of directives on that node (first 12 bits)
* - the starting index of the node's directives in the directives array (last 20 bits).
*
* These two values are necessary so DI can effectively search the directives associated
* with a node without searching the whole directives array.
*/
flags: TNodeFlags;
/** The tag name associated with this node. */
tagName: string|null;
/**
* Static attributes associated with an element. We need to store
* static attributes to support content projection with selectors.
* Attributes are stored statically because reading them from the DOM
* would be way too slow for content projection and queries.
*
* Since attrs will always be calculated first, they will never need
* to be marked undefined by other instructions.
*
* The name of the attribute and its value alternate in the array.
* e.g. ['role', 'checkbox']
*/
attrs: string[]|null;
/**
* A set of local names under which a given element is exported in a template and
* visible to queries. An entry in this array can be created for different reasons:
* - an element itself is referenced, ex.: `<div #foo>`
* - a component is referenced, ex.: `<my-cmpt #foo>`
* - a directive is referenced, ex.: `<my-cmpt #foo="directiveExportAs">`.
*
* A given element might have different local names and those names can be associated
* with a directive. We store local names at even indexes while odd indexes are reserved
* for directive index in a view (or `-1` if there is no associated directive).
*
* Some examples:
* - `<div #foo>` => `["foo", -1]`
* - `<my-cmpt #foo>` => `["foo", myCmptIdx]`
* - `<my-cmpt #foo #bar="directiveExportAs">` => `["foo", myCmptIdx, "bar", directiveIdx]`
* - `<div #foo #bar="directiveExportAs">` => `["foo", -1, "bar", directiveIdx]`
*/
localNames: (string|number)[]|null;
/** Information about input properties that need to be set once from attribute data. */
initialInputs: InitialInputData|null|undefined;
/**
* Input data for all directives on this node.
*
* - `undefined` means that the prop has not been initialized yet,
* - `null` means that the prop has been initialized but no inputs have been found.
*/
inputs: PropertyAliases|null|undefined;
/**
* Output data for all directives on this node.
*
* - `undefined` means that the prop has not been initialized yet,
* - `null` means that the prop has been initialized but no outputs have been found.
*/
outputs: PropertyAliases|null|undefined;
/**
* The static data equivalent of LNode.data.
*
* If this TNode corresponds to an LContainerNode, the container will
* need to store separate static data for each of its views (TContainer).
*
* If this TNode corresponds to an LElementNode, data will be null.
*/
data: TContainer|null;
}
/** Static data for an LElementNode */
export interface TElementNode extends TNode { data: null; }
/** Static data for an LContainerNode */
export interface TContainerNode extends TNode { data: TContainer; }
/**
* This mapping is necessary so we can set input properties and output listeners
* properly at runtime when property names are minified or aliased.
*
* Key: unminified / public input or output name
* Value: array containing minified / internal name and related directive index
*
* The value must be an array to support inputs and outputs with the same name
* on the same node.
*/
export type PropertyAliases = {
// This uses an object map because using the Map type would be too slow
[key: string]: PropertyAliasValue
};
/**
* Store the runtime input or output names for all the directives.
*
* - Even indices: directive index
* - Odd indices: minified / internal name
*
* e.g. [0, 'change-minified']
*/
export type PropertyAliasValue = (number | string)[];
/**
* This array contains information about input properties that
* need to be set once from attribute data. It's ordered by
* directive index (relative to element) so it's simple to
* look up a specific directive's initial input data.
*
* Within each sub-array:
*
* Even indices: minified/internal input name
* Odd indices: initial value
*
* If a directive on a node does not have any input properties
* that should be set from attributes, its index is set to null
* to avoid a sparse array.
*
* e.g. [null, ['role-min', 'button']]
*/
export type InitialInputData = (InitialInputs | null)[];
/**
* Used by InitialInputData to store input properties
* that should be set once from attributes.
*
* Even indices: minified/internal input name
* Odd indices: initial value
*
* e.g. ['role-min', 'button']
*/
export type InitialInputs = string[];
// Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types.
export const unusedValueExportToPlacateAjd = 1;