@angular/core
Version:
Angular - the core framework
1,261 lines (1,235 loc) • 1.47 MB
JavaScript
/**
* @license Angular v17.0.0
* (c) 2010-2022 Google LLC. https://angular.io/
* License: MIT
*/
import { setActiveConsumer as setActiveConsumer$1, consumerDestroy as consumerDestroy$1, SIGNAL as SIGNAL$1, createComputed as createComputed$1, createSignal as createSignal$1, signalSetFn as signalSetFn$1, signalUpdateFn as signalUpdateFn$1, REACTIVE_NODE as REACTIVE_NODE$1, consumerBeforeComputation as consumerBeforeComputation$1, consumerAfterComputation as consumerAfterComputation$1, consumerPollProducersForChange as consumerPollProducersForChange$1, getActiveConsumer as getActiveConsumer$1, createWatch as createWatch$1, setThrowInvalidWriteToSignalError as setThrowInvalidWriteToSignalError$1 } from '@angular/core/primitives/signals';
import { Subject, Subscription, Observable, merge as merge$1, BehaviorSubject, of } from 'rxjs';
import { share, switchMap, distinctUntilChanged, first } from 'rxjs/operators';
function getClosureSafeProperty(objWithPropertyToExtract) {
for (let key in objWithPropertyToExtract) {
if (objWithPropertyToExtract[key] === getClosureSafeProperty) {
return key;
}
}
throw Error('Could not find renamed property on target object.');
}
/**
* Sets properties on a target object from a source object, but only if
* the property doesn't already exist on the target object.
* @param target The target to set properties on
* @param source The source of the property keys and values to set
*/
function fillProperties(target, source) {
for (const key in source) {
if (source.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
function stringify(token) {
if (typeof token === 'string') {
return token;
}
if (Array.isArray(token)) {
return '[' + token.map(stringify).join(', ') + ']';
}
if (token == null) {
return '' + token;
}
if (token.overriddenName) {
return `${token.overriddenName}`;
}
if (token.name) {
return `${token.name}`;
}
const res = token.toString();
if (res == null) {
return '' + res;
}
const newLineIndex = res.indexOf('\n');
return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
}
/**
* Concatenates two strings with separator, allocating new strings only when necessary.
*
* @param before before string.
* @param separator separator string.
* @param after after string.
* @returns concatenated string.
*/
function concatStringsWithSpace(before, after) {
return (before == null || before === '') ?
(after === null ? '' : after) :
((after == null || after === '') ? before : before + ' ' + after);
}
/**
* Ellipses the string in the middle when longer than the max length
*
* @param string
* @param maxLength of the output string
* @returns elispsed string with ... in the middle
*/
function truncateMiddle(str, maxLength = 100) {
if (!str || maxLength < 1 || str.length <= maxLength)
return str;
if (maxLength == 1)
return str.substring(0, 1) + '...';
const halfLimit = Math.round(maxLength / 2);
return str.substring(0, halfLimit) + '...' + str.substring(str.length - halfLimit);
}
const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty });
/**
* Allows to refer to references which are not yet defined.
*
* For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
* DI is declared, but not yet defined. It is also used when the `token` which we use when creating
* a query is not yet defined.
*
* `forwardRef` is also used to break circularities in standalone components imports.
*
* @usageNotes
* ### Circular dependency example
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
*
* ### Circular standalone reference import example
* ```ts
* @Component({
* standalone: true,
* imports: [ChildComponent],
* selector: 'app-parent',
* template: `<app-child [hideParent]="hideParent"></app-child>`,
* })
* export class ParentComponent {
* @Input() hideParent: boolean;
* }
*
*
* @Component({
* standalone: true,
* imports: [CommonModule, forwardRef(() => ParentComponent)],
* selector: 'app-child',
* template: `<app-parent *ngIf="!hideParent"></app-parent>`,
* })
* export class ChildComponent {
* @Input() hideParent: boolean;
* }
* ```
*
* @publicApi
*/
function forwardRef(forwardRefFn) {
forwardRefFn.__forward_ref__ = forwardRef;
forwardRefFn.toString = function () {
return stringify(this());
};
return forwardRefFn;
}
/**
* Lazily retrieves the reference value from a forwardRef.
*
* Acts as the identity function when given a non-forward-ref value.
*
* @usageNotes
* ### Example
*
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'}
*
* @see {@link forwardRef}
* @publicApi
*/
function resolveForwardRef(type) {
return isForwardRef(type) ? type() : type;
}
/** Checks whether a function is wrapped by a `forwardRef`. */
function isForwardRef(fn) {
return typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) &&
fn.__forward_ref__ === forwardRef;
}
function isEnvironmentProviders(value) {
return value && !!value.ɵproviders;
}
/**
* Base URL for the error details page.
*
* Keep this constant in sync across:
* - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts
* - packages/core/src/error_details_base_url.ts
*/
const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
/**
* URL for the XSS security documentation.
*/
const XSS_SECURITY_URL = 'https://g.co/ng/security#xss';
/**
* Class that represents a runtime error.
* Formats and outputs the error message in a consistent way.
*
* Example:
* ```
* throw new RuntimeError(
* RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED,
* ngDevMode && 'Injector has already been destroyed.');
* ```
*
* Note: the `message` argument contains a descriptive error message as a string in development
* mode (when the `ngDevMode` is defined). In production mode (after tree-shaking pass), the
* `message` argument becomes `false`, thus we account for it in the typings and the runtime
* logic.
*/
class RuntimeError extends Error {
constructor(code, message) {
super(formatRuntimeError(code, message));
this.code = code;
}
}
/**
* Called to format a runtime error.
* See additional info on the `message` argument type in the `RuntimeError` class description.
*/
function formatRuntimeError(code, message) {
// Error code might be a negative number, which is a special marker that instructs the logic to
// generate a link to the error details page on angular.io.
// We also prepend `0` to non-compile-time errors.
const fullCode = `NG0${Math.abs(code)}`;
let errorMessage = `${fullCode}${message ? ': ' + message : ''}`;
if (ngDevMode && code < 0) {
const addPeriodSeparator = !errorMessage.match(/[.,;!?\n]$/);
const separator = addPeriodSeparator ? '.' : '';
errorMessage =
`${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`;
}
return errorMessage;
}
const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty });
const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty });
const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty });
const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty });
const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty });
/**
* If a directive is diPublic, bloomAdd sets a property on the type with this constant as
* the key and the directive's unique ID as the value. This allows us to map directives to their
* bloom filter bit for DI.
*/
// TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified.
const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafeProperty });
/**
* The `NG_ENV_ID` field on a DI token indicates special processing in the `EnvironmentInjector`:
* getting such tokens from the `EnvironmentInjector` will bypass the standard DI resolution
* strategy and instead will return implementation produced by the `NG_ENV_ID` factory function.
*
* This particular retrieval of DI tokens is mostly done to eliminate circular dependencies and
* improve tree-shaking.
*/
const NG_ENV_ID = getClosureSafeProperty({ __NG_ENV_ID__: getClosureSafeProperty });
/**
* Used for stringify render output in Ivy.
* Important! This function is very performance-sensitive and we should
* be extra careful not to introduce megamorphic reads in it.
* Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations.
*/
function renderStringify(value) {
if (typeof value === 'string')
return value;
if (value == null)
return '';
// Use `String` so that it invokes the `toString` method of the value. Note that this
// appears to be faster than calling `value.toString` (see `render_stringify` benchmark).
return String(value);
}
/**
* Used to stringify a value so that it can be displayed in an error message.
*
* Important! This function contains a megamorphic read and should only be
* used for error messages.
*/
function stringifyForError(value) {
if (typeof value === 'function')
return value.name || value.toString();
if (typeof value === 'object' && value != null && typeof value.type === 'function') {
return value.type.name || value.type.toString();
}
return renderStringify(value);
}
/**
* Used to stringify a `Type` and including the file path and line number in which it is defined, if
* possible, for better debugging experience.
*
* Important! This function contains a megamorphic read and should only be used for error messages.
*/
function debugStringifyTypeForError(type) {
// TODO(pmvald): Do some refactoring so that we can use getComponentDef here without creating
// circular deps.
let componentDef = type[NG_COMP_DEF] || null;
if (componentDef !== null && componentDef.debugInfo) {
return stringifyTypeFromDebugInfo(componentDef.debugInfo);
}
return stringifyForError(type);
}
// TODO(pmvald): Do some refactoring so that we can use the type ClassDebugInfo for the param
// debugInfo here without creating circular deps.
function stringifyTypeFromDebugInfo(debugInfo) {
if (!debugInfo.filePath || !debugInfo.lineNumber) {
return debugInfo.className;
}
else {
return `${debugInfo.className} (at ${debugInfo.filePath}:${debugInfo.lineNumber})`;
}
}
/** Called when directives inject each other (creating a circular dependency) */
function throwCyclicDependencyError(token, path) {
const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : '';
throw new RuntimeError(-200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, `Circular dependency in DI detected for ${token}${depPath}`);
}
function throwMixedMultiProviderError() {
throw new Error(`Cannot mix multi providers and regular providers`);
}
function throwInvalidProviderError(ngModuleType, providers, provider) {
if (ngModuleType && providers) {
const providerDetail = providers.map(v => v == provider ? '?' + provider + '?' : '...');
throw new Error(`Invalid provider for the NgModule '${stringify(ngModuleType)}' - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`);
}
else if (isEnvironmentProviders(provider)) {
if (provider.ɵfromNgModule) {
throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers from 'importProvidersFrom' present in a non-environment injector. 'importProvidersFrom' can't be used for component providers.`);
}
else {
throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers present in a non-environment injector. 'EnvironmentProviders' can't be used for component providers.`);
}
}
else {
throw new Error('Invalid provider');
}
}
/** Throws an error when a token is not found in DI. */
function throwProviderNotFoundError(token, injectorName) {
const injectorDetails = injectorName ? ` in ${injectorName}` : '';
throw new RuntimeError(-201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */, ngDevMode && `No provider for ${stringifyForError(token)} found${injectorDetails}`);
}
// The functions in this file verify that the assumptions we are making
function assertNumber(actual, msg) {
if (!(typeof actual === 'number')) {
throwError(msg, typeof actual, 'number', '===');
}
}
function assertNumberInRange(actual, minInclusive, maxInclusive) {
assertNumber(actual, 'Expected a number');
assertLessThanOrEqual(actual, maxInclusive, 'Expected number to be less than or equal to');
assertGreaterThanOrEqual(actual, minInclusive, 'Expected number to be greater than or equal to');
}
function assertString(actual, msg) {
if (!(typeof actual === 'string')) {
throwError(msg, actual === null ? 'null' : typeof actual, 'string', '===');
}
}
function assertFunction(actual, msg) {
if (!(typeof actual === 'function')) {
throwError(msg, actual === null ? 'null' : typeof actual, 'function', '===');
}
}
function assertEqual(actual, expected, msg) {
if (!(actual == expected)) {
throwError(msg, actual, expected, '==');
}
}
function assertNotEqual(actual, expected, msg) {
if (!(actual != expected)) {
throwError(msg, actual, expected, '!=');
}
}
function assertSame(actual, expected, msg) {
if (!(actual === expected)) {
throwError(msg, actual, expected, '===');
}
}
function assertNotSame(actual, expected, msg) {
if (!(actual !== expected)) {
throwError(msg, actual, expected, '!==');
}
}
function assertLessThan(actual, expected, msg) {
if (!(actual < expected)) {
throwError(msg, actual, expected, '<');
}
}
function assertLessThanOrEqual(actual, expected, msg) {
if (!(actual <= expected)) {
throwError(msg, actual, expected, '<=');
}
}
function assertGreaterThan(actual, expected, msg) {
if (!(actual > expected)) {
throwError(msg, actual, expected, '>');
}
}
function assertGreaterThanOrEqual(actual, expected, msg) {
if (!(actual >= expected)) {
throwError(msg, actual, expected, '>=');
}
}
function assertNotDefined(actual, msg) {
if (actual != null) {
throwError(msg, actual, null, '==');
}
}
function assertDefined(actual, msg) {
if (actual == null) {
throwError(msg, actual, null, '!=');
}
}
function throwError(msg, actual, expected, comparison) {
throw new Error(`ASSERTION ERROR: ${msg}` +
(comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`));
}
function assertDomNode(node) {
if (!(node instanceof Node)) {
throwError(`The provided value must be an instance of a DOM Node but got ${stringify(node)}`);
}
}
function assertElement(node) {
if (!(node instanceof Element)) {
throwError(`The provided value must be an element but got ${stringify(node)}`);
}
}
function assertIndexInRange(arr, index) {
assertDefined(arr, 'Array must be defined.');
const maxLen = arr.length;
if (index < 0 || index >= maxLen) {
throwError(`Index expected to be less than ${maxLen} but got ${index}`);
}
}
function assertOneOf(value, ...validValues) {
if (validValues.indexOf(value) !== -1)
return true;
throwError(`Expected value to be one of ${JSON.stringify(validValues)} but was ${JSON.stringify(value)}.`);
}
/**
* Construct an injectable definition which defines how a token will be constructed by the DI
* system, and in which injectors (if any) it will be available.
*
* This should be assigned to a static `ɵprov` field on a type, which will then be an
* `InjectableType`.
*
* Options:
* * `providedIn` determines which injectors will include the injectable, by either associating it
* with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
* provided in the `'root'` injector, which will be the application-level injector in most apps.
* * `factory` gives the zero argument function which will create an instance of the injectable.
* The factory can call [`inject`](api/core/inject) to access the `Injector` and request injection
* of dependencies.
*
* @codeGenApi
* @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm.
*/
function ɵɵdefineInjectable(opts) {
return {
token: opts.token,
providedIn: opts.providedIn || null,
factory: opts.factory,
value: undefined,
};
}
/**
* @deprecated in v8, delete after v10. This API should be used only by generated code, and that
* code should now use ɵɵdefineInjectable instead.
* @publicApi
*/
const defineInjectable = ɵɵdefineInjectable;
/**
* Construct an `InjectorDef` which configures an injector.
*
* This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an
* `InjectorType`.
*
* Options:
*
* * `providers`: an optional array of providers to add to the injector. Each provider must
* either have a factory or point to a type which has a `ɵprov` static property (the
* type must be an `InjectableType`).
* * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
* whose providers will also be added to the injector. Locally provided types will override
* providers from imports.
*
* @codeGenApi
*/
function ɵɵdefineInjector(options) {
return { providers: options.providers || [], imports: options.imports || [] };
}
/**
* Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading
* inherited value.
*
* @param type A type which may have its own (non-inherited) `ɵprov`.
*/
function getInjectableDef(type) {
return getOwnDefinition(type, NG_PROV_DEF) || getOwnDefinition(type, NG_INJECTABLE_DEF);
}
function isInjectable(type) {
return getInjectableDef(type) !== null;
}
/**
* Return definition only if it is defined directly on `type` and is not inherited from a base
* class of `type`.
*/
function getOwnDefinition(type, field) {
return type.hasOwnProperty(field) ? type[field] : null;
}
/**
* Read the injectable def (`ɵprov`) for `type` or read the `ɵprov` from one of its ancestors.
*
* @param type A type which may have `ɵprov`, via inheritance.
*
* @deprecated Will be removed in a future version of Angular, where an error will occur in the
* scenario if we find the `ɵprov` on an ancestor only.
*/
function getInheritedInjectableDef(type) {
const def = type && (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF]);
if (def) {
ngDevMode &&
console.warn(`DEPRECATED: DI is instantiating a token "${type.name}" that inherits its @Injectable decorator but does not provide one itself.\n` +
`This will become an error in a future version of Angular. Please add @Injectable() to the "${type.name}" class.`);
return def;
}
else {
return null;
}
}
/**
* Read the injector def type in a way which is immune to accidentally reading inherited value.
*
* @param type type which may have an injector def (`ɵinj`)
*/
function getInjectorDef(type) {
return type && (type.hasOwnProperty(NG_INJ_DEF) || type.hasOwnProperty(NG_INJECTOR_DEF)) ?
type[NG_INJ_DEF] :
null;
}
const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty });
const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty });
// We need to keep these around so we can read off old defs if new defs are unavailable
const NG_INJECTABLE_DEF = getClosureSafeProperty({ ngInjectableDef: getClosureSafeProperty });
const NG_INJECTOR_DEF = getClosureSafeProperty({ ngInjectorDef: getClosureSafeProperty });
/**
* Injection flags for DI.
*
* @publicApi
* @deprecated use an options object for [`inject`](api/core/inject) instead.
*/
var InjectFlags;
(function (InjectFlags) {
// TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer
// writes exports of it into ngfactory files.
/** Check self and check parent injector if needed */
InjectFlags[InjectFlags["Default"] = 0] = "Default";
/**
* Specifies that an injector should retrieve a dependency from any injector until reaching the
* host element of the current component. (Only used with Element Injector)
*/
InjectFlags[InjectFlags["Host"] = 1] = "Host";
/** Don't ascend to ancestors of the node requesting injection. */
InjectFlags[InjectFlags["Self"] = 2] = "Self";
/** Skip the node that is requesting injection. */
InjectFlags[InjectFlags["SkipSelf"] = 4] = "SkipSelf";
/** Inject `defaultValue` instead if token not found. */
InjectFlags[InjectFlags["Optional"] = 8] = "Optional";
})(InjectFlags || (InjectFlags = {}));
/**
* Current implementation of inject.
*
* By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
* to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
* way for two reasons:
* 1. `Injector` should not depend on ivy logic.
* 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
*/
let _injectImplementation;
function getInjectImplementation() {
return _injectImplementation;
}
/**
* Sets the current inject implementation.
*/
function setInjectImplementation(impl) {
const previous = _injectImplementation;
_injectImplementation = impl;
return previous;
}
/**
* Injects `root` tokens in limp mode.
*
* If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
* `"root"`. This is known as the limp mode injection. In such case the value is stored in the
* injectable definition.
*/
function injectRootLimpMode(token, notFoundValue, flags) {
const injectableDef = getInjectableDef(token);
if (injectableDef && injectableDef.providedIn == 'root') {
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
injectableDef.value;
}
if (flags & InjectFlags.Optional)
return null;
if (notFoundValue !== undefined)
return notFoundValue;
throwProviderNotFoundError(stringify(token), 'Injector');
}
/**
* Assert that `_injectImplementation` is not `fn`.
*
* This is useful, to prevent infinite recursion.
*
* @param fn Function which it should not equal to
*/
function assertInjectImplementationNotEqual(fn) {
ngDevMode &&
assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion');
}
const _global = globalThis;
function ngDevModeResetPerfCounters() {
const locationString = typeof location !== 'undefined' ? location.toString() : '';
const newCounters = {
namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1,
firstCreatePass: 0,
tNode: 0,
tView: 0,
rendererCreateTextNode: 0,
rendererSetText: 0,
rendererCreateElement: 0,
rendererAddEventListener: 0,
rendererSetAttribute: 0,
rendererRemoveAttribute: 0,
rendererSetProperty: 0,
rendererSetClassName: 0,
rendererAddClass: 0,
rendererRemoveClass: 0,
rendererSetStyle: 0,
rendererRemoveStyle: 0,
rendererDestroy: 0,
rendererDestroyNode: 0,
rendererMoveNode: 0,
rendererRemoveNode: 0,
rendererAppendChild: 0,
rendererInsertBefore: 0,
rendererCreateComment: 0,
hydratedNodes: 0,
hydratedComponents: 0,
dehydratedViewsRemoved: 0,
dehydratedViewsCleanupRuns: 0,
componentsSkippedHydration: 0,
};
// Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
_global['ngDevMode'] = allowNgDevModeTrue && newCounters;
return newCounters;
}
/**
* This function checks to see if the `ngDevMode` has been set. If yes,
* then we honor it, otherwise we default to dev mode with additional checks.
*
* The idea is that unless we are doing production build where we explicitly
* set `ngDevMode == false` we should be helping the developer by providing
* as much early warning and errors as possible.
*
* `ɵɵdefineComponent` is guaranteed to have been called before any component template functions
* (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode
* is defined for the entire instruction set.
*
* When checking `ngDevMode` on toplevel, always init it before referencing it
* (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can
* get a `ReferenceError` like in https://github.com/angular/angular/issues/31595.
*
* Details on possible values for `ngDevMode` can be found on its docstring.
*
* NOTE:
* - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`.
*/
function initNgDevMode() {
// The below checks are to ensure that calling `initNgDevMode` multiple times does not
// reset the counters.
// If the `ngDevMode` is not an object, then it means we have not created the perf counters
// yet.
if (typeof ngDevMode === 'undefined' || ngDevMode) {
if (typeof ngDevMode !== 'object') {
ngDevModeResetPerfCounters();
}
return typeof ngDevMode !== 'undefined' && !!ngDevMode;
}
return false;
}
/**
* Creates a token that can be used in a DI Provider.
*
* Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
* runtime representation) such as when injecting an interface, callable type, array or
* parameterized type.
*
* `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
* the `Injector`. This provides an additional level of type safety.
*
* <div class="alert is-helpful">
*
* **Important Note**: Ensure that you use the same instance of the `InjectionToken` in both the
* provider and the injection call. Creating a new instance of `InjectionToken` in different places,
* even with the same description, will be treated as different tokens by Angular's DI system,
* leading to a `NullInjectorError`.
*
* </div>
*
* <code-example format="typescript" language="typescript" path="injection-token/src/main.ts"
* region="InjectionToken"></code-example>
*
* When creating an `InjectionToken`, you can optionally specify a factory function which returns
* (possibly by creating) a default value of the parameterized type `T`. This sets up the
* `InjectionToken` using this factory as a provider as if it was defined explicitly in the
* application's root injector. If the factory function, which takes zero arguments, needs to inject
* dependencies, it can do so using the [`inject`](api/core/inject) function.
* As you can see in the Tree-shakable InjectionToken example below.
*
* Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
* overrides the above behavior and marks the token as belonging to a particular `@NgModule` (note:
* this option is now deprecated). As mentioned above, `'root'` is the default value for
* `providedIn`.
*
* The `providedIn: NgModule` and `providedIn: 'any'` options are deprecated.
*
* @usageNotes
* ### Basic Examples
*
* ### Plain InjectionToken
*
* {@example core/di/ts/injector_spec.ts region='InjectionToken'}
*
* ### Tree-shakable InjectionToken
*
* {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'}
*
* @publicApi
*/
class InjectionToken {
/**
* @param _desc Description for the token,
* used only for debugging purposes,
* it should but does not need to be unique
* @param options Options for the token's usage, as described above
*/
constructor(_desc, options) {
this._desc = _desc;
/** @internal */
this.ngMetadataName = 'InjectionToken';
this.ɵprov = undefined;
if (typeof options == 'number') {
(typeof ngDevMode === 'undefined' || ngDevMode) &&
assertLessThan(options, 0, 'Only negative numbers are supported here');
// This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
// See `InjectorMarkers`
this.__NG_ELEMENT_ID__ = options;
}
else if (options !== undefined) {
this.ɵprov = ɵɵdefineInjectable({
token: this,
providedIn: options.providedIn || 'root',
factory: options.factory,
});
}
}
/**
* @internal
*/
get multi() {
return this;
}
toString() {
return `InjectionToken ${this._desc}`;
}
}
let _injectorProfilerContext;
function getInjectorProfilerContext() {
!ngDevMode && throwError('getInjectorProfilerContext should never be called in production mode');
return _injectorProfilerContext;
}
function setInjectorProfilerContext(context) {
!ngDevMode && throwError('setInjectorProfilerContext should never be called in production mode');
const previous = _injectorProfilerContext;
_injectorProfilerContext = context;
return previous;
}
let injectorProfilerCallback = null;
/**
* Sets the callback function which will be invoked during certain DI events within the
* runtime (for example: injecting services, creating injectable instances, configuring providers)
*
* 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 profiling.
*/
const setInjectorProfiler = (injectorProfiler) => {
!ngDevMode && throwError('setInjectorProfiler should never be called in production mode');
injectorProfilerCallback = injectorProfiler;
};
/**
* Injector profiler function which emits on DI events executed by the runtime.
*
* @param event InjectorProfilerEvent corresponding to the DI event being emitted
*/
function injectorProfiler(event) {
!ngDevMode && throwError('Injector profiler should never be called in production mode');
if (injectorProfilerCallback != null /* both `null` and `undefined` */) {
injectorProfilerCallback(event);
}
}
/**
* Emits an InjectorProfilerEventType.ProviderConfigured to the injector profiler. The data in the
* emitted event includes the raw provider, as well as the token that provider is providing.
*
* @param eventProvider A provider object
*/
function emitProviderConfiguredEvent(eventProvider, isViewProvider = false) {
!ngDevMode && throwError('Injector profiler should never be called in production mode');
let token;
// if the provider is a TypeProvider (typeof provider is function) then the token is the
// provider itself
if (typeof eventProvider === 'function') {
token = eventProvider;
}
// if the provider is an injection token, then the token is the injection token.
else if (eventProvider instanceof InjectionToken) {
token = eventProvider;
}
// in all other cases we can access the token via the `provide` property of the provider
else {
token = resolveForwardRef(eventProvider.provide);
}
let provider = eventProvider;
// Injection tokens may define their own default provider which gets attached to the token itself
// as `ɵprov`. In this case, we want to emit the provider that is attached to the token, not the
// token itself.
if (eventProvider instanceof InjectionToken) {
provider = eventProvider.ɵprov || eventProvider;
}
injectorProfiler({
type: 2 /* InjectorProfilerEventType.ProviderConfigured */,
context: getInjectorProfilerContext(),
providerRecord: { token, provider, isViewProvider }
});
}
/**
* Emits an event to the injector profiler with the instance that was created. Note that
* the injector associated with this emission can be accessed by using getDebugInjectContext()
*
* @param instance an object created by an injector
*/
function emitInstanceCreatedByInjectorEvent(instance) {
!ngDevMode && throwError('Injector profiler should never be called in production mode');
injectorProfiler({
type: 1 /* InjectorProfilerEventType.InstanceCreatedByInjector */,
context: getInjectorProfilerContext(),
instance: { value: instance }
});
}
/**
* @param token DI token associated with injected service
* @param value the instance of the injected service (i.e the result of `inject(token)`)
* @param flags the flags that the token was injected with
*/
function emitInjectEvent(token, value, flags) {
!ngDevMode && throwError('Injector profiler should never be called in production mode');
injectorProfiler({
type: 0 /* InjectorProfilerEventType.Inject */,
context: getInjectorProfilerContext(),
service: { token, value, flags }
});
}
function runInInjectorProfilerContext(injector, token, callback) {
!ngDevMode &&
throwError('runInInjectorProfilerContext should never be called in production mode');
const prevInjectContext = setInjectorProfilerContext({ injector, token });
try {
callback();
}
finally {
setInjectorProfilerContext(prevInjectContext);
}
}
const _THROW_IF_NOT_FOUND = {};
const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
/*
* Name of a property (that we patch onto DI decorator), which is used as an annotation of which
* InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators
* in the code, thus making them tree-shakable.
*/
const DI_DECORATOR_FLAG = '__NG_DI_FLAG__';
const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
const NG_TOKEN_PATH = 'ngTokenPath';
const NEW_LINE = /\n/gm;
const NO_NEW_LINE = 'ɵ';
const SOURCE = '__source';
/**
* Current injector value used by `inject`.
* - `undefined`: it is an error to call `inject`
* - `null`: `inject` can be called but there is no injector (limp-mode).
* - Injector instance: Use the injector for resolution.
*/
let _currentInjector = undefined;
function getCurrentInjector() {
return _currentInjector;
}
function setCurrentInjector(injector) {
const former = _currentInjector;
_currentInjector = injector;
return former;
}
function injectInjectorOnly(token, flags = InjectFlags.Default) {
if (_currentInjector === undefined) {
throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
`inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`runInInjectionContext\`.`);
}
else if (_currentInjector === null) {
return injectRootLimpMode(token, undefined, flags);
}
else {
const value = _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
ngDevMode && emitInjectEvent(token, value, flags);
return value;
}
}
function ɵɵinject(token, flags = InjectFlags.Default) {
return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags);
}
/**
* Throws an error indicating that a factory function could not be generated by the compiler for a
* particular class.
*
* The name of the class is not mentioned here, but will be in the generated factory function name
* and thus in the stack trace.
*
* @codeGenApi
*/
function ɵɵinvalidFactoryDep(index) {
throw new RuntimeError(202 /* RuntimeErrorCode.INVALID_FACTORY_DEPENDENCY */, ngDevMode &&
`This constructor is not compatible with Angular Dependency Injection because its dependency at index ${index} of the parameter list is invalid.
This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator.
Please check that 1) the type for the parameter at index ${index} is correct and 2) the correct Angular decorators are defined for this class and its ancestors.`);
}
/**
* Injects a token from the currently active injector.
* `inject` is only supported in an [injection context](/guide/dependency-injection-context). It can
* be used during:
* - Construction (via the `constructor`) of a class being instantiated by the DI system, such
* as an `@Injectable` or `@Component`.
* - In the initializer for fields of such classes.
* - In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
* - In the `factory` function specified for an `InjectionToken`.
* - In a stackframe of a function call in a DI context
*
* @param token A token that represents a dependency that should be injected.
* @param flags Optional flags that control how injection is executed.
* The flags correspond to injection strategies that can be specified with
* parameter decorators `@Host`, `@Self`, `@SkipSelf`, and `@Optional`.
* @returns the injected value if operation is successful, `null` otherwise.
* @throws if called outside of a supported context.
*
* @usageNotes
* In practice the `inject()` calls are allowed in a constructor, a constructor parameter and a
* field initializer:
*
* ```typescript
* @Injectable({providedIn: 'root'})
* export class Car {
* radio: Radio|undefined;
* // OK: field initializer
* spareTyre = inject(Tyre);
*
* constructor() {
* // OK: constructor body
* this.radio = inject(Radio);
* }
* }
* ```
*
* It is also legal to call `inject` from a provider's factory:
*
* ```typescript
* providers: [
* {provide: Car, useFactory: () => {
* // OK: a class factory
* const engine = inject(Engine);
* return new Car(engine);
* }}
* ]
* ```
*
* Calls to the `inject()` function outside of the class creation context will result in error. Most
* notably, calls to `inject()` are disallowed after a class instance was created, in methods
* (including lifecycle hooks):
*
* ```typescript
* @Component({ ... })
* export class CarComponent {
* ngOnInit() {
* // ERROR: too late, the component instance was already created
* const engine = inject(Engine);
* engine.start();
* }
* }
* ```
*
* @publicApi
*/
function inject(token, flags = InjectFlags.Default) {
return ɵɵinject(token, convertToBitFlags(flags));
}
// Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).
function convertToBitFlags(flags) {
if (typeof flags === 'undefined' || typeof flags === 'number') {
return flags;
}
// While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
// JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
// `InjectOptions` to `InjectFlags`.
return (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
(flags.optional && 8 /* InternalInjectFlags.Optional */) |
(flags.host && 1 /* InternalInjectFlags.Host */) |
(flags.self && 2 /* InternalInjectFlags.Self */) |
(flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
}
function injectArgs(types) {
const args = [];
for (let i = 0; i < types.length; i++) {
const arg = resolveForwardRef(types[i]);
if (Array.isArray(arg)) {
if (arg.length === 0) {
throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && 'Arguments array must have arguments.');
}
let type = undefined;
let flags = InjectFlags.Default;
for (let j = 0; j < arg.length; j++) {
const meta = arg[j];
const flag = getInjectFlag(meta);
if (typeof flag === 'number') {
// Special case when we handle @Inject decorator.
if (flag === -1 /* DecoratorFlags.Inject */) {
type = meta.token;
}
else {
flags |= flag;
}
}
else {
type = meta;
}
}
args.push(ɵɵinject(type, flags));
}
else {
args.push(ɵɵinject(arg));
}
}
return args;
}
/**
* Attaches a given InjectFlag to a given decorator using monkey-patching.
* Since DI decorators can be used in providers `deps` array (when provider is configured using
* `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we
* attach the flag to make it available both as a static property and as a field on decorator
* instance.
*
* @param decorator Provided DI decorator.
* @param flag InjectFlag that should be applied.
*/
function attachInjectFlag(decorator, flag) {
decorator[DI_DECORATOR_FLAG] = flag;
decorator.prototype[DI_DECORATOR_FLAG] = flag;
return decorator;
}
/**
* Reads monkey-patched property that contains InjectFlag attached to a decorator.
*
* @param token Token that may contain monkey-patched DI flags property.
*/
function getInjectFlag(token) {
return token[DI_DECORATOR_FLAG];
}
function catchInjectorError(e, token, injectorErrorName, source) {
const tokenPath = e[NG_TEMP_TOKEN_PATH];
if (token[SOURCE]) {
tokenPath.unshift(token[SOURCE]);
}
e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source);
e[NG_TOKEN_PATH] = tokenPath;
e[NG_TEMP_TOKEN_PATH] = null;
throw e;
}
function formatError(text, obj, injectorErrorName, source = null) {
text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.slice(2) : text;
let context = stringify(obj);
if (Array.isArray(obj)) {
context = obj.map(stringify).join(' -> ');
}
else if (typeof obj === 'object') {
let parts = [];
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];
parts.push(key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value)));
}
}
context = `{${parts.join(', ')}}`;
}
return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
}
/**
* 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();
}
/**
* The strategy that the default change detector uses to detect changes.
* When set, takes effect the next time change detection is triggered.
*
* @see {@link ChangeDetectorRef#usage-notes Change detection usage}
*
* @publicApi
*/
var ChangeDetectionStrategy;
(function (ChangeDetectionStrategy) {
/**
* Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
* until reactivated by setting the strategy to `Default` (`CheckAlways`).
* Change detection can still be explicitly invoked.
* This strategy applies to all child directives and cannot be overridden.
*/
ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
/**
* Use the default `CheckAlways` strategy, in which change detection is automatic until
* explicitly deactivated.
*/
ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
/**
* Defines the CSS styles encapsulation policies for the {@link Component} decorator's
* `encapsulation` option.
*
* See {@link Component#encapsulation encapsulation}.
*
* @usageNotes
* ### Example
*
* {@example core/ts/metadata/encapsulation.ts region='longform'}
*
* @publicApi
*/
var ViewEncapsulation$1;
(function (ViewEncapsulation) {
// TODO: consider making `ViewEncapsulation` a `const enum` instead. See
// https://github.com/angular/angular/issues/44119 for additional information.
/**
* Emulates a native Shadow DOM encapsulation behavior by adding a specific attribute to the
* component's host element and applying the same attribute to all the CSS selectors provided
* via {@link Component#styles styles} or {@link Component#styleUrls styleUrls}.
*
* This is the default option.
*/
ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
// Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
/**
* Doesn't provide any sort of CSS style encapsulation, meaning that all the styles provided
* via {@link Component#styles styles} or {@link Component#styleUrls styleUrls} are applicable
* to any HTML element of the application regardless of their host Component.
*/
ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
/**
* Uses the browser's native Shadow DOM API to encapsulate CSS styles, meaning that it creates
* a ShadowRoot for the component's host element which is then used to encapsulate
* all the Component's styling.
*/
ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
})(ViewEncapsulation$1 || (ViewEncapsulation$1 = {}));
/**
* This file contains reuseable "empty" symbols that can be used as default return values
* in different parts of the rendering code. Because the same symbols are returned, this
* allows for identity checks against these values to be consistently used by the framework
* code.
*/
const EMPTY_OBJ = {};
const EMPTY_ARRAY = [];
// freezing the values prevents any code from accidentally inserting new values in
if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
// These property accesses can be ignored because ngDevMode will be set to false
// when optimizing code and the whole if statement will be dropped.
// tslint:disable-next-line:no-toplevel-property-access
Object.freeze(EMPTY_OBJ);
// tslint:disable-next-line:no-toplevel-property-access
Object.freeze(EMPTY_ARRAY);
}
/**
* Returns an index of `classToSearch` in `className` taking token boundaries into account.
*
* `classIndexOf('AB A', 'A', 0)` will be 3 (not 0 since `AB!==A`)
*
* @param className A string containing classes (whitespace separated)
* @param classToSearch A class name to locate
* @param startingIndex Starting location of search
* @returns an index of the located class (or -1 if not found)
*/
function classIndexOf(className, classToSearch, startingIndex) {
ngDevMode && assertNotEqual(classToSearch, '', 'can not look for "" string.');
let end = className.length;
while (true) {
const foundIndex = className.indexOf(classToSearch, startingIndex);
if (foundIndex === -1)
return foundIndex;
if (foundIndex === 0 || className.charCodeAt(foundIndex - 1) <= 32 /* CharCode.SPACE */) {
// Ensure that it has leading whitespace
const length = classToSearch.length;
if (foundIndex + length === end ||
className.charCodeAt(foundIndex + length) <= 32 /* CharCode.SPACE */) {
// Ensure that it has trailing whitespace
return foundIndex;
}
}
// False positive, keep searching from where we left off.
startingIndex = foundIndex + 1;
}
}
/**
* Assigns all attribute values to the provided element via the inferred renderer.
*
* This function accepts two forms of attribute entries:
*
* default: (key, value):
* attrs = [key1, value1, key2, value2]
*
* namespaced: (NAMESPACE_MARKER, uri, name, value)
* attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
*
* The `attrs` array can contain a mix of both the default and namespaced entries.
* The "default" values are set without a marker, but if the function comes across
* a marker value then it will attempt to set a namespaced value. If the marker is
* not of a namespaced value then the function will quit and return the index value
* where it stopped during the iteration of the attrs array.
*
* See [AttributeMarker] to understand what the namespace marker value is.
*
* Note that this instruction does not support assigning style and class values to
* an element. See `elementStart` and `elementHostAttrs` to learn how styling values
* are applied to an element.
* @param renderer The renderer to be used
* @param native The element that the attributes will be assigned to
* @param attrs The attribute array of values that will be assigned to the element
* @returns the index value that was last accessed in the attributes array
*/
function setUpAttributes(renderer, native, attrs) {
let i = 0;
while (i < attrs.length) {
const value = attrs[i];
if (typeof value === 'number') {
// only namespace