@angular/core
Version:
Angular - the core framework
1,333 lines (1,319 loc) • 280 kB
TypeScript
/**
* @license Angular v20.0.3
* (c) 2010-2025 Google LLC. https://angular.io/
* License: MIT
*/
import { InjectionToken, Type, ValueProvider, ExistingProvider, FactoryProvider, ConstructorProvider, StaticClassProvider, ClassProvider, EnvironmentProviders, Injector, ProviderToken, InjectOptions, Provider, ProcessProvidersFunction, ModuleWithProviders, DestroyRef, InternalInjectFlags, WritableSignal, OutputRef, StaticProvider } from './chrome_dev_tools_performance.d-DvzAxqBc.js';
import { Observable, Subject, Subscription } from 'rxjs';
import './event_dispatcher.d-BReQpZfC.js';
import { SignalNode } from './signal.d-BcmOdASA.js';
import { Injector as Injector$1, InjectionToken as InjectionToken$1, NotFound } from '@angular/core/primitives/di';
import { ReactiveNode } from './graph.d-BcIOep_B.js';
/**
* Reactive node type for an input signal. An input signal extends a signal.
* There are special properties to enable transforms and required inputs.
*/
interface InputSignalNode<T, TransformT> extends SignalNode<T> {
/**
* User-configured transform that will run whenever a new value is applied
* to the input signal node.
*/
transformFn: ((value: TransformT) => T) | undefined;
/**
* Applies a new value to the input signal. Expects transforms to be run
* manually before.
*
* This function is called by the framework runtime code whenever a binding
* changes. The value can in practice be anything at runtime, but for typing
* purposes we assume it's a valid `T` value. Type-checking will enforce that.
*/
applyValueToInputSignal<T, TransformT>(node: InputSignalNode<T, TransformT>, value: T): void;
/**
* A debug name for the input signal. Used in Angular DevTools to identify the signal.
*/
debugName?: string;
}
declare const enum NotificationSource {
MarkAncestorsForTraversal = 0,
SetInput = 1,
DeferBlockStateUpdate = 2,
DebugApplyChanges = 3,
MarkForCheck = 4,
Listener = 5,
CustomElement = 6,
RenderHook = 7,
ViewAttached = 8,
ViewDetachedFromDOM = 9,
AsyncAnimationsLoaded = 10,
PendingTaskRemoved = 11,
RootEffect = 12,
ViewEffect = 13
}
/**
* Injectable that is notified when an `LView` is made aware of changes to application state.
*/
declare abstract class ChangeDetectionScheduler {
abstract notify(source: NotificationSource): void;
abstract runningTick: boolean;
}
/** Token used to indicate if zoneless was enabled via provideZonelessChangeDetection(). */
declare const ZONELESS_ENABLED: InjectionToken<boolean>;
/**
* @fileoverview
* While Angular only uses Trusted Types internally for the time being,
* references to Trusted Types could leak into our core.d.ts, which would force
* anyone compiling against @angular/core to provide the @types/trusted-types
* package in their compilation unit.
*
* Until https://github.com/microsoft/TypeScript/issues/30024 is resolved, we
* will keep Angular's public API surface free of references to Trusted Types.
* For internal and semi-private APIs that need to reference Trusted Types, the
* minimal type definitions for the Trusted Types API provided by this module
* should be used instead. They are marked as "declare" to prevent them from
* being renamed by compiler optimization.
*
* Adapted from
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/trusted-types/index.d.ts
* but restricted to the API surface used within Angular.
*/
type TrustedHTML = string & {
__brand__: 'TrustedHTML';
};
type TrustedScript = string & {
__brand__: 'TrustedScript';
};
type TrustedScriptURL = string & {
__brand__: 'TrustedScriptURL';
};
/**
* Function used to sanitize the value before writing it into the renderer.
*/
type SanitizerFn = (value: any, tagName?: string, propName?: string) => string | TrustedHTML | TrustedScript | TrustedScriptURL;
/**
* Stores a list of nodes which need to be removed.
*
* Numbers are indexes into the `LView`
* - index > 0: `removeRNode(lView[0])`
* - index < 0: `removeICU(~lView[0])`
*/
interface I18nRemoveOpCodes extends Array<number> {
__brand__: 'I18nRemoveOpCodes';
}
/**
* Array storing OpCode for dynamically creating `i18n` blocks.
*
* Example:
* ```ts
* <I18nCreateOpCode>[
* // For adding text nodes
* // ---------------------
* // Equivalent to:
* // lView[1].appendChild(lView[0] = document.createTextNode('xyz'));
* 'xyz', 0, 1 << SHIFT_PARENT | 0 << SHIFT_REF | AppendChild,
*
* // For adding element nodes
* // ---------------------
* // Equivalent to:
* // lView[1].appendChild(lView[0] = document.createElement('div'));
* ELEMENT_MARKER, 'div', 0, 1 << SHIFT_PARENT | 0 << SHIFT_REF | AppendChild,
*
* // For adding comment nodes
* // ---------------------
* // Equivalent to:
* // lView[1].appendChild(lView[0] = document.createComment(''));
* ICU_MARKER, '', 0, 1 << SHIFT_PARENT | 0 << SHIFT_REF | AppendChild,
*
* // For moving existing nodes to a different location
* // --------------------------------------------------
* // Equivalent to:
* // const node = lView[1];
* // lView[2].appendChild(node);
* 1 << SHIFT_REF | Select, 2 << SHIFT_PARENT | 0 << SHIFT_REF | AppendChild,
*
* // For removing existing nodes
* // --------------------------------------------------
* // const node = lView[1];
* // removeChild(tView.data(1), node, lView);
* 1 << SHIFT_REF | Remove,
*
* // For writing attributes
* // --------------------------------------------------
* // const node = lView[1];
* // node.setAttribute('attr', 'value');
* 1 << SHIFT_REF | Attr, 'attr', 'value'
* ];
* ```
*/
interface IcuCreateOpCodes extends Array<number | string | ELEMENT_MARKER | ICU_MARKER | null>, I18nDebug {
__brand__: 'I18nCreateOpCodes';
}
/**
* Marks that the next string is an element name.
*
* See `I18nMutateOpCodes` documentation.
*/
declare const ELEMENT_MARKER: ELEMENT_MARKER;
interface ELEMENT_MARKER {
marker: 'element';
}
/**
* Marks that the next string is comment text need for ICU.
*
* See `I18nMutateOpCodes` documentation.
*/
declare const ICU_MARKER: ICU_MARKER;
interface ICU_MARKER {
marker: 'ICU';
}
interface I18nDebug {
/**
* Human readable representation of the OpCode arrays.
*
* NOTE: This property only exists if `ngDevMode` is set to `true` and it is not present in
* production. Its presence is purely to help debug issue in development, and should not be relied
* on in production application.
*/
debug?: string[];
}
/**
* Array storing OpCode for dynamically creating `i18n` translation DOM elements.
*
* This array creates a sequence of `Text` and `Comment` (as ICU anchor) DOM elements. It consists
* of a pair of `number` and `string` pairs which encode the operations for the creation of the
* translated block.
*
* The number is shifted and encoded according to `I18nCreateOpCode`
*
* Pseudocode:
* ```ts
* const i18nCreateOpCodes = [
* 10 << I18nCreateOpCode.SHIFT, "Text Node add to DOM",
* 11 << I18nCreateOpCode.SHIFT | I18nCreateOpCode.COMMENT, "Comment Node add to DOM",
* 12 << I18nCreateOpCode.SHIFT | I18nCreateOpCode.APPEND_LATER, "Text Node added later"
* ];
*
* for(var i=0; i<i18nCreateOpCodes.length; i++) {
* const opcode = i18NCreateOpCodes[i++];
* const index = opcode >> I18nCreateOpCode.SHIFT;
* const text = i18NCreateOpCodes[i];
* let node: Text|Comment;
* if (opcode & I18nCreateOpCode.COMMENT === I18nCreateOpCode.COMMENT) {
* node = lView[~index] = document.createComment(text);
* } else {
* node = lView[index] = document.createText(text);
* }
* if (opcode & I18nCreateOpCode.APPEND_EAGERLY !== I18nCreateOpCode.APPEND_EAGERLY) {
* parentNode.appendChild(node);
* }
* }
* ```
*/
interface I18nCreateOpCodes extends Array<number | string>, I18nDebug {
__brand__: 'I18nCreateOpCodes';
}
/**
* Stores DOM operations which need to be applied to update DOM render tree due to changes in
* expressions.
*
* The basic idea is that `i18nExp` OpCodes capture expression changes and update a change
* mask bit. (Bit 1 for expression 1, bit 2 for expression 2 etc..., bit 32 for expression 32 and
* higher.) The OpCodes then compare its own change mask against the expression change mask to
* determine if the OpCodes should execute.
*
* NOTE: 32nd bit is special as it says 32nd or higher. This way if we have more than 32 bindings
* the code still works, but with lower efficiency. (it is unlikely that a translation would have
* more than 32 bindings.)
*
* These OpCodes can be used by both the i18n block as well as ICU sub-block.
*
* ## Example
*
* Assume
* ```ts
* if (rf & RenderFlags.Update) {
* i18nExp(ctx.exp1); // If changed set mask bit 1
* i18nExp(ctx.exp2); // If changed set mask bit 2
* i18nExp(ctx.exp3); // If changed set mask bit 3
* i18nExp(ctx.exp4); // If changed set mask bit 4
* i18nApply(0); // Apply all changes by executing the OpCodes.
* }
* ```
* We can assume that each call to `i18nExp` sets an internal `changeMask` bit depending on the
* index of `i18nExp`.
*
* ### OpCodes
* ```ts
* <I18nUpdateOpCodes>[
* // The following OpCodes represent: `<div i18n-title="pre{{exp1}}in{{exp2}}post">`
* // If `changeMask & 0b11`
* // has changed then execute update OpCodes.
* // has NOT changed then skip `8` values and start processing next OpCodes.
* 0b11, 8,
* // Concatenate `newValue = 'pre'+lView[bindIndex-4]+'in'+lView[bindIndex-3]+'post';`.
* 'pre', -4, 'in', -3, 'post',
* // Update attribute: `elementAttribute(1, 'title', sanitizerFn(newValue));`
* 1 << SHIFT_REF | Attr, 'title', sanitizerFn,
*
* // The following OpCodes represent: `<div i18n>Hello {{exp3}}!">`
* // If `changeMask & 0b100`
* // has changed then execute update OpCodes.
* // has NOT changed then skip `4` values and start processing next OpCodes.
* 0b100, 4,
* // Concatenate `newValue = 'Hello ' + lView[bindIndex -2] + '!';`.
* 'Hello ', -2, '!',
* // Update text: `lView[1].textContent = newValue;`
* 1 << SHIFT_REF | Text,
*
* // The following OpCodes represent: `<div i18n>{exp4, plural, ... }">`
* // If `changeMask & 0b1000`
* // has changed then execute update OpCodes.
* // has NOT changed then skip `2` values and start processing next OpCodes.
* 0b1000, 2,
* // Concatenate `newValue = lView[bindIndex -1];`.
* -1,
* // Switch ICU: `icuSwitchCase(lView[1], 0, newValue);`
* 0 << SHIFT_ICU | 1 << SHIFT_REF | IcuSwitch,
*
* // Note `changeMask & -1` is always true, so the IcuUpdate will always execute.
* -1, 1,
* // Update ICU: `icuUpdateCase(lView[1], 0);`
* 0 << SHIFT_ICU | 1 << SHIFT_REF | IcuUpdate,
*
* ];
* ```
*
*/
interface I18nUpdateOpCodes extends Array<string | number | SanitizerFn | null>, I18nDebug {
__brand__: 'I18nUpdateOpCodes';
}
/**
* Store information for the i18n translation block.
*/
interface TI18n {
/**
* A set of OpCodes which will create the Text Nodes and ICU anchors for the translation blocks.
*
* NOTE: The ICU anchors are filled in with ICU Update OpCode.
*/
create: I18nCreateOpCodes;
/**
* A set of OpCodes which will be executed on each change detection to determine if any changes to
* DOM are required.
*/
update: I18nUpdateOpCodes;
/**
* An AST representing the translated message. This is used for hydration (and serialization),
* while the Update and Create OpCodes are used at runtime.
*/
ast: Array<I18nNode>;
/**
* Index of a parent TNode, which represents a host node for this i18n block.
*/
parentTNodeIndex: number;
}
/**
* Defines the ICU type of `select` or `plural`
*/
declare const enum IcuType {
select = 0,
plural = 1
}
interface TIcu {
/**
* Defines the ICU type of `select` or `plural`
*/
type: IcuType;
/**
* Index in `LView` where the anchor node is stored. `<!-- ICU 0:0 -->`
*/
anchorIdx: number;
/**
* Currently selected ICU case pointer.
*
* `lView[currentCaseLViewIndex]` stores the currently selected case. This is needed to know how
* to clean up the current case when transitioning no the new case.
*
* If the value stored is:
* `null`: No current case selected.
* `<0`: A flag which means that the ICU just switched and that `icuUpdate` must be executed
* regardless of the `mask`. (After the execution the flag is cleared)
* `>=0` A currently selected case index.
*/
currentCaseLViewIndex: number;
/**
* A list of case values which the current ICU will try to match.
*
* The last value is `other`
*/
cases: any[];
/**
* A set of OpCodes to apply in order to build up the DOM render tree for the ICU
*/
create: IcuCreateOpCodes[];
/**
* A set of OpCodes to apply in order to destroy the DOM render tree for the ICU.
*/
remove: I18nRemoveOpCodes[];
/**
* A set of OpCodes to apply in order to update the DOM render tree for the ICU bindings.
*/
update: I18nUpdateOpCodes[];
}
type I18nNode = I18nTextNode | I18nElementNode | I18nICUNode | I18nPlaceholderNode;
/**
* Represents a block of text in a translation, such as `Hello, {{ name }}!`.
*/
interface I18nTextNode {
/** The AST node kind */
kind: I18nNodeKind.TEXT;
/** The LView index */
index: number;
}
/**
* Represents a simple DOM element in a translation, such as `<div>...</div>`
*/
interface I18nElementNode {
/** The AST node kind */
kind: I18nNodeKind.ELEMENT;
/** The LView index */
index: number;
/** The child nodes */
children: Array<I18nNode>;
}
/**
* Represents an ICU in a translation.
*/
interface I18nICUNode {
/** The AST node kind */
kind: I18nNodeKind.ICU;
/** The LView index */
index: number;
/** The branching cases */
cases: Array<Array<I18nNode>>;
/** The LView index that stores the active case */
currentCaseLViewIndex: number;
}
/**
* Represents special content that is embedded into the translation. This can
* either be a special built-in element, such as <ng-container> and <ng-content>,
* or it can be a sub-template, for example, from a structural directive.
*/
interface I18nPlaceholderNode {
/** The AST node kind */
kind: I18nNodeKind.PLACEHOLDER;
/** The LView index */
index: number;
/** The child nodes */
children: Array<I18nNode>;
/** The placeholder type */
type: I18nPlaceholderType;
}
declare const enum I18nPlaceholderType {
ELEMENT = 0,
SUBTEMPLATE = 1
}
declare const enum I18nNodeKind {
TEXT = 0,
ELEMENT = 1,
PLACEHOLDER = 2,
ICU = 3
}
/**
* The goal here is to make sure that the browser DOM API is the Renderer.
* We do this by defining a subset of DOM API to be the renderer and then
* use that at runtime for rendering.
*
* At runtime we can then use the DOM api directly, in server or web-worker
* it will be easy to implement such API.
*/
/** Subset of API needed for appending elements and text nodes. */
interface RNode {
/**
* Returns the parent Element, Document, or DocumentFragment
*/
parentNode: RNode | null;
/**
* Returns the parent Element if there is one
*/
parentElement: RElement | null;
/**
* Gets the Node immediately following this one in the parent's childNodes
*/
nextSibling: RNode | null;
/**
* Insert a child node.
*
* Used exclusively for adding View root nodes into ViewAnchor location.
*/
insertBefore(newChild: RNode, refChild: RNode | null, isViewRoot: boolean): void;
/**
* Append a child node.
*
* Used exclusively for building up DOM which are static (ie not View roots)
*/
appendChild(newChild: RNode): RNode;
}
/**
* Subset of API needed for writing attributes, properties, and setting up
* listeners on Element.
*/
interface RElement extends RNode {
firstChild: RNode | null;
style: RCssStyleDeclaration;
classList: RDomTokenList;
className: string;
tagName: string;
textContent: string | null;
hasAttribute(name: string): boolean;
getAttribute(name: string): string | null;
setAttribute(name: string, value: string | TrustedHTML | TrustedScript | TrustedScriptURL): void;
removeAttribute(name: string): void;
setAttributeNS(namespaceURI: string, qualifiedName: string, value: string | TrustedHTML | TrustedScript | TrustedScriptURL): void;
addEventListener(type: string, listener: EventListener, useCapture?: boolean): void;
removeEventListener(type: string, listener?: EventListener, options?: boolean): void;
remove(): void;
setProperty?(name: string, value: any): void;
}
interface RCssStyleDeclaration {
removeProperty(propertyName: string): string;
setProperty(propertyName: string, value: string | null, priority?: string): void;
}
interface RDomTokenList {
add(token: string): void;
remove(token: string): void;
}
interface RText extends RNode {
textContent: string | null;
}
interface RComment extends RNode {
textContent: string | null;
}
/**
* Keys within serialized view data structure to represent various
* parts. See the `SerializedView` interface below for additional information.
*/
declare const ELEMENT_CONTAINERS = "e";
declare const TEMPLATES = "t";
declare const CONTAINERS = "c";
declare const MULTIPLIER = "x";
declare const NUM_ROOT_NODES = "r";
declare const TEMPLATE_ID = "i";
declare const NODES = "n";
declare const DISCONNECTED_NODES = "d";
declare const I18N_DATA = "l";
declare const DEFER_BLOCK_ID = "di";
declare const DEFER_BLOCK_STATE = "s";
/**
* Represents element containers within this view, stored as key-value pairs
* where key is an index of a container in an LView (also used in the
* `elementContainerStart` instruction), the value is the number of root nodes
* in this container. This information is needed to locate an anchor comment
* node that goes after all container nodes.
*/
interface SerializedElementContainers {
[key: number]: number;
}
/**
* Serialized data structure that contains relevant hydration
* annotation information that describes a given hydration boundary
* (e.g. a component).
*/
interface SerializedView {
/**
* Serialized information about <ng-container>s.
*/
[ELEMENT_CONTAINERS]?: SerializedElementContainers;
/**
* Serialized information about templates.
* Key-value pairs where a key is an index of the corresponding
* `template` instruction and the value is a unique id that can
* be used during hydration to identify that template.
*/
[TEMPLATES]?: Record<number, string>;
/**
* Serialized information about view containers.
* Key-value pairs where a key is an index of the corresponding
* LContainer entry within an LView, and the value is a list
* of serialized information about views within this container.
*/
[CONTAINERS]?: Record<number, SerializedContainerView[]>;
/**
* Serialized information about nodes in a template.
* Key-value pairs where a key is an index of the corresponding
* DOM node in an LView and the value is a path that describes
* the location of this node (as a set of navigation instructions).
*/
[NODES]?: Record<number, string>;
/**
* A list of ids which represents a set of nodes disconnected
* from the DOM tree at the serialization time, but otherwise
* present in the internal data structures.
*
* This information is used to avoid triggering the hydration
* logic for such nodes and instead use a regular "creation mode".
*/
[DISCONNECTED_NODES]?: number[];
/**
* Serialized information about i18n blocks in a template.
* Key-value pairs where a key is an index of the corresponding
* i18n entry within an LView, and the value is a list of
* active ICU cases.
*/
[I18N_DATA]?: Record<number, number[]>;
/**
* If this view represents a `@defer` block, this field contains
* unique id of the block.
*/
[DEFER_BLOCK_ID]?: string;
/**
* This field represents a status, based on the `DeferBlockState` enum.
*/
[DEFER_BLOCK_STATE]?: number;
}
/**
* Serialized data structure that contains relevant hydration
* annotation information about a view that is a part of a
* ViewContainer collection.
*/
interface SerializedContainerView extends SerializedView {
/**
* Unique id that represents a TView that was used to create
* a given instance of a view:
* - TViewType.Embedded: a unique id generated during serialization on the server
* - TViewType.Component: an id generated based on component properties
* (see `getComponentId` function for details)
*/
[TEMPLATE_ID]: string;
/**
* Number of root nodes that belong to this view.
* This information is needed to effectively traverse the DOM tree
* and identify segments that belong to different views.
*/
[NUM_ROOT_NODES]: number;
/**
* Number of times this view is repeated.
* This is used to avoid serializing and sending the same hydration
* information about similar views (for example, produced by *ngFor).
*/
[MULTIPLIER]?: number;
}
/**
* An object that contains hydration-related information serialized
* on the server, as well as the necessary references to segments of
* the DOM, to facilitate the hydration process for a given hydration
* boundary on the client.
*/
interface DehydratedView {
/**
* The readonly hydration annotation data.
*/
data: Readonly<SerializedView>;
/**
* A reference to the first child in a DOM segment associated
* with a given hydration boundary.
*
* Once a view becomes hydrated, the value is set to `null`, which
* indicates that further detaching/attaching view actions should result
* in invoking corresponding DOM actions (attaching DOM nodes action is
* skipped when we hydrate, since nodes are already in the DOM).
*/
firstChild: RNode | null;
/**
* Stores references to first nodes in DOM segments that
* represent either an <ng-container> or a view container.
*/
segmentHeads?: {
[index: number]: RNode | null;
};
/**
* An instance of a Set that represents nodes disconnected from
* the DOM tree at the serialization time, but otherwise present
* in the internal data structures.
*
* The Set is based on the `SerializedView[DISCONNECTED_NODES]` data
* and is needed to have constant-time lookups.
*
* If the value is `null`, it means that there were no disconnected
* nodes detected in this view at serialization time.
*/
disconnectedNodes?: Set<number> | null;
/**
* A mapping from a view to the first child to begin claiming nodes.
*
* This mapping is generated by an i18n block, and is the source of
* truth for the nodes inside of it.
*/
i18nNodes?: Map<number, RNode | null>;
/**
* A mapping from the index of an ICU node to dehydrated data for it.
*
* This information is used during the hydration process on the client.
* ICU cases that were active during server-side rendering will be added
* to the map. The hydration logic will "claim" matching cases, removing
* them from the map. The remaining entries are "unclaimed", and will be
* removed from the DOM during hydration cleanup.
*/
dehydratedIcuData?: Map<number, DehydratedIcuData>;
}
/**
* An object that contains hydration-related information serialized
* on the server, as well as the necessary references to segments of
* the DOM, to facilitate the hydration process for a given view
* inside a view container (either an embedded view or a view created
* for a component).
*/
interface DehydratedContainerView extends DehydratedView {
data: Readonly<SerializedContainerView>;
}
/**
* An object that contains information about a dehydrated ICU case,
* to facilitate cleaning up ICU cases that were active during
* server-side rendering, but not during hydration.
*/
interface DehydratedIcuData {
/**
* The case index that this data represents.
*/
case: number;
/**
* A reference back to the AST for the ICU node. This allows the
* AST to be used to clean up dehydrated nodes.
*/
node: I18nICUNode;
}
/**
* `KeyValueArray` is an array where even positions contain keys and odd positions contain values.
*
* `KeyValueArray` provides a very efficient way of iterating over its contents. For small
* sets (~10) the cost of binary searching an `KeyValueArray` has about the same performance
* characteristics that of a `Map` with significantly better memory footprint.
*
* If used as a `Map` the keys are stored in alphabetical order so that they can be binary searched
* for retrieval.
*
* See: `keyValueArraySet`, `keyValueArrayGet`, `keyValueArrayIndexOf`, `keyValueArrayDelete`.
*/
interface KeyValueArray<VALUE> extends Array<VALUE | string> {
__brand__: 'array-map';
}
/**
* Value stored in the `TData` which is needed to re-concatenate the styling.
*
* See: `TStylingKeyPrimitive` and `TStylingStatic`
*/
type TStylingKey = TStylingKeyPrimitive | TStylingStatic;
/**
* The primitive portion (`TStylingStatic` removed) of the value stored in the `TData` which is
* needed to re-concatenate the styling.
*
* - `string`: Stores the property name. Used with `ɵɵstyleProp`/`ɵɵclassProp` instruction.
* - `null`: Represents map, so there is no name. Used with `ɵɵstyleMap`/`ɵɵclassMap`.
* - `false`: Represents an ignore case. This happens when `ɵɵstyleProp`/`ɵɵclassProp` instruction
* is combined with directive which shadows its input `@Input('class')`. That way the binding
* should not participate in the styling resolution.
*/
type TStylingKeyPrimitive = string | null | false;
/**
* Store the static values for the styling binding.
*
* The `TStylingStatic` is just `KeyValueArray` where key `""` (stored at location 0) contains the
* `TStylingKey` (stored at location 1). In other words this wraps the `TStylingKey` such that the
* `""` contains the wrapped value.
*
* When instructions are resolving styling they may need to look forward or backwards in the linked
* list to resolve the value. For this reason we have to make sure that he linked list also contains
* the static values. However the list only has space for one item per styling instruction. For this
* reason we store the static values here as part of the `TStylingKey`. This means that the
* resolution function when looking for a value needs to first look at the binding value, and than
* at `TStylingKey` (if it exists).
*
* Imagine we have:
*
* ```angular-ts
* <div class="TEMPLATE" my-dir>
*
* @Directive({
* host: {
* class: 'DIR',
* '[class.dynamic]': 'exp' // ɵɵclassProp('dynamic', ctx.exp);
* }
* })
* ```
*
* In the above case the linked list will contain one item:
*
* ```ts
* // assume binding location: 10 for `ɵɵclassProp('dynamic', ctx.exp);`
* tData[10] = <TStylingStatic>[
* '': 'dynamic', // This is the wrapped value of `TStylingKey`
* 'DIR': true, // This is the default static value of directive binding.
* ];
* tData[10 + 1] = 0; // We don't have prev/next.
*
* lView[10] = undefined; // assume `ctx.exp` is `undefined`
* lView[10 + 1] = undefined; // Just normalized `lView[10]`
* ```
*
* So when the function is resolving styling value, it first needs to look into the linked list
* (there is none) and than into the static `TStylingStatic` too see if there is a default value for
* `dynamic` (there is not). Therefore it is safe to remove it.
*
* If setting `true` case:
* ```ts
* lView[10] = true; // assume `ctx.exp` is `true`
* lView[10 + 1] = true; // Just normalized `lView[10]`
* ```
* So when the function is resolving styling value, it first needs to look into the linked list
* (there is none) and than into `TNode.residualClass` (TNode.residualStyle) which contains
* ```ts
* tNode.residualClass = [
* 'TEMPLATE': true,
* ];
* ```
*
* This means that it is safe to add class.
*/
interface TStylingStatic extends KeyValueArray<any> {
}
/**
* This is a branded number which contains previous and next index.
*
* When we come across styling instructions we need to store the `TStylingKey` in the correct
* order so that we can re-concatenate the styling value in the desired priority.
*
* The insertion can happen either at the:
* - end of template as in the case of coming across additional styling instruction in the template
* - in front of the template in the case of coming across additional instruction in the
* `hostBindings`.
*
* We use `TStylingRange` to store the previous and next index into the `TData` where the template
* bindings can be found.
*
* - bit 0 is used to mark that the previous index has a duplicate for current value.
* - bit 1 is used to mark that the next index has a duplicate for the current value.
* - bits 2-16 are used to encode the next/tail of the template.
* - bits 17-32 are used to encode the previous/head of template.
*
* NODE: *duplicate* false implies that it is statically known that this binding will not collide
* with other bindings and therefore there is no need to check other bindings. For example the
* bindings in `<div [style.color]="exp" [style.width]="exp">` will never collide and will have
* their bits set accordingly. Previous duplicate means that we may need to check previous if the
* current binding is `null`. Next duplicate means that we may need to check next bindings if the
* current binding is not `null`.
*
* NOTE: `0` has special significance and represents `null` as in no additional pointer.
*/
type TStylingRange = number & {
__brand__: 'TStylingRange';
};
/**
* A set of marker values to be used in the attributes arrays. These markers indicate that some
* items are not regular attributes and the processing should be adapted accordingly.
*/
declare const enum AttributeMarker {
/**
* An implicit marker which indicates that the value in the array are of `attributeKey`,
* `attributeValue` format.
*
* NOTE: This is implicit as it is the type when no marker is present in array. We indicate that
* it should not be present at runtime by the negative number.
*/
ImplicitAttributes = -1,
/**
* Marker indicates that the following 3 values in the attributes array are:
* namespaceUri, attributeName, attributeValue
* in that order.
*/
NamespaceURI = 0,
/**
* Signals class declaration.
*
* Each value following `Classes` designates a class name to include on the element.
* ## Example:
*
* Given:
* ```html
* <div class="foo bar baz">...</div>
* ```
*
* the generated code is:
* ```ts
* var _c1 = [AttributeMarker.Classes, 'foo', 'bar', 'baz'];
* ```
*/
Classes = 1,
/**
* Signals style declaration.
*
* Each pair of values following `Styles` designates a style name and value to include on the
* element.
* ## Example:
*
* Given:
* ```html
* <div style="width:100px; height:200px; color:red">...</div>
* ```
*
* the generated code is:
* ```ts
* var _c1 = [AttributeMarker.Styles, 'width', '100px', 'height'. '200px', 'color', 'red'];
* ```
*/
Styles = 2,
/**
* Signals that the following attribute names were extracted from input or output bindings.
*
* For example, given the following HTML:
*
* ```html
* <div moo="car" [foo]="exp" (bar)="doSth()">
* ```
*
* the generated code is:
*
* ```ts
* var _c1 = ['moo', 'car', AttributeMarker.Bindings, 'foo', 'bar'];
* ```
*/
Bindings = 3,
/**
* Signals that the following attribute names were hoisted from an inline-template declaration.
*
* For example, given the following HTML:
*
* ```html
* <div *ngFor="let value of values; trackBy:trackBy" dirA [dirB]="value">
* ```
*
* the generated code for the `template()` instruction would include:
*
* ```
* ['dirA', '', AttributeMarker.Bindings, 'dirB', AttributeMarker.Template, 'ngFor', 'ngForOf',
* 'ngForTrackBy', 'let-value']
* ```
*
* while the generated code for the `element()` instruction inside the template function would
* include:
*
* ```
* ['dirA', '', AttributeMarker.Bindings, 'dirB']
* ```
*/
Template = 4,
/**
* Signals that the following attribute is `ngProjectAs` and its value is a parsed
* `CssSelector`.
*
* For example, given the following HTML:
*
* ```html
* <h1 attr="value" ngProjectAs="[title]">
* ```
*
* the generated code for the `element()` instruction would include:
*
* ```ts
* ['attr', 'value', AttributeMarker.ProjectAs, ['', 'title', '']]
* ```
*/
ProjectAs = 5,
/**
* Signals that the following attribute will be translated by runtime i18n
*
* For example, given the following HTML:
*
* ```html
* <div moo="car" foo="value" i18n-foo [bar]="binding" i18n-bar>
* ```
*
* the generated code is:
*
* ```ts
* var _c1 = ['moo', 'car', AttributeMarker.I18n, 'foo', 'bar'];
* ```
*/
I18n = 6
}
/**
* Expresses a single CSS Selector.
*
* Beginning of array
* - First index: element name
* - Subsequent odd indices: attr keys
* - Subsequent even indices: attr values
*
* After SelectorFlags.CLASS flag
* - Class name values
*
* SelectorFlags.NOT flag
* - Changes the mode to NOT
* - Can be combined with other flags to set the element / attr / class mode
*
* e.g. SelectorFlags.NOT | SelectorFlags.ELEMENT
*
* Example:
* Original: `div.foo.bar[attr1=val1][attr2]`
* Parsed: ['div', 'attr1', 'val1', 'attr2', '', SelectorFlags.CLASS, 'foo', 'bar']
*
* Original: 'div[attr1]:not(.foo[attr2])
* Parsed: [
* 'div', 'attr1', '',
* SelectorFlags.NOT | SelectorFlags.ATTRIBUTE 'attr2', '', SelectorFlags.CLASS, 'foo'
* ]
*
* See more examples in node_selector_matcher_spec.ts
*/
type CssSelector = (string | SelectorFlags)[];
/**
* A list of CssSelectors.
*
* A directive or component can have multiple selectors. This type is used for
* directive defs so any of the selectors in the list will match that directive.
*
* Original: 'form, [ngForm]'
* Parsed: [['form'], ['', 'ngForm', '']]
*/
type CssSelectorList = CssSelector[];
/**
* List of slots for a projection. A slot can be either based on a parsed CSS selector
* which will be used to determine nodes which are projected into that slot.
*
* When set to "*", the slot is reserved and can be used for multi-slot projection
* using {@link ViewContainerRef#createComponent}. The last slot that specifies the
* wildcard selector will retrieve all projectable nodes which do not match any selector.
*/
type ProjectionSlots = (CssSelectorList | '*')[];
/** Flags used to build up CssSelectors */
declare const enum SelectorFlags {
/** Indicates this is the beginning of a new negative selector */
NOT = 1,
/** Mode for matching attributes */
ATTRIBUTE = 2,
/** Mode for matching tag names */
ELEMENT = 4,
/** Mode for matching class names */
CLASS = 8
}
/**
* TNodeType corresponds to the {@link TNode} `type` property.
*
* NOTE: type IDs are such that we use each bit to denote a type. This is done so that we can easily
* check if the `TNode` is of more than one type.
*
* `if (tNode.type === TNodeType.Text || tNode.type === TNode.Element)`
* can be written as:
* `if (tNode.type & (TNodeType.Text | TNodeType.Element))`
*
* However any given `TNode` can only be of one type.
*/
declare const enum TNodeType {
/**
* The TNode contains information about a DOM element aka {@link RText}.
*/
Text = 1,
/**
* The TNode contains information about a DOM element aka {@link RElement}.
*/
Element = 2,
/**
* The TNode contains information about an {@link LContainer} for embedded views.
*/
Container = 4,
/**
* The TNode contains information about an `<ng-container>` element {@link RNode}.
*/
ElementContainer = 8,
/**
* The TNode contains information about an `<ng-content>` projection
*/
Projection = 16,
/**
* The TNode contains information about an ICU comment used in `i18n`.
*/
Icu = 32,
/**
* Special node type representing a placeholder for future `TNode` at this location.
*
* I18n translation blocks are created before the element nodes which they contain. (I18n blocks
* can span over many elements.) Because i18n `TNode`s (representing text) are created first they
* often may need to point to element `TNode`s which are not yet created. In such a case we create
* a `Placeholder` `TNode`. This allows the i18n to structurally link the `TNode`s together
* without knowing any information about the future nodes which will be at that location.
*
* On `firstCreatePass` When element instruction executes it will try to create a `TNode` at that
* location. Seeing a `Placeholder` `TNode` already there tells the system that it should reuse
* existing `TNode` (rather than create a new one) and just update the missing information.
*/
Placeholder = 64,
/**
* The TNode contains information about a `@let` declaration.
*/
LetDeclaration = 128,
AnyRNode = 3,// Text | Element
AnyContainer = 12
}
/**
* Corresponds to the TNode.flags property.
*/
declare const enum TNodeFlags {
/** Bit #1 - This bit is set if the node is a host for any directive (including a component) */
isDirectiveHost = 1,
/** Bit #2 - This bit is set if the node has been projected */
isProjected = 2,
/** Bit #3 - This bit is set if any directive on this node has content queries */
hasContentQuery = 4,
/** Bit #4 - This bit is set if the node has any "class" inputs */
hasClassInput = 8,
/** Bit #5 - This bit is set if the node has any "style" inputs */
hasStyleInput = 16,
/** Bit #6 - This bit is set if the node has been detached by i18n */
isDetached = 32,
/**
* Bit #7 - This bit is set if the node has directives with host bindings.
*
* This flags allows us to guard host-binding logic and invoke it only on nodes
* that actually have directives with host bindings.
*/
hasHostBindings = 64,
/**
* Bit #8 - This bit is set if the node is a located inside skip hydration block.
*/
inSkipHydrationBlock = 128,
/**
* Bit #9 - This bit is set if the node is a start of a set of control flow blocks.
*/
isControlFlowStart = 256,
/**
* Bit #10 - This bit is set if the node is within a set of control flow blocks.
*/
isInControlFlow = 512
}
/**
* Corresponds to the TNode.providerIndexes property.
*/
declare const enum TNodeProviderIndexes {
/** The index of the first provider on this node is encoded on the least significant bits. */
ProvidersStartIndexMask = 1048575,
/**
* The count of view providers from the component on this node is
* encoded on the 20 most significant bits.
*/
CptViewProvidersCountShift = 20,
CptViewProvidersCountShifter = 1048576
}
/**
* A combination of:
* - Attribute names and values.
* - Special markers acting as flags to alter attributes processing.
* - Parsed ngProjectAs selectors.
*/
type TAttributes = (string | AttributeMarker | CssSelector)[];
/**
* Constants that are associated with a view. Includes:
* - Attribute arrays.
* - Local definition arrays.
* - Translated messages (i18n).
*/
type TConstants = (TAttributes | string)[];
/**
* Factory function that returns an array of consts. Consts can be represented as a function in
* case any additional statements are required to define consts in the list. An example is i18n
* where additional i18n calls are generated, which should be executed when consts are requested
* for the first time.
*/
type TConstantsFactory = () => TConstants;
/**
* TConstants type that describes how the `consts` field is generated on ComponentDef: it can be
* either an array or a factory function that returns that array.
*/
type TConstantsOrFactory = TConstants | TConstantsFactory;
/**
* 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
*/
interface TNode {
/** The type of the TNode. See TNodeType. */
type: TNodeType;
/**
* Index of the TNode in TView.data and corresponding native element in LView.
*
* This is necessary to get from any TNode to its corresponding native element when
* traversing the node tree.
*
* If index is -1, this is a dynamically created container node or embedded view node.
*/
index: number;
/**
* Insert before existing DOM node index.
*
* When DOM nodes are being inserted, normally they are being appended as they are created.
* Under i18n case, the translated text nodes are created ahead of time as part of the
* `ɵɵi18nStart` instruction which means that this `TNode` can't just be appended and instead
* needs to be inserted using `insertBeforeIndex` semantics.
*
* Additionally sometimes it is necessary to insert new text nodes as a child of this `TNode`. In
* such a case the value stores an array of text nodes to insert.
*
* Example:
* ```html
* <div i18n>
* Hello <span>World</span>!
* </div>
* ```
* In the above example the `ɵɵi18nStart` instruction can create `Hello `, `World` and `!` text
* nodes. It can also insert `Hello ` and `!` text node as a child of `<div>`, but it can't
* insert `World` because the `<span>` node has not yet been created. In such a case the
* `<span>` `TNode` will have an array which will direct the `<span>` to not only insert
* itself in front of `!` but also to insert the `World` (created by `ɵɵi18nStart`) into
* `<span>` itself.
*
* Pseudo code:
* ```ts
* if (insertBeforeIndex === null) {
* // append as normal
* } else if (Array.isArray(insertBeforeIndex)) {
* // First insert current `TNode` at correct location
* const currentNode = lView[this.index];
* parentNode.insertBefore(currentNode, lView[this.insertBeforeIndex[0]]);
* // Now append all of the children
* for(let i=1; i<this.insertBeforeIndex; i++) {
* currentNode.appendChild(lView[this.insertBeforeIndex[i]]);
* }
* } else {
* parentNode.insertBefore(lView[this.index], lView[this.insertBeforeIndex])
* }
* ```
* - null: Append as normal using `parentNode.appendChild`
* - `number`: Append using
* `parentNode.insertBefore(lView[this.index], lView[this.insertBeforeIndex])`
*
* *Initialization*
*
* Because `ɵɵi18nStart` executes before nodes are created, on `TView.firstCreatePass` it is not
* possible for `ɵɵi18nStart` to set the `insertBeforeIndex` value as the corresponding `TNode`
* has not yet been created. For this reason the `ɵɵi18nStart` creates a `TNodeType.Placeholder`
* `TNode` at that location. See `TNodeType.Placeholder` for more information.
*/
insertBeforeIndex: InsertBeforeIndex;
/**
* The index of the closest injector in this node's LView.
*
* If the index === -1, there is no injector on this node or any ancestor node in this view.
*
* If the index !== -1, it is the index of this node's injector OR the index of a parent
* injector in the same view. We pass the parent injector index down the node tree of a view so
* it's possible to find the parent injector without walking a potentially deep node tree.
* Injector indices are not set across view boundaries because there could be multiple component
* hosts.
*
* If tNode.injectorIndex === tNode.parent.injectorIndex, then the index belongs to a parent
* injector.
*/
injectorIndex: number;
/** Stores starting index of the directives. */
directiveStart: number;
/**
* Stores final exclusive index of the directives.
*
* The area right behind the `directiveStart-directiveEnd` range is used to allocate the
* `HostBindingFunction` `vars` (or null if no bindings.) Therefore `directiveEnd` is used to set
* `LFrame.bindingRootIndex` before `HostBindingFunction` is executed.
*/
directiveEnd: number;
/**
* Offset from the `directiveStart` at which the component (one at most) of the node is stored.
* Set to -1 if no components have been applied to the node. Component index can be found using
* `directiveStart + componentOffset`.
*/
componentOffset: number;
/**
* Stores the last directive which had a styling instruction.
*
* Initial value of this is `-1` which means that no `hostBindings` styling instruction has
* executed. As `hostBindings` instructions execute they set the value to the index of the
* `DirectiveDef` which contained the last `hostBindings` styling instruction.
*
* Valid values are:
* - `-1` No `hostBindings` instruction has executed.
* - `directiveStart <= directiveStylingLast < directiveEnd`: Points to the `DirectiveDef` of
* the last styling instruction which executed in the `hostBindings`.
*
* This data is needed so that styling instructions know which static styling data needs to be
* collected from the `DirectiveDef.hostAttrs`. A styling instruction needs to collect all data
* since last styling instruction.
*/
directiveStylingLast: number;
/**
* Stores indexes of property bindings. This field is only set in the ngDevMode and holds
* indexes of property bindings so TestBed can get bound property metadata for a given node.
*/
propertyBindings: number[] | null;
/**
* Stores if Node isComponent, isProjected, hasContentQuery, hasClassInput and hasStyleInput
* etc.
*/
flags: TNodeFlags;
/**
* This number stores two values using its bits:
*
* - the index of the first provider on that node (first 16 bits)
* - the count of view providers from the component on this node (last 16 bits)
*/
providerIndexes: TNodeProviderIndexes;
/**
* The value name associated with this node.
* if type:
* `TNodeType.Text`: text value
* `TNodeType.Element`: tag name
* `TNodeType.ICUContainer`: `TIcu`
*/
value: any;
/**
* Attributes associated with an element. We need to store attributes to support various
* use-cases (attribute injection, content projection with selectors, directives matching).
* 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.
*
* For regular attributes a name of an attribute and its value alternate in the array.
* e.g. ['role', 'checkbox']
* This array can contain flags that will indicate "special attributes" (attributes with
* namespaces, attributes extracted from bindings and outputs).
*/
attrs: TAttributes | null;
/**
* Same as `TNode.attrs` but contains merged data across all directive host bindings.
*
* We need to keep `attrs` as unmerged so that it can be used for attribute selectors.
* We merge attrs here so that it can be used in a performant way for initial rendering.
*
* The `attrs` are merged in first pass in following order:
* - Component's `hostAttrs`
* - Directives' `hostAttrs`
* - Template `TNode.attrs` associated with the current `TNode`.
*/
mergedAttrs: TAttributes | 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]`