UNPKG

@angular/core

Version:

Angular - the core framework

1,079 lines (1,065 loc) 1.33 MB
/** * @license Angular v20.0.3 * (c) 2010-2025 Google LLC. https://angular.io/ * License: MIT */ import { attachInjectFlag, _global, ɵɵdefineInjectable as __defineInjectable, ɵɵdefineInjector as __defineInjector, ɵɵinject as __inject, ɵɵinvalidFactoryDep as __invalidFactoryDep, resolveForwardRef, newArray, EMPTY_OBJ, assertString, assertFirstCreatePass, assertDefined, assertNotEqual, FLAGS, assertEqual, isInCheckNoChangesMode, PREORDER_HOOK_FLAGS, throwError, assertNumber, assertGreaterThan, HEADER_OFFSET, DECLARATION_VIEW, NG_FACTORY_DEF, isForwardRef, getFactoryDef, assertIndexInRange, assertTNodeForLView, enterDI, runInInjectorProfilerContext, getCurrentTNode, getLView, emitInjectorToCreateInstanceEvent, emitInstanceCreatedByInjectorEvent, throwProviderNotFoundError, leaveDI, assertNodeInjector, throwCyclicDependencyError, stringifyForError, setInjectorProfilerContext, setInjectImplementation, assertDirectiveDef, NG_ELEMENT_ID, convertToBitFlags, isRootView, T_HOST, TVIEW, injectRootLimpMode, isComponentDef, EMBEDDED_VIEW_INJECTOR, INJECTOR$1 as INJECTOR, DECLARATION_COMPONENT_VIEW, isComponentHost, RuntimeError, NG_PROV_DEF, getClosureSafeProperty, getNativeByTNode, flatten, arrayEquals, ID, isLView, assertDomNode, unwrapRNode, getComponentLViewByIndex, CONTEXT, EMPTY_ARRAY, assertLView, HOST, CHILD_HEAD, NEXT, isLContainer, getLViewParent, Injector, CLEANUP, getComponentDef, getDirectiveDef, InjectionToken, inject, isContentQueryHost, setCurrentQueryIndex, XSS_SECURITY_URL, renderStringify, ENVIRONMENT, makeEnvironmentProviders, formatRuntimeError, resetPreOrderHookFlags, PARENT, RENDERER, HYDRATION, CHILD_TAIL, assertSame, assertFirstUpdatePass, getSelectedIndex, getTView, assertIndexInDeclRange, setSelectedIndex, assertNotSame, setCurrentDirectiveIndex, INTERNAL_APPLICATION_ERROR_HANDLER, stringify, getCurrentDirectiveIndex, unwrapLView, isCreationMode, assertNotReactive, enterView, QUERIES, leaveView, assertLContainer, DECLARATION_LCONTAINER, MOVED_VIEWS, isDestroyed, REACTIVE_TEMPLATE_CONSUMER, ON_DESTROY_HOOKS, assertFunction, EFFECTS, assertProjectionSlots, NATIVE, assertParentView, CONTAINER_HEADER_OFFSET, AFTER_RENDER_SEQUENCES_TO_ADD, markAncestorsForTraversal, markViewForRefresh, setIsRefreshingViews, isExhaustiveCheckNoChanges, requiresRefreshOrTraversal, setIsInCheckNoChangesMode, CheckNoChangesMode, setBindingIndex, EFFECTS_TO_SCHEDULE, viewAttachedToChangeDetector, setBindingRootForHostBindings, isRefreshingViews, removeFromArray, addToArray, updateAncestorTraversalFlagsOnAttach, storeLViewOnDestroy, VIEW_REFS, assertGreaterThanOrEqual, isInI18nBlock, assertTNodeForTView, setCurrentTNode, getCurrentParentTNode, getCurrentTNodePlaceholderOk, isCurrentTNodeParent, isInSkipHydrationBlock as isInSkipHydrationBlock$1, assertTNode, assertTIcu, assertNumberInRange, DEHYDRATED_VIEWS, getNgModuleDef, getPipeDef as getPipeDef$1, getNgModuleDefOrThrow, isStandalone, concatStringsWithSpace, assertInjectImplementationNotEqual, emitInjectEvent, getConstant, assertLessThan, getOrCreateTViewCleanup, getOrCreateLViewCleanup, isDirectiveHost, assertNotDefined, nextBindingIndex, getSelectedTNode, getDirectiveDefOrThrow, getTNode, assertComponentType, debugStringifyTypeForError, ChangeDetectionScheduler, EnvironmentInjector, SVG_NAMESPACE, MATH_ML_NAMESPACE, viewAttachedToContainer, storeCleanupWithContext, signal, createInjectorWithoutInjectorInstances, R3Injector, getNullInjector, internalImportProvidersFrom, initNgDevMode, fillProperties, wasLastNodeCreated, lastNodeWasCreated, getBindingsEnabled, isInInjectionContext, DestroyRef, PendingTasksInternal, noop, ErrorHandler, assertNotInReactiveContext, assertInInjectionContext, ViewContext, removeLViewOnDestroy, walkUpViews, getNativeByIndex, assertElement, arrayInsert2, arraySplice, setInjectorProfiler, NullInjector, ENVIRONMENT_INITIALIZER, INJECTOR_DEF_TYPES, walkProviderTree, getInjectorDef, deepForEach, isTypeProvider, isSignal, runInInjectionContext, ZONELESS_ENABLED, EffectScheduler, PendingTasks, getBindingIndex, getElementDepthCount, increaseElementDepthCount, setCurrentTNodeAsNotParent, assertHasParent, isSkipHydrationRootTNode, leaveSkipHydrationBlock, decreaseElementDepthCount, getNamespace, enterSkipHydrationBlock, getCurrentDirectiveDef, assertIndexInExpandoRange, assertOneOf, setInI18nBlock, nextContextImpl, getCurrentQueryIndex, getContextLView, load, keyValueArrayIndexOf, keyValueArraySet, keyValueArrayGet, incrementBindingIndex, isWritableSignal, store, providerToFactory, emitProviderConfiguredEvent, isClassProvider, getBindingRoot, NG_COMP_DEF, ɵɵresetView as __resetView, ɵɵnamespaceHTML as __namespaceHTML, ɵɵnamespaceMathML as __namespaceMathML, ɵɵnamespaceSVG as __namespaceSVG, ɵɵenableBindings as __enableBindings, ɵɵdisableBindings as __disableBindings, ɵɵrestoreView as __restoreView, forwardRef, NG_MOD_DEF, NG_INJ_DEF, NG_DIR_DEF, NG_PIPE_DEF, ZONELESS_SCHEDULER_DISABLED, SCHEDULE_IN_ROOT_ZONE, PROVIDED_ZONELESS, getNativeByTNodeOrNull } from './root_effect_scheduler-DCy1y1b8.mjs'; import { setActiveConsumer, SIGNAL, consumerDestroy, REACTIVE_NODE, consumerPollProducersForChange, consumerBeforeComputation, getActiveConsumer, consumerAfterComputation, createComputed, setThrowInvalidWriteToSignalError } from './signal-nCiHhWf6.mjs'; import { Subject, Subscription } from 'rxjs'; import { setActiveConsumer as setActiveConsumer$1 } from '@angular/core/primitives/signals'; import { map } from 'rxjs/operators'; import { Attribute as Attribute$1 } from './attribute-BWp59EjE.mjs'; /** * Convince closure compiler that the wrapped function has no side-effects. * * Closure compiler always assumes that `toString` has no side-effects. We use this quirk to * allow us to execute a function but have closure compiler mark the call as no-side-effects. * It is important that the return value for the `noSideEffects` function be assigned * to something which is retained otherwise the call to `noSideEffects` will be removed by closure * compiler. */ function noSideEffects(fn) { return { toString: fn }.toString(); } const ANNOTATIONS = '__annotations__'; const PARAMETERS = '__parameters__'; const PROP_METADATA = '__prop__metadata__'; /** * @suppress {globalThis} */ function makeDecorator(name, props, parentClass, additionalProcessing, typeFn) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function DecoratorFactory(...args) { if (this instanceof DecoratorFactory) { metaCtor.call(this, ...args); return this; } const annotationInstance = new DecoratorFactory(...args); return function TypeDecorator(cls) { if (typeFn) typeFn(cls, ...args); // Use of Object.defineProperty is important since it creates non-enumerable property which // prevents the property is copied during subclassing. const annotations = cls.hasOwnProperty(ANNOTATIONS) ? cls[ANNOTATIONS] : Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS]; annotations.push(annotationInstance); return cls; }; } if (parentClass) { DecoratorFactory.prototype = Object.create(parentClass.prototype); } DecoratorFactory.prototype.ngMetadataName = name; DecoratorFactory.annotationCls = DecoratorFactory; return DecoratorFactory; }); } function makeMetadataCtor(props) { return function ctor(...args) { if (props) { const values = props(...args); for (const propName in values) { this[propName] = values[propName]; } } }; } function makeParamDecorator(name, props, parentClass) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function ParamDecoratorFactory(...args) { if (this instanceof ParamDecoratorFactory) { metaCtor.apply(this, args); return this; } const annotationInstance = new ParamDecoratorFactory(...args); ParamDecorator.annotation = annotationInstance; return ParamDecorator; function ParamDecorator(cls, unusedKey, index) { // Use of Object.defineProperty is important since it creates non-enumerable property which // prevents the property is copied during subclassing. const parameters = cls.hasOwnProperty(PARAMETERS) ? cls[PARAMETERS] : Object.defineProperty(cls, PARAMETERS, { value: [] })[PARAMETERS]; // there might be gaps if some in between parameters do not have annotations. // we pad with nulls. while (parameters.length <= index) { parameters.push(null); } (parameters[index] = parameters[index] || []).push(annotationInstance); return cls; } } ParamDecoratorFactory.prototype.ngMetadataName = name; ParamDecoratorFactory.annotationCls = ParamDecoratorFactory; return ParamDecoratorFactory; }); } function makePropDecorator(name, props, parentClass, additionalProcessing) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function PropDecoratorFactory(...args) { if (this instanceof PropDecoratorFactory) { metaCtor.apply(this, args); return this; } const decoratorInstance = new PropDecoratorFactory(...args); function PropDecorator(target, name) { // target is undefined with standard decorators. This case is not supported and will throw // if this decorator is used in JIT mode with standard decorators. if (target === undefined) { throw new Error('Standard Angular field decorators are not supported in JIT mode.'); } const constructor = target.constructor; // Use of Object.defineProperty is important because it creates a non-enumerable property // which prevents the property from being copied during subclassing. const meta = constructor.hasOwnProperty(PROP_METADATA) ? constructor[PROP_METADATA] : Object.defineProperty(constructor, PROP_METADATA, { value: {} })[PROP_METADATA]; meta[name] = (meta.hasOwnProperty(name) && meta[name]) || []; meta[name].unshift(decoratorInstance); } return PropDecorator; } if (parentClass) { PropDecoratorFactory.prototype = Object.create(parentClass.prototype); } PropDecoratorFactory.prototype.ngMetadataName = name; PropDecoratorFactory.annotationCls = PropDecoratorFactory; return PropDecoratorFactory; }); } /** * Inject decorator and metadata. * * @Annotation * @publicApi */ const Inject = attachInjectFlag( // Disable tslint because `DecoratorFlags` is a const enum which gets inlined. makeParamDecorator('Inject', (token) => ({ token })), -1 /* DecoratorFlags.Inject */); /** * Optional decorator and metadata. * * @Annotation * @publicApi */ const Optional = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Optional'), 8 /* InternalInjectFlags.Optional */); /** * Self decorator and metadata. * * @Annotation * @publicApi */ const Self = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Self'), 2 /* InternalInjectFlags.Self */); /** * `SkipSelf` decorator and metadata. * * @Annotation * @publicApi */ const SkipSelf = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('SkipSelf'), 4 /* InternalInjectFlags.SkipSelf */); /** * Host decorator and metadata. * * @Annotation * @publicApi */ const Host = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Host'), 1 /* InternalInjectFlags.Host */); function getCompilerFacade(request) { const globalNg = _global['ng']; if (globalNg && globalNg.ɵcompilerFacade) { return globalNg.ɵcompilerFacade; } if (typeof ngDevMode === 'undefined' || ngDevMode) { // Log the type as an error so that a developer can easily navigate to the type from the // console. console.error(`JIT compilation failed for ${request.kind}`, request.type); let message = `The ${request.kind} '${request.type.name}' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available.\n\n`; if (request.usage === 1 /* JitCompilerUsage.PartialDeclaration */) { message += `The ${request.kind} is part of a library that has been partially compiled.\n`; message += `However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.\n`; message += '\n'; message += `Ideally, the library is processed using the Angular Linker to become fully AOT compiled.\n`; } else { message += `JIT compilation is discouraged for production use-cases! Consider using AOT mode instead.\n`; } message += `Alternatively, the JIT compiler should be loaded by bootstrapping using '@angular/platform-browser-dynamic' or '@angular/platform-server',\n`; message += `or manually provide the compiler with 'import "@angular/compiler";' before bootstrapping.`; throw new Error(message); } else { throw new Error('JIT compiler unavailable'); } } /** * A mapping of the @angular/core API surface used in generated expressions to the actual symbols. * * This should be kept up to date with the public exports of @angular/core. */ const angularCoreDiEnv = { 'ɵɵdefineInjectable': __defineInjectable, 'ɵɵdefineInjector': __defineInjector, 'ɵɵinject': __inject, 'ɵɵinvalidFactoryDep': __invalidFactoryDep, 'resolveForwardRef': resolveForwardRef, }; /** * @description * * Represents a type that a Component or other object is instances of. * * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by * the `MyCustomComponent` constructor function. * * @publicApi */ const Type = Function; function isType(v) { return typeof v === 'function'; } /* * ######################### * Attention: These Regular expressions have to hold even if the code is minified! * ########################## */ /** * Regular expression that detects pass-through constructors for ES5 output. This Regex * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also * it intends to capture the pattern where existing constructors have been downleveled from * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g. * * ```ts * function MyClass() { * var _this = _super.apply(this, arguments) || this; * ``` * * downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2: * ```ts * function MyClass() { * var _this = _super.apply(this, __spread(arguments)) || this; * ``` * * or downleveled to ES5 with `downlevelIteration` for TypeScript >= 4.2: * ```ts * function MyClass() { * var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this; * ``` * * More details can be found in: https://github.com/angular/angular/issues/38453. */ const ES5_DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|(?:[^()]+\(\[\],)?[^()]+\(arguments\).*)\)/; /** Regular expression that detects ES2015 classes which extend from other classes. */ const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/; /** * Regular expression that detects ES2015 classes which extend from other classes and * have an explicit constructor defined. */ const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/; /** * Regular expression that detects ES2015 classes which extend from other classes * and inherit a constructor. */ const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{[^}]*super\(\.\.\.arguments\)/; /** * Determine whether a stringified type is a class which delegates its constructor * to its parent. * * This is not trivial since compiled code can actually contain a constructor function * even if the original source code did not. For instance, when the child class contains * an initialized instance property. */ function isDelegateCtor(typeStr) { return (ES5_DELEGATE_CTOR.test(typeStr) || ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) || (ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr))); } class ReflectionCapabilities { _reflect; constructor(reflect) { this._reflect = reflect || _global['Reflect']; } factory(t) { return (...args) => new t(...args); } /** @internal */ _zipTypesAndAnnotations(paramTypes, paramAnnotations) { let result; if (typeof paramTypes === 'undefined') { result = newArray(paramAnnotations.length); } else { result = newArray(paramTypes.length); } for (let i = 0; i < result.length; i++) { // TS outputs Object for parameters without types, while Traceur omits // the annotations. For now we preserve the Traceur behavior to aid // migration, but this can be revisited. if (typeof paramTypes === 'undefined') { result[i] = []; } else if (paramTypes[i] && paramTypes[i] != Object) { result[i] = [paramTypes[i]]; } else { result[i] = []; } if (paramAnnotations && paramAnnotations[i] != null) { result[i] = result[i].concat(paramAnnotations[i]); } } return result; } _ownParameters(type, parentCtor) { const typeStr = type.toString(); // If we have no decorators, we only have function.length as metadata. // In that case, to detect whether a child class declared an own constructor or not, // we need to look inside of that constructor to check whether it is // just calling the parent. // This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439 // that sets 'design:paramtypes' to [] // if a class inherits from another class but has no ctor declared itself. if (isDelegateCtor(typeStr)) { return null; } // Prefer the direct API. if (type.parameters && type.parameters !== parentCtor.parameters) { return type.parameters; } // API of tsickle for lowering decorators to properties on the class. const tsickleCtorParams = type.ctorParameters; if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) { // Newer tsickle uses a function closure // Retain the non-function case for compatibility with older tsickle const ctorParameters = typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams; const paramTypes = ctorParameters.map((ctorParam) => ctorParam && ctorParam.type); const paramAnnotations = ctorParameters.map((ctorParam) => ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators)); return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); } // API for metadata created by invoking the decorators. const paramAnnotations = type.hasOwnProperty(PARAMETERS) && type[PARAMETERS]; const paramTypes = this._reflect && this._reflect.getOwnMetadata && this._reflect.getOwnMetadata('design:paramtypes', type); if (paramTypes || paramAnnotations) { return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); } // If a class has no decorators, at least create metadata // based on function.length. // Note: We know that this is a real constructor as we checked // the content of the constructor above. return newArray(type.length); } parameters(type) { // Note: only report metadata if we have at least one class decorator // to stay in sync with the static reflector. if (!isType(type)) { return []; } const parentCtor = getParentCtor(type); let parameters = this._ownParameters(type, parentCtor); if (!parameters && parentCtor !== Object) { parameters = this.parameters(parentCtor); } return parameters || []; } _ownAnnotations(typeOrFunc, parentCtor) { // Prefer the direct API. if (typeOrFunc.annotations && typeOrFunc.annotations !== parentCtor.annotations) { let annotations = typeOrFunc.annotations; if (typeof annotations === 'function' && annotations.annotations) { annotations = annotations.annotations; } return annotations; } // API of tsickle for lowering decorators to properties on the class. if (typeOrFunc.decorators && typeOrFunc.decorators !== parentCtor.decorators) { return convertTsickleDecoratorIntoMetadata(typeOrFunc.decorators); } // API for metadata created by invoking the decorators. if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) { return typeOrFunc[ANNOTATIONS]; } return null; } annotations(typeOrFunc) { if (!isType(typeOrFunc)) { return []; } const parentCtor = getParentCtor(typeOrFunc); const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || []; const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : []; return parentAnnotations.concat(ownAnnotations); } _ownPropMetadata(typeOrFunc, parentCtor) { // Prefer the direct API. if (typeOrFunc.propMetadata && typeOrFunc.propMetadata !== parentCtor.propMetadata) { let propMetadata = typeOrFunc.propMetadata; if (typeof propMetadata === 'function' && propMetadata.propMetadata) { propMetadata = propMetadata.propMetadata; } return propMetadata; } // API of tsickle for lowering decorators to properties on the class. if (typeOrFunc.propDecorators && typeOrFunc.propDecorators !== parentCtor.propDecorators) { const propDecorators = typeOrFunc.propDecorators; const propMetadata = {}; Object.keys(propDecorators).forEach((prop) => { propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]); }); return propMetadata; } // API for metadata created by invoking the decorators. if (typeOrFunc.hasOwnProperty(PROP_METADATA)) { return typeOrFunc[PROP_METADATA]; } return null; } propMetadata(typeOrFunc) { if (!isType(typeOrFunc)) { return {}; } const parentCtor = getParentCtor(typeOrFunc); const propMetadata = {}; if (parentCtor !== Object) { const parentPropMetadata = this.propMetadata(parentCtor); Object.keys(parentPropMetadata).forEach((propName) => { propMetadata[propName] = parentPropMetadata[propName]; }); } const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor); if (ownPropMetadata) { Object.keys(ownPropMetadata).forEach((propName) => { const decorators = []; if (propMetadata.hasOwnProperty(propName)) { decorators.push(...propMetadata[propName]); } decorators.push(...ownPropMetadata[propName]); propMetadata[propName] = decorators; }); } return propMetadata; } ownPropMetadata(typeOrFunc) { if (!isType(typeOrFunc)) { return {}; } return this._ownPropMetadata(typeOrFunc, getParentCtor(typeOrFunc)) || {}; } hasLifecycleHook(type, lcProperty) { return type instanceof Type && lcProperty in type.prototype; } } function convertTsickleDecoratorIntoMetadata(decoratorInvocations) { if (!decoratorInvocations) { return []; } return decoratorInvocations.map((decoratorInvocation) => { const decoratorType = decoratorInvocation.type; const annotationCls = decoratorType.annotationCls; const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : []; return new annotationCls(...annotationArgs); }); } function getParentCtor(ctor) { const parentProto = ctor.prototype ? Object.getPrototypeOf(ctor.prototype) : null; const parentCtor = parentProto ? parentProto.constructor : null; // Note: We always use `Object` as the null value // to simplify checking later on. return parentCtor || Object; } /** * Represents a basic change from a previous to a new value for a single * property on a directive instance. Passed as a value in a * {@link SimpleChanges} object to the `ngOnChanges` hook. * * @see {@link OnChanges} * * @publicApi */ class SimpleChange { previousValue; currentValue; firstChange; constructor(previousValue, currentValue, firstChange) { this.previousValue = previousValue; this.currentValue = currentValue; this.firstChange = firstChange; } /** * Check whether the new value is the first value assigned. */ isFirstChange() { return this.firstChange; } } function applyValueToInputField(instance, inputSignalNode, privateName, value) { if (inputSignalNode !== null) { inputSignalNode.applyValueToInputSignal(inputSignalNode, value); } else { instance[privateName] = value; } } /** * The NgOnChangesFeature decorates a component with support for the ngOnChanges * lifecycle hook, so it should be included in any component that implements * that hook. * * If the component or directive uses inheritance, the NgOnChangesFeature MUST * be included as a feature AFTER {@link InheritDefinitionFeature}, otherwise * inherited properties will not be propagated to the ngOnChanges lifecycle * hook. * * Example usage: * * ```ts * static ɵcmp = defineComponent({ * ... * inputs: {name: 'publicName'}, * features: [NgOnChangesFeature] * }); * ``` * * @codeGenApi */ const ɵɵNgOnChangesFeature = /* @__PURE__ */ (() => { const ɵɵNgOnChangesFeatureImpl = () => NgOnChangesFeatureImpl; // This option ensures that the ngOnChanges lifecycle hook will be inherited // from superclasses (in InheritDefinitionFeature). /** @nocollapse */ ɵɵNgOnChangesFeatureImpl.ngInherit = true; return ɵɵNgOnChangesFeatureImpl; })(); function NgOnChangesFeatureImpl(definition) { if (definition.type.prototype.ngOnChanges) { definition.setInput = ngOnChangesSetInput; } return rememberChangeHistoryAndInvokeOnChangesHook; } /** * This is a synthetic lifecycle hook which gets inserted into `TView.preOrderHooks` to simulate * `ngOnChanges`. * * The hook reads the `NgSimpleChangesStore` data from the component instance and if changes are * found it invokes `ngOnChanges` on the component instance. * * @param this Component instance. Because this function gets inserted into `TView.preOrderHooks`, * it is guaranteed to be called with component instance. */ function rememberChangeHistoryAndInvokeOnChangesHook() { const simpleChangesStore = getSimpleChangesStore(this); const current = simpleChangesStore?.current; if (current) { const previous = simpleChangesStore.previous; if (previous === EMPTY_OBJ) { simpleChangesStore.previous = current; } else { // New changes are copied to the previous store, so that we don't lose history for inputs // which were not changed this time for (let key in current) { previous[key] = current[key]; } } simpleChangesStore.current = null; this.ngOnChanges(current); } } function ngOnChangesSetInput(instance, inputSignalNode, value, publicName, privateName) { const declaredName = this.declaredInputs[publicName]; ngDevMode && assertString(declaredName, 'Name of input in ngOnChanges has to be a string'); const simpleChangesStore = getSimpleChangesStore(instance) || setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null }); const current = simpleChangesStore.current || (simpleChangesStore.current = {}); const previous = simpleChangesStore.previous; const previousChange = previous[declaredName]; current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ); applyValueToInputField(instance, inputSignalNode, privateName, value); } const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__'; function getSimpleChangesStore(instance) { return instance[SIMPLE_CHANGES_STORE] || null; } function setSimpleChangesStore(instance, store) { return (instance[SIMPLE_CHANGES_STORE] = store); } const profilerCallbacks = []; const NOOP_PROFILER_REMOVAL = () => { }; function removeProfiler(profiler) { const profilerIdx = profilerCallbacks.indexOf(profiler); if (profilerIdx !== -1) { profilerCallbacks.splice(profilerIdx, 1); } } /** * Adds a callback function which will be invoked before and after performing certain actions at * runtime (for example, before and after running change detection). Multiple profiler callbacks can be set: * in this case profiling events are reported to every registered callback. * * Warning: this function is *INTERNAL* and should not be relied upon in application's code. * The contract of the function might be changed in any release and/or the function can be removed * completely. * * @param profiler function provided by the caller or null value to disable all profilers. * @returns a cleanup function that, when invoked, removes a given profiler callback. */ function setProfiler(profiler) { if (profiler !== null) { if (!profilerCallbacks.includes(profiler)) { profilerCallbacks.push(profiler); } return () => removeProfiler(profiler); } else { profilerCallbacks.length = 0; return NOOP_PROFILER_REMOVAL; } } /** * Profiler function which wraps user code executed by the runtime. * * @param event ProfilerEvent corresponding to the execution context * @param instance component instance * @param eventFn function associated with event. * For example a template function, lifecycle hook, or output listener. * The value depends on the execution context */ const profiler = function (event, instance = null, eventFn) { for (let i = 0; i < profilerCallbacks.length; i++) { const profilerCallback = profilerCallbacks[i]; profilerCallback(event, instance, eventFn); } }; /** * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`. * * Must be run *only* on the first template pass. * * Sets up the pre-order hooks on the provided `tView`, * see {@link HookData} for details about the data structure. * * @param directiveIndex The index of the directive in LView * @param directiveDef The definition containing the hooks to setup in tView * @param tView The current TView */ function registerPreOrderHooks(directiveIndex, directiveDef, tView) { ngDevMode && assertFirstCreatePass(tView); const { ngOnChanges, ngOnInit, ngDoCheck } = directiveDef.type.prototype; if (ngOnChanges) { const wrappedOnChanges = NgOnChangesFeatureImpl(directiveDef); (tView.preOrderHooks ??= []).push(directiveIndex, wrappedOnChanges); (tView.preOrderCheckHooks ??= []).push(directiveIndex, wrappedOnChanges); } if (ngOnInit) { (tView.preOrderHooks ??= []).push(0 - directiveIndex, ngOnInit); } if (ngDoCheck) { (tView.preOrderHooks ??= []).push(directiveIndex, ngDoCheck); (tView.preOrderCheckHooks ??= []).push(directiveIndex, ngDoCheck); } } /** * * Loops through the directives on the provided `tNode` and queues hooks to be * run that are not initialization hooks. * * Should be executed during `elementEnd()` and similar to * preserve hook execution order. Content, view, and destroy hooks for projected * components and directives must be called *before* their hosts. * * Sets up the content, view, and destroy hooks on the provided `tView`, * see {@link HookData} for details about the data structure. * * NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up * separately at `elementStart`. * * @param tView The current TView * @param tNode The TNode whose directives are to be searched for hooks to queue */ function registerPostOrderHooks(tView, tNode) { ngDevMode && assertFirstCreatePass(tView); // It's necessary to loop through the directives at elementEnd() (rather than processing in // directiveCreate) so we can preserve the current hook order. Content, view, and destroy // hooks for projected components and directives must be called *before* their hosts. for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) { const directiveDef = tView.data[i]; ngDevMode && assertDefined(directiveDef, 'Expecting DirectiveDef'); const lifecycleHooks = directiveDef.type.prototype; const { ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy, } = lifecycleHooks; if (ngAfterContentInit) { (tView.contentHooks ??= []).push(-i, ngAfterContentInit); } if (ngAfterContentChecked) { (tView.contentHooks ??= []).push(i, ngAfterContentChecked); (tView.contentCheckHooks ??= []).push(i, ngAfterContentChecked); } if (ngAfterViewInit) { (tView.viewHooks ??= []).push(-i, ngAfterViewInit); } if (ngAfterViewChecked) { (tView.viewHooks ??= []).push(i, ngAfterViewChecked); (tView.viewCheckHooks ??= []).push(i, ngAfterViewChecked); } if (ngOnDestroy != null) { (tView.destroyHooks ??= []).push(i, ngOnDestroy); } } } /** * Executing hooks requires complex logic as we need to deal with 2 constraints. * * 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only * once, across many change detection cycles. This must be true even if some hooks throw, or if * some recursively trigger a change detection cycle. * To solve that, it is required to track the state of the execution of these init hooks. * This is done by storing and maintaining flags in the view: the {@link InitPhaseState}, * and the index within that phase. They can be seen as a cursor in the following structure: * [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]] * They are stored as flags in LView[FLAGS]. * * 2. Pre-order hooks can be executed in batches, because of the select instruction. * To be able to pause and resume their execution, we also need some state about the hook's array * that is being processed: * - the index of the next hook to be executed * - the number of init hooks already found in the processed part of the array * They are stored as flags in LView[PREORDER_HOOK_FLAGS]. */ /** * Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were * executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read * / write of the init-hooks related flags. * @param lView The LView where hooks are defined * @param hooks Hooks to be run * @param nodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function executeCheckHooks(lView, hooks, nodeIndex) { callHooks(lView, hooks, 3 /* InitPhaseState.InitPhaseCompleted */, nodeIndex); } /** * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked, * AfterViewInit, AfterViewChecked) given a view where there are pending init hooks to be executed. * @param lView The LView where hooks are defined * @param hooks Hooks to be run * @param initPhase A phase for which hooks should be run * @param nodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function executeInitAndCheckHooks(lView, hooks, initPhase, nodeIndex) { ngDevMode && assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init pre-order hooks should not be called more than once'); if ((lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { callHooks(lView, hooks, initPhase, nodeIndex); } } function incrementInitPhaseFlags(lView, initPhase) { ngDevMode && assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init hooks phase should not be incremented after all init hooks have been run.'); let flags = lView[FLAGS]; if ((flags & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { flags &= 16383 /* LViewFlags.IndexWithinInitPhaseReset */; flags += 1 /* LViewFlags.InitPhaseStateIncrementer */; lView[FLAGS] = flags; } } /** * Calls lifecycle hooks with their contexts, skipping init hooks if it's not * the first LView pass * * @param currentView The current view * @param arr The array in which the hooks are found * @param initPhaseState the current state of the init phase * @param currentNodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function callHooks(currentView, arr, initPhase, currentNodeIndex) { ngDevMode && assertEqual(isInCheckNoChangesMode(), false, 'Hooks should never be run when in check no changes mode.'); const startIndex = currentNodeIndex !== undefined ? currentView[PREORDER_HOOK_FLAGS] & 65535 /* PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask */ : 0; const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1; const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1 let lastNodeIndexFound = 0; for (let i = startIndex; i < max; i++) { const hook = arr[i + 1]; if (typeof hook === 'number') { lastNodeIndexFound = arr[i]; if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) { break; } } else { const isInitHook = arr[i] < 0; if (isInitHook) { currentView[PREORDER_HOOK_FLAGS] += 65536 /* PreOrderHookFlags.NumberOfInitHooksCalledIncrementer */; } if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) { callHook(currentView, initPhase, arr, i); currentView[PREORDER_HOOK_FLAGS] = (currentView[PREORDER_HOOK_FLAGS] & 4294901760 /* PreOrderHookFlags.NumberOfInitHooksCalledMask */) + i + 2; } i++; } } } /** * Executes a single lifecycle hook, making sure that: * - it is called in the non-reactive context; * - profiling data are registered. */ function callHookInternal(directive, hook) { profiler(4 /* ProfilerEvent.LifecycleHookStart */, directive, hook); const prevConsumer = setActiveConsumer(null); try { hook.call(directive); } finally { setActiveConsumer(prevConsumer); profiler(5 /* ProfilerEvent.LifecycleHookEnd */, directive, hook); } } /** * Execute one hook against the current `LView`. * * @param currentView The current view * @param initPhaseState the current state of the init phase * @param arr The array in which the hooks are found * @param i The current index within the hook data array */ function callHook(currentView, initPhase, arr, i) { const isInitHook = arr[i] < 0; const hook = arr[i + 1]; const directiveIndex = isInitHook ? -arr[i] : arr[i]; const directive = currentView[directiveIndex]; if (isInitHook) { const indexWithintInitPhase = currentView[FLAGS] >> 14 /* LViewFlags.IndexWithinInitPhaseShift */; // The init phase state must be always checked here as it may have been recursively updated. if (indexWithintInitPhase < currentView[PREORDER_HOOK_FLAGS] >> 16 /* PreOrderHookFlags.NumberOfInitHooksCalledShift */ && (currentView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { currentView[FLAGS] += 16384 /* LViewFlags.IndexWithinInitPhaseIncrementer */; callHookInternal(directive, hook); } } else { callHookInternal(directive, hook); } } const NO_PARENT_INJECTOR = -1; /** * Each injector is saved in 9 contiguous slots in `LView` and 9 contiguous slots in * `TView.data`. This allows us to store information about the current node's tokens (which * can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be * shared, so they live in `LView`). * * Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter * determines whether a directive is available on the associated node or not. This prevents us * from searching the directives array at this level unless it's probable the directive is in it. * * See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters. * * Because all injectors have been flattened into `LView` and `TViewData`, they cannot typed * using interfaces as they were previously. The start index of each `LInjector` and `TInjector` * will differ based on where it is flattened into the main array, so it's not possible to know * the indices ahead of time and save their types here. The interfaces are still included here * for documentation purposes. * * export interface LInjector extends Array<any> { * * // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE) * [0]: number; * * // Cumulative bloom for directive IDs 32-63 * [1]: number; * * // Cumulative bloom for directive IDs 64-95 * [2]: number; * * // Cumulative bloom for directive IDs 96-127 * [3]: number; * * // Cumulative bloom for directive IDs 128-159 * [4]: number; * * // Cumulative bloom for directive IDs 160 - 191 * [5]: number; * * // Cumulative bloom for directive IDs 192 - 223 * [6]: number; * * // Cumulative bloom for directive IDs 224 - 255 * [7]: number; * * // We need to store a reference to the injector's parent so DI can keep looking up * // the injector tree until it finds the dependency it's looking for. * [PARENT_INJECTOR]: number; * } * * export interface TInjector extends Array<any> { * * // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE) * [0]: number; * * // Shared node bloom for directive IDs 32-63 * [1]: number; * * // Shared node bloom for directive IDs 64-95 * [2]: number; * * // Shared node bloom for directive IDs 96-127 * [3]: number; * * // Shared node bloom for directive IDs 128-159 * [4]: number; * * // Shared node bloom for directive IDs 160 - 191 * [5]: number; * * // Shared node bloom for directive IDs 192 - 223 * [6]: number; * * // Shared node bloom for directive IDs 224 - 255 * [7]: number; * * // Necessary to find directive indices for a particular node. * [TNODE]: TElementNode|TElementContainerNode|TContainerNode; * } */ /** * Factory for creating instances of injectors in the NodeInjector. * * This factory is complicated by the fact that it can resolve `multi` factories as well. * * NOTE: Some of the fields are optional which means that this class has two hidden classes. * - One without `multi` support (most common) * - One with `multi` values, (rare). * * Since VMs can cache up to 4 inline hidden classes this is OK. * * - Single factory: Only `resolving` and `factory` is defined. * - `providers` factory: `componentProviders` is a number and `index = -1`. * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`. */ class NodeInjectorFactory { factory; /** * The inject implementation to be activated when using the factory. */ injectImpl; /** * Marker set to true during factory invocation to see if we get into recursive loop. * Recursive loop causes an error to be displayed. */ resolving = false; /** * Marks that the token can see other Tokens declared in `viewProviders` on the same node. */ canSeeViewProviders; /** * An array of factories to use in case of `multi` provider. */ multi; /** * Number of `multi`-providers which belong to the component. * * This is needed because when multiple components and directives declare the `multi` provider * they have to be concatenated in the correct order. * * Example: * * If we have a component and directive active an a single element as declared here * ```ts * component: * providers: [ {provide: String, useValue: 'component', multi: true} ], * viewProviders: [ {provide: String, useValue: 'componentView', multi: true} ], * * directive: * providers: [ {provide: String, useValue: 'directive', multi: true} ], * ``` * * Then the expected results are: * * ```ts * providers: ['component', 'directive'] * viewProviders: ['component', 'componentView', 'directive'] * ``` * * The way to think about it is that the `viewProviders` have been inserted after the component * but before the directives, which is why we need to know how many `multi`s have been declared by * the component. */ componentProviders; /** * Current index of the Factory in the `data`. Needed for `viewProviders` and `providers` merging. * See `providerFactory`. */ index; /** * Because the same `multi` provider can be declared in `providers` and `viewProviders` it is * possible for `viewProviders` to shadow the `providers`. For this reason we store the * `provideFactory` of the `providers` so that `providers` can be extended with `viewProviders`. * * Example: * * Given: * ```ts * providers: [ {provide: String, useValue: 'all', multi: true} ], * viewProviders: [ {provide: String, useValue: 'viewOnly', multi: true} ], * ``` * * We have to return `['all']` in case of content injection, but `['all', 'viewOnly']` in case * of view injection. We further have to make sure that the shared instances (in our case * `all`) are the exact same instance in both the content as well as the view injection. (We * have to make sure that we don't double instantiate.) For this reason the `viewProviders` * `Factory` has a pointer to the shadowed `providers` factory so that it can instantiate the * `providers` (`['all']`) and then extend it with `viewProviders` (`['all'] + ['viewOnly'] = * ['all', 'viewOnly']`). */ providerFactory; constructor( /** * Factory to invoke in order to create a new instance. */ factory, /** * Set to `true` if the token is declared in `viewProviders` (or if it is component). */ isViewProvider, injectImplementation) { this.factory = factory; ngDevMode && assertDefined(factory, 'Factory not specified'); ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.'); this.canSeeViewProviders = isViewProvider; this.injectImpl = injectImplementa