UNPKG

@angular/core

Version:

Angular - the core framework

1,333 lines (1,319 loc) 280 kB
/** * @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]`