From 4d55921412b840a0b22e7c96e3f6036089d77465 Mon Sep 17 00:00:00 2001 From: Alexandr Antonovich Date: Fri, 9 Dec 2022 21:55:35 +0200 Subject: [PATCH 01/12] added amd-loader --- .../angular-test-runner.component.ts | 16 +- .../bundles/angular-common.js | 16164 ++-- .../bundles/angular-compiler.js | 43110 +++++----- .../bundles/angular-core-testing.js | 11090 +++ .../build-umd-bundles/bundles/angular-core.js | 65342 +++++++--------- .../bundles/angular-forms.js | 12174 ++- ...ngular-platform-browser-dynamic-testing.js | 132 + .../angular-platform-browser-dynamic.js | 591 +- .../angular-platform-browser-testing.js | 79 + .../bundles/angular-platform-browser.js | 3710 +- .../bundles/rxjs-operators.js | 8277 +- .../ng2/build-umd-bundles/bundles/rxjs.js | 10089 ++- .../ng2/build-umd-bundles/bundles/tslib.js | 795 +- .../ng2/build-umd-bundles/bundles/zone.js | 6083 +- .../code-demo-runner.component.ts | 8 +- .../src/lib/runner/compile-ts-files.ts | 212 + .../src/lib/shared/fake-system-loader.js | 251 +- libs/code-demos/src/lib/shared/sandbox.ts | 17 +- 18 files changed, 85513 insertions(+), 92627 deletions(-) create mode 100644 libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-core-testing.js create mode 100644 libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-platform-browser-dynamic-testing.js create mode 100644 libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-platform-browser-testing.js create mode 100644 libs/code-demos/src/lib/runner/compile-ts-files.ts diff --git a/apps/codelab/src/app/components/angular-test-runner/angular-test-runner.component.ts b/apps/codelab/src/app/components/angular-test-runner/angular-test-runner.component.ts index 3f3a9df2d..1bc018442 100644 --- a/apps/codelab/src/app/components/angular-test-runner/angular-test-runner.component.ts +++ b/apps/codelab/src/app/components/angular-test-runner/angular-test-runner.component.ts @@ -123,6 +123,10 @@ export class SimpleAngularTestRunnerComponent { id: 'testing', url: '/assets/runner', + }, + ({ evalJs }) => { + // evalJs(this.scriptLoaderService.getScript('shim')); + // evalJs(this.scriptLoaderService.getScript('zone')); } ); @@ -134,18 +138,20 @@ export class SimpleAngularTestRunnerComponent sandbox.evalJs(this.scriptLoaderService.getScript('chai')); sandbox.evalJs(this.scriptLoaderService.getScript('mocha')); sandbox.evalJs(this.scriptLoaderService.getScript('test-bootstrap')); - sandbox.evalJs(this.scriptLoaderService.getScript('shim')); - sandbox.evalJs(this.scriptLoaderService.getScript('zone')); + // sandbox.evalJs(this.scriptLoaderService.getScript('shim')); + // sandbox.evalJs(this.scriptLoaderService.getScript('zone')); // sandbox.evalJs(this.scriptLoaderService.getScript('system-config')); - sandbox.evalJs(this.scriptLoaderService.getScript('ng-bundle')); + // sandbox.evalJs(this.scriptLoaderService.getScript('ng-bundle')); this.subscription = this.changedFilesSubject.subscribe((files) => { + addMetaInformation(sandbox, this.code); + + const hasErrors = Object.entries(files) - .filter(([path]) => path.match(/\.js$/)) + .filter(([path]) => path.match(/\.js$/) && !path.startsWith('code')) .map(([path, code]) => { try { sandbox.evalJs(`console.log('I remove modul')`); - addMetaInformation(sandbox, this.code); sandbox.evalJs(code); } catch (e) { console.groupCollapsed(e.message); diff --git a/libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-common.js b/libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-common.js index dd470da58..1c8e00aa6 100644 --- a/libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-common.js +++ b/libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-common.js @@ -1,9209 +1,7127 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' - ? factory(exports, require('@angular/core')) - : typeof define === 'function' && define.amd - ? define(['exports', '@angular/core'], factory) - : ((global = - typeof globalThis !== 'undefined' ? globalThis : global || self), - factory((global.ngCommon = {}), global.ng)); -})(this, function (exports, i0) { - 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) : + typeof define === 'function' && define.amd ? define(['exports', '@angular/core'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ngCommon = {}, global.ng)); +})(this, (function (exports, i0) { 'use strict'; - function _interopNamespaceDefault(e) { - var n = Object.create(null); - if (e) { - Object.keys(e).forEach(function (k) { - if (k !== 'default') { - var d = Object.getOwnPropertyDescriptor(e, k); - Object.defineProperty( - n, - k, - d.get - ? d - : { - enumerable: true, - get: function () { - return e[k]; - }, + function _interopNamespaceDefault(e) { + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); } - ); + }); } - }); + n.default = e; + return Object.freeze(n); } - n.default = e; - return Object.freeze(n); - } - - var i0__namespace = /*#__PURE__*/ _interopNamespaceDefault(i0); - - /** - * @license Angular v15.1.0-next.0 - * (c) 2010-2022 Google LLC. https://angular.io/ - * License: MIT - */ - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - let _DOM = null; - function getDOM() { - return _DOM; - } - function setRootDomAdapter(adapter) { - if (!_DOM) { - _DOM = adapter; - } - } - /* tslint:disable:requireParameterType */ - /** - * Provides DOM operations in an environment-agnostic way. - * - * @security Tread carefully! Interacting with the DOM directly is dangerous and - * can introduce XSS risks. - */ - class DomAdapter {} - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * A DI Token representing the main rendering context. In a browser this is the DOM Document. - * - * Note: Document might not be available in the Application Context when Application and Rendering - * Contexts are not the same (e.g. when running the application in a Web Worker). - * - * @publicApi - */ - const DOCUMENT = new i0.InjectionToken('DocumentToken'); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * This class should not be used directly by an application developer. Instead, use - * {@link Location}. - * - * `PlatformLocation` encapsulates all calls to DOM APIs, which allows the Router to be - * platform-agnostic. - * This means that we can have different implementation of `PlatformLocation` for the different - * platforms that Angular supports. For example, `@angular/platform-browser` provides an - * implementation specific to the browser environment, while `@angular/platform-server` provides - * one suitable for use with server-side rendering. - * - * The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy} - * when they need to interact with the DOM APIs like pushState, popState, etc. - * - * {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly - * by the {@link Router} in order to navigate between routes. Since all interactions between {@link - * Router} / - * {@link Location} / {@link LocationStrategy} and DOM APIs flow through the `PlatformLocation` - * class, they are all platform-agnostic. - * - * @publicApi - */ - class PlatformLocation { - historyGo(relativePosition) { - throw new Error('Not implemented'); - } - } - PlatformLocation.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PlatformLocation, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - PlatformLocation.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PlatformLocation, - providedIn: 'platform', - useFactory: useBrowserPlatformLocation, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PlatformLocation, - decorators: [ - { - type: i0.Injectable, - args: [ - { - providedIn: 'platform', - // See #23917 - useFactory: useBrowserPlatformLocation, - }, - ], - }, - ], - }); - function useBrowserPlatformLocation() { - return i0['ɵɵinject'](BrowserPlatformLocation); - } - /** - * @description - * Indicates when a location is initialized. - * - * @publicApi - */ - const LOCATION_INITIALIZED = new i0.InjectionToken('Location Initialized'); - /** - * `PlatformLocation` encapsulates all of the direct calls to platform APIs. - * This class should not be used directly by an application developer. Instead, use - * {@link Location}. - */ - class BrowserPlatformLocation extends PlatformLocation { - constructor(_doc) { - super(); - this._doc = _doc; - this._init(); - } - // This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it - /** @internal */ - _init() { - this.location = window.location; - this._history = window.history; - } - getBaseHrefFromDOM() { - return getDOM().getBaseHref(this._doc); - } - onPopState(fn) { - const window = getDOM().getGlobalEventTarget(this._doc, 'window'); - window.addEventListener('popstate', fn, false); - return () => window.removeEventListener('popstate', fn); - } - onHashChange(fn) { - const window = getDOM().getGlobalEventTarget(this._doc, 'window'); - window.addEventListener('hashchange', fn, false); - return () => window.removeEventListener('hashchange', fn); - } - get href() { - return this.location.href; - } - get protocol() { - return this.location.protocol; - } - get hostname() { - return this.location.hostname; - } - get port() { - return this.location.port; - } - get pathname() { - return this.location.pathname; - } - get search() { - return this.location.search; - } - get hash() { - return this.location.hash; - } - set pathname(newPath) { - this.location.pathname = newPath; - } - pushState(state, title, url) { - if (supportsState()) { - this._history.pushState(state, title, url); - } else { - this.location.hash = url; - } - } - replaceState(state, title, url) { - if (supportsState()) { - this._history.replaceState(state, title, url); - } else { - this.location.hash = url; - } - } - forward() { - this._history.forward(); - } - back() { - this._history.back(); - } - historyGo(relativePosition = 0) { - this._history.go(relativePosition); - } - getState() { - return this._history.state; - } - } - BrowserPlatformLocation.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: BrowserPlatformLocation, - deps: [{ token: DOCUMENT }], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - BrowserPlatformLocation.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: BrowserPlatformLocation, - providedIn: 'platform', - useFactory: createBrowserPlatformLocation, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: BrowserPlatformLocation, - decorators: [ - { - type: i0.Injectable, - args: [ - { - providedIn: 'platform', - // See #23917 - useFactory: createBrowserPlatformLocation, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [DOCUMENT], - }, - ], - }, - ]; - }, - }); - function supportsState() { - return !!window.history.pushState; - } - function createBrowserPlatformLocation() { - return new BrowserPlatformLocation(i0['ɵɵinject'](DOCUMENT)); - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Joins two parts of a URL with a slash if needed. - * - * @param start URL string - * @param end URL string - * - * - * @returns The joined URL string. - */ - function joinWithSlash(start, end) { - if (start.length == 0) { - return end; - } - if (end.length == 0) { - return start; - } - let slashes = 0; - if (start.endsWith('/')) { - slashes++; - } - if (end.startsWith('/')) { - slashes++; - } - if (slashes == 2) { - return start + end.substring(1); - } - if (slashes == 1) { - return start + end; - } - return start + '/' + end; - } - /** - * Removes a trailing slash from a URL string if needed. - * Looks for the first occurrence of either `#`, `?`, or the end of the - * line as `/` characters and removes the trailing slash if one exists. - * - * @param url URL string. - * - * @returns The URL string, modified if needed. - */ - function stripTrailingSlash(url) { - const match = url.match(/#|\?|$/); - const pathEndIdx = (match && match.index) || url.length; - const droppedSlashIdx = pathEndIdx - (url[pathEndIdx - 1] === '/' ? 1 : 0); - return url.slice(0, droppedSlashIdx) + url.slice(pathEndIdx); - } - /** - * Normalizes URL parameters by prepending with `?` if needed. - * - * @param params String of URL parameters. - * - * @returns The normalized URL parameters string. - */ - function normalizeQueryParams(params) { - return params && params[0] !== '?' ? '?' + params : params; - } + var i0__namespace = /*#__PURE__*/_interopNamespaceDefault(i0); - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Enables the `Location` service to read route state from the browser's URL. - * Angular provides two strategies: - * `HashLocationStrategy` and `PathLocationStrategy`. - * - * Applications should use the `Router` or `Location` services to - * interact with application route state. - * - * For instance, `HashLocationStrategy` produces URLs like - * http://example.com#/foo, - * and `PathLocationStrategy` produces - * http://example.com/foo as an equivalent URL. - * - * See these two classes for more. - * - * @publicApi - */ - class LocationStrategy { - historyGo(relativePosition) { - throw new Error('Not implemented'); - } - } - LocationStrategy.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LocationStrategy, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - LocationStrategy.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LocationStrategy, - providedIn: 'root', - useFactory: () => i0.inject(PathLocationStrategy), - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LocationStrategy, - decorators: [ - { - type: i0.Injectable, - args: [ - { - providedIn: 'root', - useFactory: () => i0.inject(PathLocationStrategy), - }, - ], - }, - ], - }); - /** - * A predefined [DI token](guide/glossary#di-token) for the base href - * to be used with the `PathLocationStrategy`. - * The base href is the URL prefix that should be preserved when generating - * and recognizing URLs. - * - * @usageNotes - * - * The following example shows how to use this token to configure the root app injector - * with a base href value, so that the DI framework can supply the dependency anywhere in the app. - * - * ```typescript - * import {Component, NgModule} from '@angular/core'; - * import {APP_BASE_HREF} from '@angular/common'; - * - * @NgModule({ - * providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}] - * }) - * class AppModule {} - * ``` - * - * @publicApi - */ - const APP_BASE_HREF = new i0.InjectionToken('appBaseHref'); - /** - * @description - * A {@link LocationStrategy} used to configure the {@link Location} service to - * represent its state in the - * [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the - * browser's URL. - * - * If you're using `PathLocationStrategy`, you may provide a {@link APP_BASE_HREF} - * or add a `` element to the document to override the default. - * - * For instance, if you provide an `APP_BASE_HREF` of `'/my/app/'` and call - * `location.go('/foo')`, the browser's URL will become - * `example.com/my/app/foo`. To ensure all relative URIs resolve correctly, - * the `` and/or `APP_BASE_HREF` should end with a `/`. - * - * Similarly, if you add `` to the document and call - * `location.go('/foo')`, the browser's URL will become - * `example.com/my/app/foo`. - * - * Note that when using `PathLocationStrategy`, neither the query nor - * the fragment in the `` will be preserved, as outlined - * by the [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2). - * - * @usageNotes - * - * ### Example - * - * {@example common/location/ts/path_location_component.ts region='LocationComponent'} - * - * @publicApi - */ - class PathLocationStrategy extends LocationStrategy { - constructor(_platformLocation, href) { - var _a, _b, _c; - super(); - this._platformLocation = _platformLocation; - this._removeListenerFns = []; - this._baseHref = - (_c = - (_a = - href !== null && href !== void 0 - ? href - : this._platformLocation.getBaseHrefFromDOM()) !== null && - _a !== void 0 - ? _a - : (_b = i0.inject(DOCUMENT).location) === null || _b === void 0 - ? void 0 - : _b.origin) !== null && _c !== void 0 - ? _c - : ''; - } - /** @nodoc */ - ngOnDestroy() { - while (this._removeListenerFns.length) { - this._removeListenerFns.pop()(); - } - } - onPopState(fn) { - this._removeListenerFns.push( - this._platformLocation.onPopState(fn), - this._platformLocation.onHashChange(fn) - ); - } - getBaseHref() { - return this._baseHref; - } - prepareExternalUrl(internal) { - return joinWithSlash(this._baseHref, internal); - } - path(includeHash = false) { - const pathname = - this._platformLocation.pathname + - normalizeQueryParams(this._platformLocation.search); - const hash = this._platformLocation.hash; - return hash && includeHash ? `${pathname}${hash}` : pathname; - } - pushState(state, title, url, queryParams) { - const externalUrl = this.prepareExternalUrl( - url + normalizeQueryParams(queryParams) - ); - this._platformLocation.pushState(state, title, externalUrl); - } - replaceState(state, title, url, queryParams) { - const externalUrl = this.prepareExternalUrl( - url + normalizeQueryParams(queryParams) - ); - this._platformLocation.replaceState(state, title, externalUrl); - } - forward() { - this._platformLocation.forward(); - } - back() { - this._platformLocation.back(); - } - getState() { - return this._platformLocation.getState(); - } - historyGo(relativePosition = 0) { - var _a, _b; - (_b = (_a = this._platformLocation).historyGo) === null || _b === void 0 - ? void 0 - : _b.call(_a, relativePosition); - } - } - PathLocationStrategy.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PathLocationStrategy, - deps: [ - { token: PlatformLocation }, - { token: APP_BASE_HREF, optional: true }, - ], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - PathLocationStrategy.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PathLocationStrategy, - providedIn: 'root', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PathLocationStrategy, - decorators: [ - { - type: i0.Injectable, - args: [{ providedIn: 'root' }], - }, - ], - ctorParameters: function () { - return [ - { type: PlatformLocation }, - { - type: undefined, - decorators: [ - { - type: i0.Optional, - }, - { - type: i0.Inject, - args: [APP_BASE_HREF], - }, - ], - }, - ]; - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @description - * A {@link LocationStrategy} used to configure the {@link Location} service to - * represent its state in the - * [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) - * of the browser's URL. - * - * For instance, if you call `location.go('/foo')`, the browser's URL will become - * `example.com#/foo`. - * - * @usageNotes - * - * ### Example - * - * {@example common/location/ts/hash_location_component.ts region='LocationComponent'} - * - * @publicApi - */ - class HashLocationStrategy extends LocationStrategy { - constructor(_platformLocation, _baseHref) { - super(); - this._platformLocation = _platformLocation; - this._baseHref = ''; - this._removeListenerFns = []; - if (_baseHref != null) { - this._baseHref = _baseHref; - } - } - /** @nodoc */ - ngOnDestroy() { - while (this._removeListenerFns.length) { - this._removeListenerFns.pop()(); - } - } - onPopState(fn) { - this._removeListenerFns.push( - this._platformLocation.onPopState(fn), - this._platformLocation.onHashChange(fn) - ); - } - getBaseHref() { - return this._baseHref; - } - path(includeHash = false) { - // the hash value is always prefixed with a `#` - // and if it is empty then it will stay empty - let path = this._platformLocation.hash; - if (path == null) path = '#'; - return path.length > 0 ? path.substring(1) : path; - } - prepareExternalUrl(internal) { - const url = joinWithSlash(this._baseHref, internal); - return url.length > 0 ? '#' + url : url; - } - pushState(state, title, path, queryParams) { - let url = this.prepareExternalUrl( - path + normalizeQueryParams(queryParams) - ); - if (url.length == 0) { - url = this._platformLocation.pathname; - } - this._platformLocation.pushState(state, title, url); - } - replaceState(state, title, path, queryParams) { - let url = this.prepareExternalUrl( - path + normalizeQueryParams(queryParams) - ); - if (url.length == 0) { - url = this._platformLocation.pathname; - } - this._platformLocation.replaceState(state, title, url); - } - forward() { - this._platformLocation.forward(); - } - back() { - this._platformLocation.back(); - } - getState() { - return this._platformLocation.getState(); - } - historyGo(relativePosition = 0) { - var _a, _b; - (_b = (_a = this._platformLocation).historyGo) === null || _b === void 0 - ? void 0 - : _b.call(_a, relativePosition); - } - } - HashLocationStrategy.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: HashLocationStrategy, - deps: [ - { token: PlatformLocation }, - { token: APP_BASE_HREF, optional: true }, - ], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - HashLocationStrategy.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: HashLocationStrategy, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: HashLocationStrategy, - decorators: [ - { - type: i0.Injectable, - }, - ], - ctorParameters: function () { - return [ - { type: PlatformLocation }, - { - type: undefined, - decorators: [ - { - type: i0.Optional, - }, - { - type: i0.Inject, - args: [APP_BASE_HREF], - }, - ], - }, - ]; - }, - }); + /** + * @license Angular v15.1.0-next.0 + * (c) 2010-2022 Google LLC. https://angular.io/ + * License: MIT + */ - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @description - * - * A service that applications can use to interact with a browser's URL. - * - * Depending on the `LocationStrategy` used, `Location` persists - * to the URL's path or the URL's hash segment. - * - * @usageNotes - * - * It's better to use the `Router.navigate()` service to trigger route changes. Use - * `Location` only if you need to interact with or create normalized URLs outside of - * routing. - * - * `Location` is responsible for normalizing the URL against the application's base href. - * A normalized URL is absolute from the URL host, includes the application's base href, and has no - * trailing slash: - * - `/my/app/user/123` is normalized - * - `my/app/user/123` **is not** normalized - * - `/my/app/user/123/` **is not** normalized - * - * ### Example - * - * - * - * @publicApi - */ - class Location { - constructor(locationStrategy) { - /** @internal */ - this._subject = new i0.EventEmitter(); - /** @internal */ - this._urlChangeListeners = []; - /** @internal */ - this._urlChangeSubscription = null; - this._locationStrategy = locationStrategy; - const browserBaseHref = this._locationStrategy.getBaseHref(); - this._baseHref = stripTrailingSlash(_stripIndexHtml(browserBaseHref)); - this._locationStrategy.onPopState((ev) => { - this._subject.emit({ - url: this.path(true), - pop: true, - state: ev.state, - type: ev.type, - }); - }); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + let _DOM = null; + function getDOM() { + return _DOM; + } + function setRootDomAdapter(adapter) { + if (!_DOM) { + _DOM = adapter; + } } - /** @nodoc */ - ngOnDestroy() { - var _a; - (_a = this._urlChangeSubscription) === null || _a === void 0 - ? void 0 - : _a.unsubscribe(); - this._urlChangeListeners = []; + /* tslint:disable:requireParameterType */ + /** + * Provides DOM operations in an environment-agnostic way. + * + * @security Tread carefully! Interacting with the DOM directly is dangerous and + * can introduce XSS risks. + */ + class DomAdapter { } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * A DI Token representing the main rendering context. In a browser this is the DOM Document. + * + * Note: Document might not be available in the Application Context when Application and Rendering + * Contexts are not the same (e.g. when running the application in a Web Worker). + * + * @publicApi + */ + const DOCUMENT = new i0.InjectionToken('DocumentToken'); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ /** - * Normalizes the URL path for this location. + * This class should not be used directly by an application developer. Instead, use + * {@link Location}. + * + * `PlatformLocation` encapsulates all calls to DOM APIs, which allows the Router to be + * platform-agnostic. + * This means that we can have different implementation of `PlatformLocation` for the different + * platforms that Angular supports. For example, `@angular/platform-browser` provides an + * implementation specific to the browser environment, while `@angular/platform-server` provides + * one suitable for use with server-side rendering. * - * @param includeHash True to include an anchor fragment in the path. + * The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy} + * when they need to interact with the DOM APIs like pushState, popState, etc. * - * @returns The normalized URL path. + * {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly + * by the {@link Router} in order to navigate between routes. Since all interactions between {@link + * Router} / + * {@link Location} / {@link LocationStrategy} and DOM APIs flow through the `PlatformLocation` + * class, they are all platform-agnostic. + * + * @publicApi */ - // TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is - // removed. - path(includeHash = false) { - return this.normalize(this._locationStrategy.path(includeHash)); + class PlatformLocation { + historyGo(relativePosition) { + throw new Error('Not implemented'); + } + } + PlatformLocation.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PlatformLocation, deps: [], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + PlatformLocation.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PlatformLocation, providedIn: 'platform', useFactory: useBrowserPlatformLocation }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PlatformLocation, decorators: [{ + type: i0.Injectable, + args: [{ + providedIn: 'platform', + // See #23917 + useFactory: useBrowserPlatformLocation + }] + }] }); + function useBrowserPlatformLocation() { + return i0["ɵɵinject"](BrowserPlatformLocation); } /** - * Reports the current state of the location history. - * @returns The current value of the `history.state` object. + * @description + * Indicates when a location is initialized. + * + * @publicApi + */ + const LOCATION_INITIALIZED = new i0.InjectionToken('Location Initialized'); + /** + * `PlatformLocation` encapsulates all of the direct calls to platform APIs. + * This class should not be used directly by an application developer. Instead, use + * {@link Location}. */ - getState() { - return this._locationStrategy.getState(); + class BrowserPlatformLocation extends PlatformLocation { + constructor(_doc) { + super(); + this._doc = _doc; + this._init(); + } + // This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it + /** @internal */ + _init() { + this.location = window.location; + this._history = window.history; + } + getBaseHrefFromDOM() { + return getDOM().getBaseHref(this._doc); + } + onPopState(fn) { + const window = getDOM().getGlobalEventTarget(this._doc, 'window'); + window.addEventListener('popstate', fn, false); + return () => window.removeEventListener('popstate', fn); + } + onHashChange(fn) { + const window = getDOM().getGlobalEventTarget(this._doc, 'window'); + window.addEventListener('hashchange', fn, false); + return () => window.removeEventListener('hashchange', fn); + } + get href() { + return this.location.href; + } + get protocol() { + return this.location.protocol; + } + get hostname() { + return this.location.hostname; + } + get port() { + return this.location.port; + } + get pathname() { + return this.location.pathname; + } + get search() { + return this.location.search; + } + get hash() { + return this.location.hash; + } + set pathname(newPath) { + this.location.pathname = newPath; + } + pushState(state, title, url) { + if (supportsState()) { + this._history.pushState(state, title, url); + } + else { + this.location.hash = url; + } + } + replaceState(state, title, url) { + if (supportsState()) { + this._history.replaceState(state, title, url); + } + else { + this.location.hash = url; + } + } + forward() { + this._history.forward(); + } + back() { + this._history.back(); + } + historyGo(relativePosition = 0) { + this._history.go(relativePosition); + } + getState() { + return this._history.state; + } + } + BrowserPlatformLocation.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: BrowserPlatformLocation, deps: [{ token: DOCUMENT }], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + BrowserPlatformLocation.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: BrowserPlatformLocation, providedIn: 'platform', useFactory: createBrowserPlatformLocation }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: BrowserPlatformLocation, decorators: [{ + type: i0.Injectable, + args: [{ + providedIn: 'platform', + // See #23917 + useFactory: createBrowserPlatformLocation, + }] + }], ctorParameters: function () { + return [{ type: undefined, decorators: [{ + type: i0.Inject, + args: [DOCUMENT] + }] }]; + } }); + function supportsState() { + return !!window.history.pushState; + } + function createBrowserPlatformLocation() { + return new BrowserPlatformLocation(i0["ɵɵinject"](DOCUMENT)); } + /** - * Normalizes the given path and compares to the current normalized path. + * @license + * Copyright Google LLC All Rights Reserved. * - * @param path The given URL path. - * @param query Query parameters. + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @license + * Copyright Google LLC All Rights Reserved. * - * @returns True if the given URL path is equal to the current normalized path, false - * otherwise. + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license */ - isCurrentPathEqualTo(path, query = '') { - return this.path() == this.normalize(path + normalizeQueryParams(query)); - } /** - * Normalizes a URL path by stripping any trailing slashes. + * Joins two parts of a URL with a slash if needed. + * + * @param start URL string + * @param end URL string * - * @param url String representing a URL. * - * @returns The normalized URL string. + * @returns The joined URL string. */ - normalize(url) { - return Location.stripTrailingSlash( - _stripBaseHref(this._baseHref, _stripIndexHtml(url)) - ); + function joinWithSlash(start, end) { + if (start.length == 0) { + return end; + } + if (end.length == 0) { + return start; + } + let slashes = 0; + if (start.endsWith('/')) { + slashes++; + } + if (end.startsWith('/')) { + slashes++; + } + if (slashes == 2) { + return start + end.substring(1); + } + if (slashes == 1) { + return start + end; + } + return start + '/' + end; } /** - * Normalizes an external URL path. - * If the given URL doesn't begin with a leading slash (`'/'`), adds one - * before normalizing. Adds a hash if `HashLocationStrategy` is - * in use, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use. + * Removes a trailing slash from a URL string if needed. + * Looks for the first occurrence of either `#`, `?`, or the end of the + * line as `/` characters and removes the trailing slash if one exists. * - * @param url String representing a URL. + * @param url URL string. * - * @returns A normalized platform-specific URL. + * @returns The URL string, modified if needed. */ - prepareExternalUrl(url) { - if (url && url[0] !== '/') { - url = '/' + url; - } - return this._locationStrategy.prepareExternalUrl(url); + function stripTrailingSlash(url) { + const match = url.match(/#|\?|$/); + const pathEndIdx = match && match.index || url.length; + const droppedSlashIdx = pathEndIdx - (url[pathEndIdx - 1] === '/' ? 1 : 0); + return url.slice(0, droppedSlashIdx) + url.slice(pathEndIdx); } - // TODO: rename this method to pushState /** - * Changes the browser's URL to a normalized version of a given URL, and pushes a - * new item onto the platform's history. + * Normalizes URL parameters by prepending with `?` if needed. * - * @param path URL path to normalize. - * @param query Query parameters. - * @param state Location history state. + * @param params String of URL parameters. * + * @returns The normalized URL parameters string. */ - go(path, query = '', state = null) { - this._locationStrategy.pushState(state, '', path, query); - this._notifyUrlChangeListeners( - this.prepareExternalUrl(path + normalizeQueryParams(query)), - state - ); + function normalizeQueryParams(params) { + return params && params[0] !== '?' ? '?' + params : params; } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ /** - * Changes the browser's URL to a normalized version of the given URL, and replaces - * the top item on the platform's history stack. + * Enables the `Location` service to read route state from the browser's URL. + * Angular provides two strategies: + * `HashLocationStrategy` and `PathLocationStrategy`. + * + * Applications should use the `Router` or `Location` services to + * interact with application route state. + * + * For instance, `HashLocationStrategy` produces URLs like + * http://example.com#/foo, + * and `PathLocationStrategy` produces + * http://example.com/foo as an equivalent URL. * - * @param path URL path to normalize. - * @param query Query parameters. - * @param state Location history state. + * See these two classes for more. + * + * @publicApi */ - replaceState(path, query = '', state = null) { - this._locationStrategy.replaceState(state, '', path, query); - this._notifyUrlChangeListeners( - this.prepareExternalUrl(path + normalizeQueryParams(query)), - state - ); + class LocationStrategy { + historyGo(relativePosition) { + throw new Error('Not implemented'); + } } + LocationStrategy.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: LocationStrategy, deps: [], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + LocationStrategy.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: LocationStrategy, providedIn: 'root', useFactory: () => i0.inject(PathLocationStrategy) }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: LocationStrategy, decorators: [{ + type: i0.Injectable, + args: [{ providedIn: 'root', useFactory: () => i0.inject(PathLocationStrategy) }] + }] }); + /** + * A predefined [DI token](guide/glossary#di-token) for the base href + * to be used with the `PathLocationStrategy`. + * The base href is the URL prefix that should be preserved when generating + * and recognizing URLs. + * + * @usageNotes + * + * The following example shows how to use this token to configure the root app injector + * with a base href value, so that the DI framework can supply the dependency anywhere in the app. + * + * ```typescript + * import {Component, NgModule} from '@angular/core'; + * import {APP_BASE_HREF} from '@angular/common'; + * + * @NgModule({ + * providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}] + * }) + * class AppModule {} + * ``` + * + * @publicApi + */ + const APP_BASE_HREF = new i0.InjectionToken('appBaseHref'); /** - * Navigates forward in the platform's history. + * @description + * A {@link LocationStrategy} used to configure the {@link Location} service to + * represent its state in the + * [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the + * browser's URL. + * + * If you're using `PathLocationStrategy`, you may provide a {@link APP_BASE_HREF} + * or add a `` element to the document to override the default. + * + * For instance, if you provide an `APP_BASE_HREF` of `'/my/app/'` and call + * `location.go('/foo')`, the browser's URL will become + * `example.com/my/app/foo`. To ensure all relative URIs resolve correctly, + * the `` and/or `APP_BASE_HREF` should end with a `/`. + * + * Similarly, if you add `` to the document and call + * `location.go('/foo')`, the browser's URL will become + * `example.com/my/app/foo`. + * + * Note that when using `PathLocationStrategy`, neither the query nor + * the fragment in the `` will be preserved, as outlined + * by the [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2). + * + * @usageNotes + * + * ### Example + * + * {@example common/location/ts/path_location_component.ts region='LocationComponent'} + * + * @publicApi */ - forward() { - this._locationStrategy.forward(); + class PathLocationStrategy extends LocationStrategy { + constructor(_platformLocation, href) { + var _a, _b, _c; + super(); + this._platformLocation = _platformLocation; + this._removeListenerFns = []; + this._baseHref = (_c = (_a = href !== null && href !== void 0 ? href : this._platformLocation.getBaseHrefFromDOM()) !== null && _a !== void 0 ? _a : (_b = i0.inject(DOCUMENT).location) === null || _b === void 0 ? void 0 : _b.origin) !== null && _c !== void 0 ? _c : ''; + } + /** @nodoc */ + ngOnDestroy() { + while (this._removeListenerFns.length) { + this._removeListenerFns.pop()(); + } + } + onPopState(fn) { + this._removeListenerFns.push(this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn)); + } + getBaseHref() { + return this._baseHref; + } + prepareExternalUrl(internal) { + return joinWithSlash(this._baseHref, internal); + } + path(includeHash = false) { + const pathname = this._platformLocation.pathname + normalizeQueryParams(this._platformLocation.search); + const hash = this._platformLocation.hash; + return hash && includeHash ? `${pathname}${hash}` : pathname; + } + pushState(state, title, url, queryParams) { + const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams)); + this._platformLocation.pushState(state, title, externalUrl); + } + replaceState(state, title, url, queryParams) { + const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams)); + this._platformLocation.replaceState(state, title, externalUrl); + } + forward() { + this._platformLocation.forward(); + } + back() { + this._platformLocation.back(); + } + getState() { + return this._platformLocation.getState(); + } + historyGo(relativePosition = 0) { + var _a, _b; + (_b = (_a = this._platformLocation).historyGo) === null || _b === void 0 ? void 0 : _b.call(_a, relativePosition); + } } + PathLocationStrategy.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PathLocationStrategy, deps: [{ token: PlatformLocation }, { token: APP_BASE_HREF, optional: true }], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + PathLocationStrategy.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PathLocationStrategy, providedIn: 'root' }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PathLocationStrategy, decorators: [{ + type: i0.Injectable, + args: [{ providedIn: 'root' }] + }], ctorParameters: function () { + return [{ type: PlatformLocation }, { type: undefined, decorators: [{ + type: i0.Optional + }, { + type: i0.Inject, + args: [APP_BASE_HREF] + }] }]; + } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ /** - * Navigates back in the platform's history. + * @description + * A {@link LocationStrategy} used to configure the {@link Location} service to + * represent its state in the + * [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) + * of the browser's URL. + * + * For instance, if you call `location.go('/foo')`, the browser's URL will become + * `example.com#/foo`. + * + * @usageNotes + * + * ### Example + * + * {@example common/location/ts/hash_location_component.ts region='LocationComponent'} + * + * @publicApi */ - back() { - this._locationStrategy.back(); + class HashLocationStrategy extends LocationStrategy { + constructor(_platformLocation, _baseHref) { + super(); + this._platformLocation = _platformLocation; + this._baseHref = ''; + this._removeListenerFns = []; + if (_baseHref != null) { + this._baseHref = _baseHref; + } + } + /** @nodoc */ + ngOnDestroy() { + while (this._removeListenerFns.length) { + this._removeListenerFns.pop()(); + } + } + onPopState(fn) { + this._removeListenerFns.push(this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn)); + } + getBaseHref() { + return this._baseHref; + } + path(includeHash = false) { + // the hash value is always prefixed with a `#` + // and if it is empty then it will stay empty + let path = this._platformLocation.hash; + if (path == null) + path = '#'; + return path.length > 0 ? path.substring(1) : path; + } + prepareExternalUrl(internal) { + const url = joinWithSlash(this._baseHref, internal); + return url.length > 0 ? ('#' + url) : url; + } + pushState(state, title, path, queryParams) { + let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams)); + if (url.length == 0) { + url = this._platformLocation.pathname; + } + this._platformLocation.pushState(state, title, url); + } + replaceState(state, title, path, queryParams) { + let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams)); + if (url.length == 0) { + url = this._platformLocation.pathname; + } + this._platformLocation.replaceState(state, title, url); + } + forward() { + this._platformLocation.forward(); + } + back() { + this._platformLocation.back(); + } + getState() { + return this._platformLocation.getState(); + } + historyGo(relativePosition = 0) { + var _a, _b; + (_b = (_a = this._platformLocation).historyGo) === null || _b === void 0 ? void 0 : _b.call(_a, relativePosition); + } } + HashLocationStrategy.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: HashLocationStrategy, deps: [{ token: PlatformLocation }, { token: APP_BASE_HREF, optional: true }], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + HashLocationStrategy.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: HashLocationStrategy }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: HashLocationStrategy, decorators: [{ + type: i0.Injectable + }], ctorParameters: function () { + return [{ type: PlatformLocation }, { type: undefined, decorators: [{ + type: i0.Optional + }, { + type: i0.Inject, + args: [APP_BASE_HREF] + }] }]; + } }); + /** - * Navigate to a specific page from session history, identified by its relative position to the - * current page. + * @license + * Copyright Google LLC All Rights Reserved. * - * @param relativePosition Position of the target page in the history relative to the current - * page. - * A negative value moves backwards, a positive value moves forwards, e.g. `location.historyGo(2)` - * moves forward two pages and `location.historyGo(-2)` moves back two pages. When we try to go - * beyond what's stored in the history session, we stay in the current page. Same behaviour occurs - * when `relativePosition` equals 0. - * @see https://developer.mozilla.org/en-US/docs/Web/API/History_API#Moving_to_a_specific_point_in_history + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license */ - historyGo(relativePosition = 0) { - var _a, _b; - (_b = (_a = this._locationStrategy).historyGo) === null || _b === void 0 - ? void 0 - : _b.call(_a, relativePosition); + /** + * @description + * + * A service that applications can use to interact with a browser's URL. + * + * Depending on the `LocationStrategy` used, `Location` persists + * to the URL's path or the URL's hash segment. + * + * @usageNotes + * + * It's better to use the `Router.navigate()` service to trigger route changes. Use + * `Location` only if you need to interact with or create normalized URLs outside of + * routing. + * + * `Location` is responsible for normalizing the URL against the application's base href. + * A normalized URL is absolute from the URL host, includes the application's base href, and has no + * trailing slash: + * - `/my/app/user/123` is normalized + * - `my/app/user/123` **is not** normalized + * - `/my/app/user/123/` **is not** normalized + * + * ### Example + * + * + * + * @publicApi + */ + class Location { + constructor(locationStrategy) { + /** @internal */ + this._subject = new i0.EventEmitter(); + /** @internal */ + this._urlChangeListeners = []; + /** @internal */ + this._urlChangeSubscription = null; + this._locationStrategy = locationStrategy; + const browserBaseHref = this._locationStrategy.getBaseHref(); + this._baseHref = stripTrailingSlash(_stripIndexHtml(browserBaseHref)); + this._locationStrategy.onPopState((ev) => { + this._subject.emit({ + 'url': this.path(true), + 'pop': true, + 'state': ev.state, + 'type': ev.type, + }); + }); + } + /** @nodoc */ + ngOnDestroy() { + var _a; + (_a = this._urlChangeSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe(); + this._urlChangeListeners = []; + } + /** + * Normalizes the URL path for this location. + * + * @param includeHash True to include an anchor fragment in the path. + * + * @returns The normalized URL path. + */ + // TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is + // removed. + path(includeHash = false) { + return this.normalize(this._locationStrategy.path(includeHash)); + } + /** + * Reports the current state of the location history. + * @returns The current value of the `history.state` object. + */ + getState() { + return this._locationStrategy.getState(); + } + /** + * Normalizes the given path and compares to the current normalized path. + * + * @param path The given URL path. + * @param query Query parameters. + * + * @returns True if the given URL path is equal to the current normalized path, false + * otherwise. + */ + isCurrentPathEqualTo(path, query = '') { + return this.path() == this.normalize(path + normalizeQueryParams(query)); + } + /** + * Normalizes a URL path by stripping any trailing slashes. + * + * @param url String representing a URL. + * + * @returns The normalized URL string. + */ + normalize(url) { + return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url))); + } + /** + * Normalizes an external URL path. + * If the given URL doesn't begin with a leading slash (`'/'`), adds one + * before normalizing. Adds a hash if `HashLocationStrategy` is + * in use, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use. + * + * @param url String representing a URL. + * + * @returns A normalized platform-specific URL. + */ + prepareExternalUrl(url) { + if (url && url[0] !== '/') { + url = '/' + url; + } + return this._locationStrategy.prepareExternalUrl(url); + } + // TODO: rename this method to pushState + /** + * Changes the browser's URL to a normalized version of a given URL, and pushes a + * new item onto the platform's history. + * + * @param path URL path to normalize. + * @param query Query parameters. + * @param state Location history state. + * + */ + go(path, query = '', state = null) { + this._locationStrategy.pushState(state, '', path, query); + this._notifyUrlChangeListeners(this.prepareExternalUrl(path + normalizeQueryParams(query)), state); + } + /** + * Changes the browser's URL to a normalized version of the given URL, and replaces + * the top item on the platform's history stack. + * + * @param path URL path to normalize. + * @param query Query parameters. + * @param state Location history state. + */ + replaceState(path, query = '', state = null) { + this._locationStrategy.replaceState(state, '', path, query); + this._notifyUrlChangeListeners(this.prepareExternalUrl(path + normalizeQueryParams(query)), state); + } + /** + * Navigates forward in the platform's history. + */ + forward() { + this._locationStrategy.forward(); + } + /** + * Navigates back in the platform's history. + */ + back() { + this._locationStrategy.back(); + } + /** + * Navigate to a specific page from session history, identified by its relative position to the + * current page. + * + * @param relativePosition Position of the target page in the history relative to the current + * page. + * A negative value moves backwards, a positive value moves forwards, e.g. `location.historyGo(2)` + * moves forward two pages and `location.historyGo(-2)` moves back two pages. When we try to go + * beyond what's stored in the history session, we stay in the current page. Same behaviour occurs + * when `relativePosition` equals 0. + * @see https://developer.mozilla.org/en-US/docs/Web/API/History_API#Moving_to_a_specific_point_in_history + */ + historyGo(relativePosition = 0) { + var _a, _b; + (_b = (_a = this._locationStrategy).historyGo) === null || _b === void 0 ? void 0 : _b.call(_a, relativePosition); + } + /** + * Registers a URL change listener. Use to catch updates performed by the Angular + * framework that are not detectible through "popstate" or "hashchange" events. + * + * @param fn The change handler function, which take a URL and a location history state. + * @returns A function that, when executed, unregisters a URL change listener. + */ + onUrlChange(fn) { + this._urlChangeListeners.push(fn); + if (!this._urlChangeSubscription) { + this._urlChangeSubscription = this.subscribe(v => { + this._notifyUrlChangeListeners(v.url, v.state); + }); + } + return () => { + var _a; + const fnIndex = this._urlChangeListeners.indexOf(fn); + this._urlChangeListeners.splice(fnIndex, 1); + if (this._urlChangeListeners.length === 0) { + (_a = this._urlChangeSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe(); + this._urlChangeSubscription = null; + } + }; + } + /** @internal */ + _notifyUrlChangeListeners(url = '', state) { + this._urlChangeListeners.forEach(fn => fn(url, state)); + } + /** + * Subscribes to the platform's `popState` events. + * + * Note: `Location.go()` does not trigger the `popState` event in the browser. Use + * `Location.onUrlChange()` to subscribe to URL changes instead. + * + * @param value Event that is triggered when the state history changes. + * @param exception The exception to throw. + * + * @see [onpopstate](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate) + * + * @returns Subscribed events. + */ + subscribe(onNext, onThrow, onReturn) { + return this._subject.subscribe({ next: onNext, error: onThrow, complete: onReturn }); + } } /** - * Registers a URL change listener. Use to catch updates performed by the Angular - * framework that are not detectible through "popstate" or "hashchange" events. + * Normalizes URL parameters by prepending with `?` if needed. * - * @param fn The change handler function, which take a URL and a location history state. - * @returns A function that, when executed, unregisters a URL change listener. + * @param params String of URL parameters. + * + * @returns The normalized URL parameters string. */ - onUrlChange(fn) { - this._urlChangeListeners.push(fn); - if (!this._urlChangeSubscription) { - this._urlChangeSubscription = this.subscribe((v) => { - this._notifyUrlChangeListeners(v.url, v.state); - }); - } - return () => { - var _a; - const fnIndex = this._urlChangeListeners.indexOf(fn); - this._urlChangeListeners.splice(fnIndex, 1); - if (this._urlChangeListeners.length === 0) { - (_a = this._urlChangeSubscription) === null || _a === void 0 - ? void 0 - : _a.unsubscribe(); - this._urlChangeSubscription = null; - } - }; + Location.normalizeQueryParams = normalizeQueryParams; + /** + * Joins two parts of a URL with a slash if needed. + * + * @param start URL string + * @param end URL string + * + * + * @returns The joined URL string. + */ + Location.joinWithSlash = joinWithSlash; + /** + * Removes a trailing slash from a URL string if needed. + * Looks for the first occurrence of either `#`, `?`, or the end of the + * line as `/` characters and removes the trailing slash if one exists. + * + * @param url URL string. + * + * @returns The URL string, modified if needed. + */ + Location.stripTrailingSlash = stripTrailingSlash; + Location.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: Location, deps: [{ token: LocationStrategy }], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + Location.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: Location, providedIn: 'root', useFactory: createLocation }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: Location, decorators: [{ + type: i0.Injectable, + args: [{ + providedIn: 'root', + // See #23917 + useFactory: createLocation, + }] + }], ctorParameters: function () { return [{ type: LocationStrategy }]; } }); + function createLocation() { + return new Location(i0["ɵɵinject"](LocationStrategy)); + } + function _stripBaseHref(baseHref, url) { + return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url; + } + function _stripIndexHtml(url) { + return url.replace(/\/index.html$/, ''); } - /** @internal */ - _notifyUrlChangeListeners(url = '', state) { - this._urlChangeListeners.forEach((fn) => fn(url, state)); - } - /** - * Subscribes to the platform's `popState` events. - * - * Note: `Location.go()` does not trigger the `popState` event in the browser. Use - * `Location.onUrlChange()` to subscribe to URL changes instead. - * - * @param value Event that is triggered when the state history changes. - * @param exception The exception to throw. - * - * @see [onpopstate](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate) - * - * @returns Subscribed events. - */ - subscribe(onNext, onThrow, onReturn) { - return this._subject.subscribe({ - next: onNext, - error: onThrow, - complete: onReturn, - }); - } - } - /** - * Normalizes URL parameters by prepending with `?` if needed. - * - * @param params String of URL parameters. - * - * @returns The normalized URL parameters string. - */ - Location.normalizeQueryParams = normalizeQueryParams; - /** - * Joins two parts of a URL with a slash if needed. - * - * @param start URL string - * @param end URL string - * - * - * @returns The joined URL string. - */ - Location.joinWithSlash = joinWithSlash; - /** - * Removes a trailing slash from a URL string if needed. - * Looks for the first occurrence of either `#`, `?`, or the end of the - * line as `/` characters and removes the trailing slash if one exists. - * - * @param url URL string. - * - * @returns The URL string, modified if needed. - */ - Location.stripTrailingSlash = stripTrailingSlash; - Location.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: Location, - deps: [{ token: LocationStrategy }], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - Location.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: Location, - providedIn: 'root', - useFactory: createLocation, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: Location, - decorators: [ - { - type: i0.Injectable, - args: [ - { - providedIn: 'root', - // See #23917 - useFactory: createLocation, - }, - ], - }, - ], - ctorParameters: function () { - return [{ type: LocationStrategy }]; - }, - }); - function createLocation() { - return new Location(i0['ɵɵinject'](LocationStrategy)); - } - function _stripBaseHref(baseHref, url) { - return baseHref && url.startsWith(baseHref) - ? url.substring(baseHref.length) - : url; - } - function _stripIndexHtml(url) { - return url.replace(/\/index.html$/, ''); - } - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** @internal */ - const CURRENCIES_EN = { - ADP: [undefined, undefined, 0], - AFN: [undefined, '؋', 0], - ALL: [undefined, undefined, 0], - AMD: [undefined, '֏', 2], - AOA: [undefined, 'Kz'], - ARS: [undefined, '$'], - AUD: ['A$', '$'], - AZN: [undefined, '₼'], - BAM: [undefined, 'KM'], - BBD: [undefined, '$'], - BDT: [undefined, '৳'], - BHD: [undefined, undefined, 3], - BIF: [undefined, undefined, 0], - BMD: [undefined, '$'], - BND: [undefined, '$'], - BOB: [undefined, 'Bs'], - BRL: ['R$'], - BSD: [undefined, '$'], - BWP: [undefined, 'P'], - BYN: [undefined, undefined, 2], - BYR: [undefined, undefined, 0], - BZD: [undefined, '$'], - CAD: ['CA$', '$', 2], - CHF: [undefined, undefined, 2], - CLF: [undefined, undefined, 4], - CLP: [undefined, '$', 0], - CNY: ['CN¥', '¥'], - COP: [undefined, '$', 2], - CRC: [undefined, '₡', 2], - CUC: [undefined, '$'], - CUP: [undefined, '$'], - CZK: [undefined, 'Kč', 2], - DJF: [undefined, undefined, 0], - DKK: [undefined, 'kr', 2], - DOP: [undefined, '$'], - EGP: [undefined, 'E£'], - ESP: [undefined, '₧', 0], - EUR: ['€'], - FJD: [undefined, '$'], - FKP: [undefined, '£'], - GBP: ['£'], - GEL: [undefined, '₾'], - GHS: [undefined, 'GH₵'], - GIP: [undefined, '£'], - GNF: [undefined, 'FG', 0], - GTQ: [undefined, 'Q'], - GYD: [undefined, '$', 2], - HKD: ['HK$', '$'], - HNL: [undefined, 'L'], - HRK: [undefined, 'kn'], - HUF: [undefined, 'Ft', 2], - IDR: [undefined, 'Rp', 2], - ILS: ['₪'], - INR: ['₹'], - IQD: [undefined, undefined, 0], - IRR: [undefined, undefined, 0], - ISK: [undefined, 'kr', 0], - ITL: [undefined, undefined, 0], - JMD: [undefined, '$'], - JOD: [undefined, undefined, 3], - JPY: ['¥', undefined, 0], - KHR: [undefined, '៛'], - KMF: [undefined, 'CF', 0], - KPW: [undefined, '₩', 0], - KRW: ['₩', undefined, 0], - KWD: [undefined, undefined, 3], - KYD: [undefined, '$'], - KZT: [undefined, '₸'], - LAK: [undefined, '₭', 0], - LBP: [undefined, 'L£', 0], - LKR: [undefined, 'Rs'], - LRD: [undefined, '$'], - LTL: [undefined, 'Lt'], - LUF: [undefined, undefined, 0], - LVL: [undefined, 'Ls'], - LYD: [undefined, undefined, 3], - MGA: [undefined, 'Ar', 0], - MGF: [undefined, undefined, 0], - MMK: [undefined, 'K', 0], - MNT: [undefined, '₮', 2], - MRO: [undefined, undefined, 0], - MUR: [undefined, 'Rs', 2], - MXN: ['MX$', '$'], - MYR: [undefined, 'RM'], - NAD: [undefined, '$'], - NGN: [undefined, '₦'], - NIO: [undefined, 'C$'], - NOK: [undefined, 'kr', 2], - NPR: [undefined, 'Rs'], - NZD: ['NZ$', '$'], - OMR: [undefined, undefined, 3], - PHP: ['₱'], - PKR: [undefined, 'Rs', 2], - PLN: [undefined, 'zł'], - PYG: [undefined, '₲', 0], - RON: [undefined, 'lei'], - RSD: [undefined, undefined, 0], - RUB: [undefined, '₽'], - RWF: [undefined, 'RF', 0], - SBD: [undefined, '$'], - SEK: [undefined, 'kr', 2], - SGD: [undefined, '$'], - SHP: [undefined, '£'], - SLE: [undefined, undefined, 2], - SLL: [undefined, undefined, 0], - SOS: [undefined, undefined, 0], - SRD: [undefined, '$'], - SSP: [undefined, '£'], - STD: [undefined, undefined, 0], - STN: [undefined, 'Db'], - SYP: [undefined, '£', 0], - THB: [undefined, '฿'], - TMM: [undefined, undefined, 0], - TND: [undefined, undefined, 3], - TOP: [undefined, 'T$'], - TRL: [undefined, undefined, 0], - TRY: [undefined, '₺'], - TTD: [undefined, '$'], - TWD: ['NT$', '$', 2], - TZS: [undefined, undefined, 2], - UAH: [undefined, '₴'], - UGX: [undefined, undefined, 0], - USD: ['$'], - UYI: [undefined, undefined, 0], - UYU: [undefined, '$'], - UYW: [undefined, undefined, 4], - UZS: [undefined, undefined, 2], - VEF: [undefined, 'Bs', 2], - VND: ['₫', undefined, 0], - VUV: [undefined, undefined, 0], - XAF: ['FCFA', undefined, 0], - XCD: ['EC$', '$'], - XOF: ['F CFA', undefined, 0], - XPF: ['CFPF', undefined, 0], - XXX: ['¤'], - YER: [undefined, undefined, 0], - ZAR: [undefined, 'R'], - ZMK: [undefined, undefined, 0], - ZMW: [undefined, 'ZK'], - ZWD: [undefined, undefined, 0], - }; + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** @internal */ + const CURRENCIES_EN = { "ADP": [undefined, undefined, 0], "AFN": [undefined, "؋", 0], "ALL": [undefined, undefined, 0], "AMD": [undefined, "֏", 2], "AOA": [undefined, "Kz"], "ARS": [undefined, "$"], "AUD": ["A$", "$"], "AZN": [undefined, "₼"], "BAM": [undefined, "KM"], "BBD": [undefined, "$"], "BDT": [undefined, "৳"], "BHD": [undefined, undefined, 3], "BIF": [undefined, undefined, 0], "BMD": [undefined, "$"], "BND": [undefined, "$"], "BOB": [undefined, "Bs"], "BRL": ["R$"], "BSD": [undefined, "$"], "BWP": [undefined, "P"], "BYN": [undefined, undefined, 2], "BYR": [undefined, undefined, 0], "BZD": [undefined, "$"], "CAD": ["CA$", "$", 2], "CHF": [undefined, undefined, 2], "CLF": [undefined, undefined, 4], "CLP": [undefined, "$", 0], "CNY": ["CN¥", "¥"], "COP": [undefined, "$", 2], "CRC": [undefined, "₡", 2], "CUC": [undefined, "$"], "CUP": [undefined, "$"], "CZK": [undefined, "Kč", 2], "DJF": [undefined, undefined, 0], "DKK": [undefined, "kr", 2], "DOP": [undefined, "$"], "EGP": [undefined, "E£"], "ESP": [undefined, "₧", 0], "EUR": ["€"], "FJD": [undefined, "$"], "FKP": [undefined, "£"], "GBP": ["£"], "GEL": [undefined, "₾"], "GHS": [undefined, "GH₵"], "GIP": [undefined, "£"], "GNF": [undefined, "FG", 0], "GTQ": [undefined, "Q"], "GYD": [undefined, "$", 2], "HKD": ["HK$", "$"], "HNL": [undefined, "L"], "HRK": [undefined, "kn"], "HUF": [undefined, "Ft", 2], "IDR": [undefined, "Rp", 2], "ILS": ["₪"], "INR": ["₹"], "IQD": [undefined, undefined, 0], "IRR": [undefined, undefined, 0], "ISK": [undefined, "kr", 0], "ITL": [undefined, undefined, 0], "JMD": [undefined, "$"], "JOD": [undefined, undefined, 3], "JPY": ["¥", undefined, 0], "KHR": [undefined, "៛"], "KMF": [undefined, "CF", 0], "KPW": [undefined, "₩", 0], "KRW": ["₩", undefined, 0], "KWD": [undefined, undefined, 3], "KYD": [undefined, "$"], "KZT": [undefined, "₸"], "LAK": [undefined, "₭", 0], "LBP": [undefined, "L£", 0], "LKR": [undefined, "Rs"], "LRD": [undefined, "$"], "LTL": [undefined, "Lt"], "LUF": [undefined, undefined, 0], "LVL": [undefined, "Ls"], "LYD": [undefined, undefined, 3], "MGA": [undefined, "Ar", 0], "MGF": [undefined, undefined, 0], "MMK": [undefined, "K", 0], "MNT": [undefined, "₮", 2], "MRO": [undefined, undefined, 0], "MUR": [undefined, "Rs", 2], "MXN": ["MX$", "$"], "MYR": [undefined, "RM"], "NAD": [undefined, "$"], "NGN": [undefined, "₦"], "NIO": [undefined, "C$"], "NOK": [undefined, "kr", 2], "NPR": [undefined, "Rs"], "NZD": ["NZ$", "$"], "OMR": [undefined, undefined, 3], "PHP": ["₱"], "PKR": [undefined, "Rs", 2], "PLN": [undefined, "zł"], "PYG": [undefined, "₲", 0], "RON": [undefined, "lei"], "RSD": [undefined, undefined, 0], "RUB": [undefined, "₽"], "RWF": [undefined, "RF", 0], "SBD": [undefined, "$"], "SEK": [undefined, "kr", 2], "SGD": [undefined, "$"], "SHP": [undefined, "£"], "SLE": [undefined, undefined, 2], "SLL": [undefined, undefined, 0], "SOS": [undefined, undefined, 0], "SRD": [undefined, "$"], "SSP": [undefined, "£"], "STD": [undefined, undefined, 0], "STN": [undefined, "Db"], "SYP": [undefined, "£", 0], "THB": [undefined, "฿"], "TMM": [undefined, undefined, 0], "TND": [undefined, undefined, 3], "TOP": [undefined, "T$"], "TRL": [undefined, undefined, 0], "TRY": [undefined, "₺"], "TTD": [undefined, "$"], "TWD": ["NT$", "$", 2], "TZS": [undefined, undefined, 2], "UAH": [undefined, "₴"], "UGX": [undefined, undefined, 0], "USD": ["$"], "UYI": [undefined, undefined, 0], "UYU": [undefined, "$"], "UYW": [undefined, undefined, 4], "UZS": [undefined, undefined, 2], "VEF": [undefined, "Bs", 2], "VND": ["₫", undefined, 0], "VUV": [undefined, undefined, 0], "XAF": ["FCFA", undefined, 0], "XCD": ["EC$", "$"], "XOF": ["F CFA", undefined, 0], "XPF": ["CFPF", undefined, 0], "XXX": ["¤"], "YER": [undefined, undefined, 0], "ZAR": [undefined, "R"], "ZMK": [undefined, undefined, 0], "ZMW": [undefined, "ZK"], "ZWD": [undefined, undefined, 0] }; - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Format styles that can be used to represent numbers. - * @see `getLocaleNumberFormat()`. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - exports.NumberFormatStyle = void 0; - (function (NumberFormatStyle) { - NumberFormatStyle[(NumberFormatStyle['Decimal'] = 0)] = 'Decimal'; - NumberFormatStyle[(NumberFormatStyle['Percent'] = 1)] = 'Percent'; - NumberFormatStyle[(NumberFormatStyle['Currency'] = 2)] = 'Currency'; - NumberFormatStyle[(NumberFormatStyle['Scientific'] = 3)] = 'Scientific'; - })(exports.NumberFormatStyle || (exports.NumberFormatStyle = {})); - /** - * Plurality cases used for translating plurals to different languages. - * - * @see `NgPlural` - * @see `NgPluralCase` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - exports.Plural = void 0; - (function (Plural) { - Plural[(Plural['Zero'] = 0)] = 'Zero'; - Plural[(Plural['One'] = 1)] = 'One'; - Plural[(Plural['Two'] = 2)] = 'Two'; - Plural[(Plural['Few'] = 3)] = 'Few'; - Plural[(Plural['Many'] = 4)] = 'Many'; - Plural[(Plural['Other'] = 5)] = 'Other'; - })(exports.Plural || (exports.Plural = {})); - /** - * Context-dependant translation forms for strings. - * Typically the standalone version is for the nominative form of the word, - * and the format version is used for the genitive case. - * @see [CLDR website](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles) - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - exports.FormStyle = void 0; - (function (FormStyle) { - FormStyle[(FormStyle['Format'] = 0)] = 'Format'; - FormStyle[(FormStyle['Standalone'] = 1)] = 'Standalone'; - })(exports.FormStyle || (exports.FormStyle = {})); - /** - * String widths available for translations. - * The specific character widths are locale-specific. - * Examples are given for the word "Sunday" in English. - * - * @publicApi - */ - exports.TranslationWidth = void 0; - (function (TranslationWidth) { - /** 1 character for `en-US`. For example: 'S' */ - TranslationWidth[(TranslationWidth['Narrow'] = 0)] = 'Narrow'; - /** 3 characters for `en-US`. For example: 'Sun' */ - TranslationWidth[(TranslationWidth['Abbreviated'] = 1)] = 'Abbreviated'; - /** Full length for `en-US`. For example: "Sunday" */ - TranslationWidth[(TranslationWidth['Wide'] = 2)] = 'Wide'; - /** 2 characters for `en-US`, For example: "Su" */ - TranslationWidth[(TranslationWidth['Short'] = 3)] = 'Short'; - })(exports.TranslationWidth || (exports.TranslationWidth = {})); - /** - * String widths available for date-time formats. - * The specific character widths are locale-specific. - * Examples are given for `en-US`. - * - * @see `getLocaleDateFormat()` - * @see `getLocaleTimeFormat()` - * @see `getLocaleDateTimeFormat()` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * @publicApi - */ - exports.FormatWidth = void 0; - (function (FormatWidth) { - /** - * For `en-US`, 'M/d/yy, h:mm a'` - * (Example: `6/15/15, 9:03 AM`) - */ - FormatWidth[(FormatWidth['Short'] = 0)] = 'Short'; - /** - * For `en-US`, `'MMM d, y, h:mm:ss a'` - * (Example: `Jun 15, 2015, 9:03:01 AM`) - */ - FormatWidth[(FormatWidth['Medium'] = 1)] = 'Medium'; - /** - * For `en-US`, `'MMMM d, y, h:mm:ss a z'` - * (Example: `June 15, 2015 at 9:03:01 AM GMT+1`) - */ - FormatWidth[(FormatWidth['Long'] = 2)] = 'Long'; - /** - * For `en-US`, `'EEEE, MMMM d, y, h:mm:ss a zzzz'` - * (Example: `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00`) - */ - FormatWidth[(FormatWidth['Full'] = 3)] = 'Full'; - })(exports.FormatWidth || (exports.FormatWidth = {})); - /** - * Symbols that can be used to replace placeholders in number patterns. - * Examples are based on `en-US` values. - * - * @see `getLocaleNumberSymbol()` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - exports.NumberSymbol = void 0; - (function (NumberSymbol) { - /** - * Decimal separator. - * For `en-US`, the dot character. - * Example: 2,345`.`67 - */ - NumberSymbol[(NumberSymbol['Decimal'] = 0)] = 'Decimal'; - /** - * Grouping separator, typically for thousands. - * For `en-US`, the comma character. - * Example: 2`,`345.67 - */ - NumberSymbol[(NumberSymbol['Group'] = 1)] = 'Group'; - /** - * List-item separator. - * Example: "one, two, and three" - */ - NumberSymbol[(NumberSymbol['List'] = 2)] = 'List'; - /** - * Sign for percentage (out of 100). - * Example: 23.4% - */ - NumberSymbol[(NumberSymbol['PercentSign'] = 3)] = 'PercentSign'; - /** - * Sign for positive numbers. - * Example: +23 - */ - NumberSymbol[(NumberSymbol['PlusSign'] = 4)] = 'PlusSign'; - /** - * Sign for negative numbers. - * Example: -23 - */ - NumberSymbol[(NumberSymbol['MinusSign'] = 5)] = 'MinusSign'; - /** - * Computer notation for exponential value (n times a power of 10). - * Example: 1.2E3 - */ - NumberSymbol[(NumberSymbol['Exponential'] = 6)] = 'Exponential'; - /** - * Human-readable format of exponential. - * Example: 1.2x103 - */ - NumberSymbol[(NumberSymbol['SuperscriptingExponent'] = 7)] = - 'SuperscriptingExponent'; - /** - * Sign for permille (out of 1000). - * Example: 23.4‰ - */ - NumberSymbol[(NumberSymbol['PerMille'] = 8)] = 'PerMille'; - /** - * Infinity, can be used with plus and minus. - * Example: ∞, +∞, -∞ - */ - NumberSymbol[(NumberSymbol['Infinity'] = 9)] = 'Infinity'; - /** - * Not a number. - * Example: NaN - */ - NumberSymbol[(NumberSymbol['NaN'] = 10)] = 'NaN'; - /** - * Symbol used between time units. - * Example: 10:52 - */ - NumberSymbol[(NumberSymbol['TimeSeparator'] = 11)] = 'TimeSeparator'; - /** - * Decimal separator for currency values (fallback to `Decimal`). - * Example: $2,345.67 - */ - NumberSymbol[(NumberSymbol['CurrencyDecimal'] = 12)] = 'CurrencyDecimal'; - /** - * Group separator for currency values (fallback to `Group`). - * Example: $2,345.67 - */ - NumberSymbol[(NumberSymbol['CurrencyGroup'] = 13)] = 'CurrencyGroup'; - })(exports.NumberSymbol || (exports.NumberSymbol = {})); - /** - * The value for each day of the week, based on the `en-US` locale - * - * @publicApi - */ - exports.WeekDay = void 0; - (function (WeekDay) { - WeekDay[(WeekDay['Sunday'] = 0)] = 'Sunday'; - WeekDay[(WeekDay['Monday'] = 1)] = 'Monday'; - WeekDay[(WeekDay['Tuesday'] = 2)] = 'Tuesday'; - WeekDay[(WeekDay['Wednesday'] = 3)] = 'Wednesday'; - WeekDay[(WeekDay['Thursday'] = 4)] = 'Thursday'; - WeekDay[(WeekDay['Friday'] = 5)] = 'Friday'; - WeekDay[(WeekDay['Saturday'] = 6)] = 'Saturday'; - })(exports.WeekDay || (exports.WeekDay = {})); - /** - * Retrieves the locale ID from the currently loaded locale. - * The loaded locale could be, for example, a global one rather than a regional one. - * @param locale A locale code, such as `fr-FR`. - * @returns The locale code. For example, `fr`. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleId(locale) { - return i0['ɵfindLocaleData'](locale)[i0['ɵLocaleDataIndex'].LocaleId]; - } - /** - * Retrieves day period strings for the given locale. - * - * @param locale A locale code for the locale format rules to use. - * @param formStyle The required grammatical form. - * @param width The required character width. - * @returns An array of localized period strings. For example, `[AM, PM]` for `en-US`. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleDayPeriods(locale, formStyle, width) { - const data = i0['ɵfindLocaleData'](locale); - const amPmData = [ - data[i0['ɵLocaleDataIndex'].DayPeriodsFormat], - data[i0['ɵLocaleDataIndex'].DayPeriodsStandalone], - ]; - const amPm = getLastDefinedValue(amPmData, formStyle); - return getLastDefinedValue(amPm, width); - } - /** - * Retrieves days of the week for the given locale, using the Gregorian calendar. - * - * @param locale A locale code for the locale format rules to use. - * @param formStyle The required grammatical form. - * @param width The required character width. - * @returns An array of localized name strings. - * For example,`[Sunday, Monday, ... Saturday]` for `en-US`. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleDayNames(locale, formStyle, width) { - const data = i0['ɵfindLocaleData'](locale); - const daysData = [ - data[i0['ɵLocaleDataIndex'].DaysFormat], - data[i0['ɵLocaleDataIndex'].DaysStandalone], - ]; - const days = getLastDefinedValue(daysData, formStyle); - return getLastDefinedValue(days, width); - } - /** - * Retrieves months of the year for the given locale, using the Gregorian calendar. - * - * @param locale A locale code for the locale format rules to use. - * @param formStyle The required grammatical form. - * @param width The required character width. - * @returns An array of localized name strings. - * For example, `[January, February, ...]` for `en-US`. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleMonthNames(locale, formStyle, width) { - const data = i0['ɵfindLocaleData'](locale); - const monthsData = [ - data[i0['ɵLocaleDataIndex'].MonthsFormat], - data[i0['ɵLocaleDataIndex'].MonthsStandalone], - ]; - const months = getLastDefinedValue(monthsData, formStyle); - return getLastDefinedValue(months, width); - } - /** - * Retrieves Gregorian-calendar eras for the given locale. + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Format styles that can be used to represent numbers. + * @see `getLocaleNumberFormat()`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + exports.NumberFormatStyle = void 0; + (function (NumberFormatStyle) { + NumberFormatStyle[NumberFormatStyle["Decimal"] = 0] = "Decimal"; + NumberFormatStyle[NumberFormatStyle["Percent"] = 1] = "Percent"; + NumberFormatStyle[NumberFormatStyle["Currency"] = 2] = "Currency"; + NumberFormatStyle[NumberFormatStyle["Scientific"] = 3] = "Scientific"; + })(exports.NumberFormatStyle || (exports.NumberFormatStyle = {})); + /** + * Plurality cases used for translating plurals to different languages. + * + * @see `NgPlural` + * @see `NgPluralCase` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + exports.Plural = void 0; + (function (Plural) { + Plural[Plural["Zero"] = 0] = "Zero"; + Plural[Plural["One"] = 1] = "One"; + Plural[Plural["Two"] = 2] = "Two"; + Plural[Plural["Few"] = 3] = "Few"; + Plural[Plural["Many"] = 4] = "Many"; + Plural[Plural["Other"] = 5] = "Other"; + })(exports.Plural || (exports.Plural = {})); + /** + * Context-dependant translation forms for strings. + * Typically the standalone version is for the nominative form of the word, + * and the format version is used for the genitive case. + * @see [CLDR website](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles) + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + exports.FormStyle = void 0; + (function (FormStyle) { + FormStyle[FormStyle["Format"] = 0] = "Format"; + FormStyle[FormStyle["Standalone"] = 1] = "Standalone"; + })(exports.FormStyle || (exports.FormStyle = {})); + /** + * String widths available for translations. + * The specific character widths are locale-specific. + * Examples are given for the word "Sunday" in English. + * + * @publicApi + */ + exports.TranslationWidth = void 0; + (function (TranslationWidth) { + /** 1 character for `en-US`. For example: 'S' */ + TranslationWidth[TranslationWidth["Narrow"] = 0] = "Narrow"; + /** 3 characters for `en-US`. For example: 'Sun' */ + TranslationWidth[TranslationWidth["Abbreviated"] = 1] = "Abbreviated"; + /** Full length for `en-US`. For example: "Sunday" */ + TranslationWidth[TranslationWidth["Wide"] = 2] = "Wide"; + /** 2 characters for `en-US`, For example: "Su" */ + TranslationWidth[TranslationWidth["Short"] = 3] = "Short"; + })(exports.TranslationWidth || (exports.TranslationWidth = {})); + /** + * String widths available for date-time formats. + * The specific character widths are locale-specific. + * Examples are given for `en-US`. + * + * @see `getLocaleDateFormat()` + * @see `getLocaleTimeFormat()` + * @see `getLocaleDateTimeFormat()` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * @publicApi + */ + exports.FormatWidth = void 0; + (function (FormatWidth) { + /** + * For `en-US`, 'M/d/yy, h:mm a'` + * (Example: `6/15/15, 9:03 AM`) + */ + FormatWidth[FormatWidth["Short"] = 0] = "Short"; + /** + * For `en-US`, `'MMM d, y, h:mm:ss a'` + * (Example: `Jun 15, 2015, 9:03:01 AM`) + */ + FormatWidth[FormatWidth["Medium"] = 1] = "Medium"; + /** + * For `en-US`, `'MMMM d, y, h:mm:ss a z'` + * (Example: `June 15, 2015 at 9:03:01 AM GMT+1`) + */ + FormatWidth[FormatWidth["Long"] = 2] = "Long"; + /** + * For `en-US`, `'EEEE, MMMM d, y, h:mm:ss a zzzz'` + * (Example: `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00`) + */ + FormatWidth[FormatWidth["Full"] = 3] = "Full"; + })(exports.FormatWidth || (exports.FormatWidth = {})); + /** + * Symbols that can be used to replace placeholders in number patterns. + * Examples are based on `en-US` values. + * + * @see `getLocaleNumberSymbol()` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + exports.NumberSymbol = void 0; + (function (NumberSymbol) { + /** + * Decimal separator. + * For `en-US`, the dot character. + * Example: 2,345`.`67 + */ + NumberSymbol[NumberSymbol["Decimal"] = 0] = "Decimal"; + /** + * Grouping separator, typically for thousands. + * For `en-US`, the comma character. + * Example: 2`,`345.67 + */ + NumberSymbol[NumberSymbol["Group"] = 1] = "Group"; + /** + * List-item separator. + * Example: "one, two, and three" + */ + NumberSymbol[NumberSymbol["List"] = 2] = "List"; + /** + * Sign for percentage (out of 100). + * Example: 23.4% + */ + NumberSymbol[NumberSymbol["PercentSign"] = 3] = "PercentSign"; + /** + * Sign for positive numbers. + * Example: +23 + */ + NumberSymbol[NumberSymbol["PlusSign"] = 4] = "PlusSign"; + /** + * Sign for negative numbers. + * Example: -23 + */ + NumberSymbol[NumberSymbol["MinusSign"] = 5] = "MinusSign"; + /** + * Computer notation for exponential value (n times a power of 10). + * Example: 1.2E3 + */ + NumberSymbol[NumberSymbol["Exponential"] = 6] = "Exponential"; + /** + * Human-readable format of exponential. + * Example: 1.2x103 + */ + NumberSymbol[NumberSymbol["SuperscriptingExponent"] = 7] = "SuperscriptingExponent"; + /** + * Sign for permille (out of 1000). + * Example: 23.4‰ + */ + NumberSymbol[NumberSymbol["PerMille"] = 8] = "PerMille"; + /** + * Infinity, can be used with plus and minus. + * Example: ∞, +∞, -∞ + */ + NumberSymbol[NumberSymbol["Infinity"] = 9] = "Infinity"; + /** + * Not a number. + * Example: NaN + */ + NumberSymbol[NumberSymbol["NaN"] = 10] = "NaN"; + /** + * Symbol used between time units. + * Example: 10:52 + */ + NumberSymbol[NumberSymbol["TimeSeparator"] = 11] = "TimeSeparator"; + /** + * Decimal separator for currency values (fallback to `Decimal`). + * Example: $2,345.67 + */ + NumberSymbol[NumberSymbol["CurrencyDecimal"] = 12] = "CurrencyDecimal"; + /** + * Group separator for currency values (fallback to `Group`). + * Example: $2,345.67 + */ + NumberSymbol[NumberSymbol["CurrencyGroup"] = 13] = "CurrencyGroup"; + })(exports.NumberSymbol || (exports.NumberSymbol = {})); + /** + * The value for each day of the week, based on the `en-US` locale + * + * @publicApi + */ + exports.WeekDay = void 0; + (function (WeekDay) { + WeekDay[WeekDay["Sunday"] = 0] = "Sunday"; + WeekDay[WeekDay["Monday"] = 1] = "Monday"; + WeekDay[WeekDay["Tuesday"] = 2] = "Tuesday"; + WeekDay[WeekDay["Wednesday"] = 3] = "Wednesday"; + WeekDay[WeekDay["Thursday"] = 4] = "Thursday"; + WeekDay[WeekDay["Friday"] = 5] = "Friday"; + WeekDay[WeekDay["Saturday"] = 6] = "Saturday"; + })(exports.WeekDay || (exports.WeekDay = {})); + /** + * Retrieves the locale ID from the currently loaded locale. + * The loaded locale could be, for example, a global one rather than a regional one. + * @param locale A locale code, such as `fr-FR`. + * @returns The locale code. For example, `fr`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleId(locale) { + return i0["ɵfindLocaleData"](locale)[i0["ɵLocaleDataIndex"].LocaleId]; + } + /** + * Retrieves day period strings for the given locale. + * * @param locale A locale code for the locale format rules to use. + * @param formStyle The required grammatical form. * @param width The required character width. - - * @returns An array of localized era strings. - * For example, `[AD, BC]` for `en-US`. + * @returns An array of localized period strings. For example, `[AM, PM]` for `en-US`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ - function getLocaleEraNames(locale, width) { - const data = i0['ɵfindLocaleData'](locale); - const erasData = data[i0['ɵLocaleDataIndex'].Eras]; - return getLastDefinedValue(erasData, width); - } - /** - * Retrieves the first day of the week for the given locale. - * - * @param locale A locale code for the locale format rules to use. - * @returns A day index number, using the 0-based week-day index for `en-US` - * (Sunday = 0, Monday = 1, ...). - * For example, for `fr-FR`, returns 1 to indicate that the first day is Monday. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleFirstDayOfWeek(locale) { - const data = i0['ɵfindLocaleData'](locale); - return data[i0['ɵLocaleDataIndex'].FirstDayOfWeek]; - } - /** - * Range of week days that are considered the week-end for the given locale. - * - * @param locale A locale code for the locale format rules to use. - * @returns The range of day values, `[startDay, endDay]`. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleWeekEndRange(locale) { - const data = i0['ɵfindLocaleData'](locale); - return data[i0['ɵLocaleDataIndex'].WeekendRange]; - } - /** - * Retrieves a localized date-value formatting string. - * - * @param locale A locale code for the locale format rules to use. - * @param width The format type. - * @returns The localized formatting string. - * @see `FormatWidth` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleDateFormat(locale, width) { - const data = i0['ɵfindLocaleData'](locale); - return getLastDefinedValue(data[i0['ɵLocaleDataIndex'].DateFormat], width); - } - /** - * Retrieves a localized time-value formatting string. + function getLocaleDayPeriods(locale, formStyle, width) { + const data = i0["ɵfindLocaleData"](locale); + const amPmData = [ + data[i0["ɵLocaleDataIndex"].DayPeriodsFormat], data[i0["ɵLocaleDataIndex"].DayPeriodsStandalone] + ]; + const amPm = getLastDefinedValue(amPmData, formStyle); + return getLastDefinedValue(amPm, width); + } + /** + * Retrieves days of the week for the given locale, using the Gregorian calendar. * * @param locale A locale code for the locale format rules to use. - * @param width The format type. - * @returns The localized formatting string. - * @see `FormatWidth` + * @param formStyle The required grammatical form. + * @param width The required character width. + * @returns An array of localized name strings. + * For example,`[Sunday, Monday, ... Saturday]` for `en-US`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - + * * @publicApi */ - function getLocaleTimeFormat(locale, width) { - const data = i0['ɵfindLocaleData'](locale); - return getLastDefinedValue(data[i0['ɵLocaleDataIndex'].TimeFormat], width); - } - /** - * Retrieves a localized date-time formatting string. - * - * @param locale A locale code for the locale format rules to use. - * @param width The format type. - * @returns The localized formatting string. - * @see `FormatWidth` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleDateTimeFormat(locale, width) { - const data = i0['ɵfindLocaleData'](locale); - const dateTimeFormatData = data[i0['ɵLocaleDataIndex'].DateTimeFormat]; - return getLastDefinedValue(dateTimeFormatData, width); - } - /** - * Retrieves a localized number symbol that can be used to replace placeholders in number formats. - * @param locale The locale code. - * @param symbol The symbol to localize. - * @returns The character for the localized symbol. - * @see `NumberSymbol` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleNumberSymbol(locale, symbol) { - const data = i0['ɵfindLocaleData'](locale); - const res = data[i0['ɵLocaleDataIndex'].NumberSymbols][symbol]; - if (typeof res === 'undefined') { - if (symbol === exports.NumberSymbol.CurrencyDecimal) { - return data[i0['ɵLocaleDataIndex'].NumberSymbols][ - exports.NumberSymbol.Decimal - ]; - } else if (symbol === exports.NumberSymbol.CurrencyGroup) { - return data[i0['ɵLocaleDataIndex'].NumberSymbols][ - exports.NumberSymbol.Group + function getLocaleDayNames(locale, formStyle, width) { + const data = i0["ɵfindLocaleData"](locale); + const daysData = [data[i0["ɵLocaleDataIndex"].DaysFormat], data[i0["ɵLocaleDataIndex"].DaysStandalone]]; + const days = getLastDefinedValue(daysData, formStyle); + return getLastDefinedValue(days, width); + } + /** + * Retrieves months of the year for the given locale, using the Gregorian calendar. + * + * @param locale A locale code for the locale format rules to use. + * @param formStyle The required grammatical form. + * @param width The required character width. + * @returns An array of localized name strings. + * For example, `[January, February, ...]` for `en-US`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleMonthNames(locale, formStyle, width) { + const data = i0["ɵfindLocaleData"](locale); + const monthsData = [data[i0["ɵLocaleDataIndex"].MonthsFormat], data[i0["ɵLocaleDataIndex"].MonthsStandalone]]; + const months = getLastDefinedValue(monthsData, formStyle); + return getLastDefinedValue(months, width); + } + /** + * Retrieves Gregorian-calendar eras for the given locale. + * @param locale A locale code for the locale format rules to use. + * @param width The required character width. + + * @returns An array of localized era strings. + * For example, `[AD, BC]` for `en-US`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleEraNames(locale, width) { + const data = i0["ɵfindLocaleData"](locale); + const erasData = data[i0["ɵLocaleDataIndex"].Eras]; + return getLastDefinedValue(erasData, width); + } + /** + * Retrieves the first day of the week for the given locale. + * + * @param locale A locale code for the locale format rules to use. + * @returns A day index number, using the 0-based week-day index for `en-US` + * (Sunday = 0, Monday = 1, ...). + * For example, for `fr-FR`, returns 1 to indicate that the first day is Monday. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleFirstDayOfWeek(locale) { + const data = i0["ɵfindLocaleData"](locale); + return data[i0["ɵLocaleDataIndex"].FirstDayOfWeek]; + } + /** + * Range of week days that are considered the week-end for the given locale. + * + * @param locale A locale code for the locale format rules to use. + * @returns The range of day values, `[startDay, endDay]`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleWeekEndRange(locale) { + const data = i0["ɵfindLocaleData"](locale); + return data[i0["ɵLocaleDataIndex"].WeekendRange]; + } + /** + * Retrieves a localized date-value formatting string. + * + * @param locale A locale code for the locale format rules to use. + * @param width The format type. + * @returns The localized formatting string. + * @see `FormatWidth` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleDateFormat(locale, width) { + const data = i0["ɵfindLocaleData"](locale); + return getLastDefinedValue(data[i0["ɵLocaleDataIndex"].DateFormat], width); + } + /** + * Retrieves a localized time-value formatting string. + * + * @param locale A locale code for the locale format rules to use. + * @param width The format type. + * @returns The localized formatting string. + * @see `FormatWidth` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + + * @publicApi + */ + function getLocaleTimeFormat(locale, width) { + const data = i0["ɵfindLocaleData"](locale); + return getLastDefinedValue(data[i0["ɵLocaleDataIndex"].TimeFormat], width); + } + /** + * Retrieves a localized date-time formatting string. + * + * @param locale A locale code for the locale format rules to use. + * @param width The format type. + * @returns The localized formatting string. + * @see `FormatWidth` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleDateTimeFormat(locale, width) { + const data = i0["ɵfindLocaleData"](locale); + const dateTimeFormatData = data[i0["ɵLocaleDataIndex"].DateTimeFormat]; + return getLastDefinedValue(dateTimeFormatData, width); + } + /** + * Retrieves a localized number symbol that can be used to replace placeholders in number formats. + * @param locale The locale code. + * @param symbol The symbol to localize. + * @returns The character for the localized symbol. + * @see `NumberSymbol` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleNumberSymbol(locale, symbol) { + const data = i0["ɵfindLocaleData"](locale); + const res = data[i0["ɵLocaleDataIndex"].NumberSymbols][symbol]; + if (typeof res === 'undefined') { + if (symbol === exports.NumberSymbol.CurrencyDecimal) { + return data[i0["ɵLocaleDataIndex"].NumberSymbols][exports.NumberSymbol.Decimal]; + } + else if (symbol === exports.NumberSymbol.CurrencyGroup) { + return data[i0["ɵLocaleDataIndex"].NumberSymbols][exports.NumberSymbol.Group]; + } + } + return res; + } + /** + * Retrieves a number format for a given locale. + * + * Numbers are formatted using patterns, like `#,###.00`. For example, the pattern `#,###.00` + * when used to format the number 12345.678 could result in "12'345,678". That would happen if the + * grouping separator for your language is an apostrophe, and the decimal separator is a comma. + * + * Important: The characters `.` `,` `0` `#` (and others below) are special placeholders + * that stand for the decimal separator, and so on, and are NOT real characters. + * You must NOT "translate" the placeholders. For example, don't change `.` to `,` even though in + * your language the decimal point is written with a comma. The symbols should be replaced by the + * local equivalents, using the appropriate `NumberSymbol` for your language. + * + * Here are the special characters used in number patterns: + * + * | Symbol | Meaning | + * |--------|---------| + * | . | Replaced automatically by the character used for the decimal point. | + * | , | Replaced by the "grouping" (thousands) separator. | + * | 0 | Replaced by a digit (or zero if there aren't enough digits). | + * | # | Replaced by a digit (or nothing if there aren't enough). | + * | ¤ | Replaced by a currency symbol, such as $ or USD. | + * | % | Marks a percent format. The % symbol may change position, but must be retained. | + * | E | Marks a scientific format. The E symbol may change position, but must be retained. | + * | ' | Special characters used as literal characters are quoted with ASCII single quotes. | + * + * @param locale A locale code for the locale format rules to use. + * @param type The type of numeric value to be formatted (such as `Decimal` or `Currency`.) + * @returns The localized format string. + * @see `NumberFormatStyle` + * @see [CLDR website](http://cldr.unicode.org/translation/number-patterns) + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleNumberFormat(locale, type) { + const data = i0["ɵfindLocaleData"](locale); + return data[i0["ɵLocaleDataIndex"].NumberFormats][type]; + } + /** + * Retrieves the symbol used to represent the currency for the main country + * corresponding to a given locale. For example, '$' for `en-US`. + * + * @param locale A locale code for the locale format rules to use. + * @returns The localized symbol character, + * or `null` if the main country cannot be determined. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleCurrencySymbol(locale) { + const data = i0["ɵfindLocaleData"](locale); + return data[i0["ɵLocaleDataIndex"].CurrencySymbol] || null; + } + /** + * Retrieves the name of the currency for the main country corresponding + * to a given locale. For example, 'US Dollar' for `en-US`. + * @param locale A locale code for the locale format rules to use. + * @returns The currency name, + * or `null` if the main country cannot be determined. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleCurrencyName(locale) { + const data = i0["ɵfindLocaleData"](locale); + return data[i0["ɵLocaleDataIndex"].CurrencyName] || null; + } + /** + * Retrieves the default currency code for the given locale. + * + * The default is defined as the first currency which is still in use. + * + * @param locale The code of the locale whose currency code we want. + * @returns The code of the default currency for the given locale. + * + * @publicApi + */ + function getLocaleCurrencyCode(locale) { + return i0["ɵgetLocaleCurrencyCode"](locale); + } + /** + * Retrieves the currency values for a given locale. + * @param locale A locale code for the locale format rules to use. + * @returns The currency values. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + */ + function getLocaleCurrencies(locale) { + const data = i0["ɵfindLocaleData"](locale); + return data[i0["ɵLocaleDataIndex"].Currencies]; + } + /** + * @alias core/ɵgetLocalePluralCase + * @publicApi + */ + const getLocalePluralCase = i0["ɵgetLocalePluralCase"]; + function checkFullData(data) { + if (!data[i0["ɵLocaleDataIndex"].ExtraData]) { + throw new Error(`Missing extra locale data for the locale "${data[i0["ɵLocaleDataIndex"] + .LocaleId]}". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.`); + } + } + /** + * Retrieves locale-specific rules used to determine which day period to use + * when more than one period is defined for a locale. + * + * There is a rule for each defined day period. The + * first rule is applied to the first day period and so on. + * Fall back to AM/PM when no rules are available. + * + * A rule can specify a period as time range, or as a single time value. + * + * This functionality is only available when you have loaded the full locale data. + * See the ["I18n guide"](guide/i18n-common-format-data-locale). + * + * @param locale A locale code for the locale format rules to use. + * @returns The rules for the locale, a single time value or array of *from-time, to-time*, + * or null if no periods are available. + * + * @see `getLocaleExtraDayPeriods()` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleExtraDayPeriodRules(locale) { + const data = i0["ɵfindLocaleData"](locale); + checkFullData(data); + const rules = data[i0["ɵLocaleDataIndex"].ExtraData][2 /* ɵExtraLocaleDataIndex.ExtraDayPeriodsRules */] || []; + return rules.map((rule) => { + if (typeof rule === 'string') { + return extractTime(rule); + } + return [extractTime(rule[0]), extractTime(rule[1])]; + }); + } + /** + * Retrieves locale-specific day periods, which indicate roughly how a day is broken up + * in different languages. + * For example, for `en-US`, periods are morning, noon, afternoon, evening, and midnight. + * + * This functionality is only available when you have loaded the full locale data. + * See the ["I18n guide"](guide/i18n-common-format-data-locale). + * + * @param locale A locale code for the locale format rules to use. + * @param formStyle The required grammatical form. + * @param width The required character width. + * @returns The translated day-period strings. + * @see `getLocaleExtraDayPeriodRules()` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLocaleExtraDayPeriods(locale, formStyle, width) { + const data = i0["ɵfindLocaleData"](locale); + checkFullData(data); + const dayPeriodsData = [ + data[i0["ɵLocaleDataIndex"].ExtraData][0 /* ɵExtraLocaleDataIndex.ExtraDayPeriodFormats */], + data[i0["ɵLocaleDataIndex"].ExtraData][1 /* ɵExtraLocaleDataIndex.ExtraDayPeriodStandalone */] ]; - } - } - return res; - } - /** - * Retrieves a number format for a given locale. - * - * Numbers are formatted using patterns, like `#,###.00`. For example, the pattern `#,###.00` - * when used to format the number 12345.678 could result in "12'345,678". That would happen if the - * grouping separator for your language is an apostrophe, and the decimal separator is a comma. - * - * Important: The characters `.` `,` `0` `#` (and others below) are special placeholders - * that stand for the decimal separator, and so on, and are NOT real characters. - * You must NOT "translate" the placeholders. For example, don't change `.` to `,` even though in - * your language the decimal point is written with a comma. The symbols should be replaced by the - * local equivalents, using the appropriate `NumberSymbol` for your language. - * - * Here are the special characters used in number patterns: - * - * | Symbol | Meaning | - * |--------|---------| - * | . | Replaced automatically by the character used for the decimal point. | - * | , | Replaced by the "grouping" (thousands) separator. | - * | 0 | Replaced by a digit (or zero if there aren't enough digits). | - * | # | Replaced by a digit (or nothing if there aren't enough). | - * | ¤ | Replaced by a currency symbol, such as $ or USD. | - * | % | Marks a percent format. The % symbol may change position, but must be retained. | - * | E | Marks a scientific format. The E symbol may change position, but must be retained. | - * | ' | Special characters used as literal characters are quoted with ASCII single quotes. | - * - * @param locale A locale code for the locale format rules to use. - * @param type The type of numeric value to be formatted (such as `Decimal` or `Currency`.) - * @returns The localized format string. - * @see `NumberFormatStyle` - * @see [CLDR website](http://cldr.unicode.org/translation/number-patterns) - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleNumberFormat(locale, type) { - const data = i0['ɵfindLocaleData'](locale); - return data[i0['ɵLocaleDataIndex'].NumberFormats][type]; - } - /** - * Retrieves the symbol used to represent the currency for the main country - * corresponding to a given locale. For example, '$' for `en-US`. - * - * @param locale A locale code for the locale format rules to use. - * @returns The localized symbol character, - * or `null` if the main country cannot be determined. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleCurrencySymbol(locale) { - const data = i0['ɵfindLocaleData'](locale); - return data[i0['ɵLocaleDataIndex'].CurrencySymbol] || null; - } - /** - * Retrieves the name of the currency for the main country corresponding - * to a given locale. For example, 'US Dollar' for `en-US`. - * @param locale A locale code for the locale format rules to use. - * @returns The currency name, - * or `null` if the main country cannot be determined. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleCurrencyName(locale) { - const data = i0['ɵfindLocaleData'](locale); - return data[i0['ɵLocaleDataIndex'].CurrencyName] || null; - } - /** - * Retrieves the default currency code for the given locale. - * - * The default is defined as the first currency which is still in use. - * - * @param locale The code of the locale whose currency code we want. - * @returns The code of the default currency for the given locale. - * - * @publicApi - */ - function getLocaleCurrencyCode(locale) { - return i0['ɵgetLocaleCurrencyCode'](locale); - } - /** - * Retrieves the currency values for a given locale. - * @param locale A locale code for the locale format rules to use. - * @returns The currency values. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - */ - function getLocaleCurrencies(locale) { - const data = i0['ɵfindLocaleData'](locale); - return data[i0['ɵLocaleDataIndex'].Currencies]; - } - /** - * @alias core/ɵgetLocalePluralCase - * @publicApi - */ - const getLocalePluralCase = i0['ɵgetLocalePluralCase']; - function checkFullData(data) { - if (!data[i0['ɵLocaleDataIndex'].ExtraData]) { - throw new Error( - `Missing extra locale data for the locale "${ - data[i0['ɵLocaleDataIndex'].LocaleId] - }". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.` - ); - } - } - /** - * Retrieves locale-specific rules used to determine which day period to use - * when more than one period is defined for a locale. - * - * There is a rule for each defined day period. The - * first rule is applied to the first day period and so on. - * Fall back to AM/PM when no rules are available. - * - * A rule can specify a period as time range, or as a single time value. - * - * This functionality is only available when you have loaded the full locale data. - * See the ["I18n guide"](guide/i18n-common-format-data-locale). - * - * @param locale A locale code for the locale format rules to use. - * @returns The rules for the locale, a single time value or array of *from-time, to-time*, - * or null if no periods are available. - * - * @see `getLocaleExtraDayPeriods()` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleExtraDayPeriodRules(locale) { - const data = i0['ɵfindLocaleData'](locale); - checkFullData(data); - const rules = - data[ - i0['ɵLocaleDataIndex'].ExtraData - ][2 /* ɵExtraLocaleDataIndex.ExtraDayPeriodsRules */] || []; - return rules.map((rule) => { - if (typeof rule === 'string') { - return extractTime(rule); - } - return [extractTime(rule[0]), extractTime(rule[1])]; + const dayPeriods = getLastDefinedValue(dayPeriodsData, formStyle) || []; + return getLastDefinedValue(dayPeriods, width) || []; + } + /** + * Retrieves the writing direction of a specified locale + * @param locale A locale code for the locale format rules to use. + * @publicApi + * @returns 'rtl' or 'ltr' + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + */ + function getLocaleDirection(locale) { + const data = i0["ɵfindLocaleData"](locale); + return data[i0["ɵLocaleDataIndex"].Directionality]; + } + /** + * Retrieves the first value that is defined in an array, going backwards from an index position. + * + * To avoid repeating the same data (as when the "format" and "standalone" forms are the same) + * add the first value to the locale data arrays, and add other values only if they are different. + * + * @param data The data array to retrieve from. + * @param index A 0-based index into the array to start from. + * @returns The value immediately before the given index position. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getLastDefinedValue(data, index) { + for (let i = index; i > -1; i--) { + if (typeof data[i] !== 'undefined') { + return data[i]; + } + } + throw new Error('Locale data API: locale data undefined'); + } + /** + * Extracts the hours and minutes from a string like "15:45" + */ + function extractTime(time) { + const [h, m] = time.split(':'); + return { hours: +h, minutes: +m }; + } + /** + * Retrieves the currency symbol for a given currency code. + * + * For example, for the default `en-US` locale, the code `USD` can + * be represented by the narrow symbol `$` or the wide symbol `US$`. + * + * @param code The currency code. + * @param format The format, `wide` or `narrow`. + * @param locale A locale code for the locale format rules to use. + * + * @returns The symbol, or the currency code if no symbol is available. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getCurrencySymbol(code, format, locale = 'en') { + const currency = getLocaleCurrencies(locale)[code] || CURRENCIES_EN[code] || []; + const symbolNarrow = currency[1 /* ɵCurrencyIndex.SymbolNarrow */]; + if (format === 'narrow' && typeof symbolNarrow === 'string') { + return symbolNarrow; + } + return currency[0 /* ɵCurrencyIndex.Symbol */] || code; + } + // Most currencies have cents, that's why the default is 2 + const DEFAULT_NB_OF_CURRENCY_DIGITS = 2; + /** + * Reports the number of decimal digits for a given currency. + * The value depends upon the presence of cents in that particular currency. + * + * @param code The currency code. + * @returns The number of decimal digits, typically 0 or 2. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function getNumberOfCurrencyDigits(code) { + let digits; + const currency = CURRENCIES_EN[code]; + if (currency) { + digits = currency[2 /* ɵCurrencyIndex.NbOfDigits */]; + } + return typeof digits === 'number' ? digits : DEFAULT_NB_OF_CURRENCY_DIGITS; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + const ISO8601_DATE_REGEX = /^(\d{4,})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; + // 1 2 3 4 5 6 7 8 9 10 11 + const NAMED_FORMATS = {}; + const DATE_FORMATS_SPLIT = /((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; + var ZoneWidth; + (function (ZoneWidth) { + ZoneWidth[ZoneWidth["Short"] = 0] = "Short"; + ZoneWidth[ZoneWidth["ShortGMT"] = 1] = "ShortGMT"; + ZoneWidth[ZoneWidth["Long"] = 2] = "Long"; + ZoneWidth[ZoneWidth["Extended"] = 3] = "Extended"; + })(ZoneWidth || (ZoneWidth = {})); + var DateType; + (function (DateType) { + DateType[DateType["FullYear"] = 0] = "FullYear"; + DateType[DateType["Month"] = 1] = "Month"; + DateType[DateType["Date"] = 2] = "Date"; + DateType[DateType["Hours"] = 3] = "Hours"; + DateType[DateType["Minutes"] = 4] = "Minutes"; + DateType[DateType["Seconds"] = 5] = "Seconds"; + DateType[DateType["FractionalSeconds"] = 6] = "FractionalSeconds"; + DateType[DateType["Day"] = 7] = "Day"; + })(DateType || (DateType = {})); + var TranslationType; + (function (TranslationType) { + TranslationType[TranslationType["DayPeriods"] = 0] = "DayPeriods"; + TranslationType[TranslationType["Days"] = 1] = "Days"; + TranslationType[TranslationType["Months"] = 2] = "Months"; + TranslationType[TranslationType["Eras"] = 3] = "Eras"; + })(TranslationType || (TranslationType = {})); + /** + * @ngModule CommonModule + * @description + * + * Formats a date according to locale rules. + * + * @param value The date to format, as a Date, or a number (milliseconds since UTC epoch) + * or an [ISO date-time string](https://www.w3.org/TR/NOTE-datetime). + * @param format The date-time components to include. See `DatePipe` for details. + * @param locale A locale code for the locale format rules to use. + * @param timezone The time zone. A time zone offset from GMT (such as `'+0430'`), + * or a standard UTC/GMT or continental US time zone abbreviation. + * If not specified, uses host system settings. + * + * @returns The formatted date string. + * + * @see `DatePipe` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function formatDate(value, format, locale, timezone) { + let date = toDate(value); + const namedFormat = getNamedFormat(locale, format); + format = namedFormat || format; + let parts = []; + let match; + while (format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = parts.concat(match.slice(1)); + const part = parts.pop(); + if (!part) { + break; + } + format = part; + } + else { + parts.push(format); + break; + } + } + let dateTimezoneOffset = date.getTimezoneOffset(); + if (timezone) { + dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + date = convertTimezoneToLocal(date, timezone, true); + } + let text = ''; + parts.forEach(value => { + const dateFormatter = getDateFormatter(value); + text += dateFormatter ? dateFormatter(date, locale, dateTimezoneOffset) : + value === '\'\'' ? '\'' : + value.replace(/(^'|'$)/g, '').replace(/''/g, '\''); + }); + return text; + } + /** + * Create a new Date object with the given date value, and the time set to midnight. + * + * We cannot use `new Date(year, month, date)` because it maps years between 0 and 99 to 1900-1999. + * See: https://github.com/angular/angular/issues/40377 + * + * Note that this function returns a Date object whose time is midnight in the current locale's + * timezone. In the future we might want to change this to be midnight in UTC, but this would be a + * considerable breaking change. + */ + function createDate(year, month, date) { + // The `newDate` is set to midnight (UTC) on January 1st 1970. + // - In PST this will be December 31st 1969 at 4pm. + // - In GMT this will be January 1st 1970 at 1am. + // Note that they even have different years, dates and months! + const newDate = new Date(0); + // `setFullYear()` allows years like 0001 to be set correctly. This function does not + // change the internal time of the date. + // Consider calling `setFullYear(2019, 8, 20)` (September 20, 2019). + // - In PST this will now be September 20, 2019 at 4pm + // - In GMT this will now be September 20, 2019 at 1am + newDate.setFullYear(year, month, date); + // We want the final date to be at local midnight, so we reset the time. + // - In PST this will now be September 20, 2019 at 12am + // - In GMT this will now be September 20, 2019 at 12am + newDate.setHours(0, 0, 0); + return newDate; + } + function getNamedFormat(locale, format) { + const localeId = getLocaleId(locale); + NAMED_FORMATS[localeId] = NAMED_FORMATS[localeId] || {}; + if (NAMED_FORMATS[localeId][format]) { + return NAMED_FORMATS[localeId][format]; + } + let formatValue = ''; + switch (format) { + case 'shortDate': + formatValue = getLocaleDateFormat(locale, exports.FormatWidth.Short); + break; + case 'mediumDate': + formatValue = getLocaleDateFormat(locale, exports.FormatWidth.Medium); + break; + case 'longDate': + formatValue = getLocaleDateFormat(locale, exports.FormatWidth.Long); + break; + case 'fullDate': + formatValue = getLocaleDateFormat(locale, exports.FormatWidth.Full); + break; + case 'shortTime': + formatValue = getLocaleTimeFormat(locale, exports.FormatWidth.Short); + break; + case 'mediumTime': + formatValue = getLocaleTimeFormat(locale, exports.FormatWidth.Medium); + break; + case 'longTime': + formatValue = getLocaleTimeFormat(locale, exports.FormatWidth.Long); + break; + case 'fullTime': + formatValue = getLocaleTimeFormat(locale, exports.FormatWidth.Full); + break; + case 'short': + const shortTime = getNamedFormat(locale, 'shortTime'); + const shortDate = getNamedFormat(locale, 'shortDate'); + formatValue = formatDateTime(getLocaleDateTimeFormat(locale, exports.FormatWidth.Short), [shortTime, shortDate]); + break; + case 'medium': + const mediumTime = getNamedFormat(locale, 'mediumTime'); + const mediumDate = getNamedFormat(locale, 'mediumDate'); + formatValue = formatDateTime(getLocaleDateTimeFormat(locale, exports.FormatWidth.Medium), [mediumTime, mediumDate]); + break; + case 'long': + const longTime = getNamedFormat(locale, 'longTime'); + const longDate = getNamedFormat(locale, 'longDate'); + formatValue = + formatDateTime(getLocaleDateTimeFormat(locale, exports.FormatWidth.Long), [longTime, longDate]); + break; + case 'full': + const fullTime = getNamedFormat(locale, 'fullTime'); + const fullDate = getNamedFormat(locale, 'fullDate'); + formatValue = + formatDateTime(getLocaleDateTimeFormat(locale, exports.FormatWidth.Full), [fullTime, fullDate]); + break; + } + if (formatValue) { + NAMED_FORMATS[localeId][format] = formatValue; + } + return formatValue; + } + function formatDateTime(str, opt_values) { + if (opt_values) { + str = str.replace(/\{([^}]+)}/g, function (match, key) { + return (opt_values != null && key in opt_values) ? opt_values[key] : match; + }); + } + return str; + } + function padNumber(num, digits, minusSign = '-', trim, negWrap) { + let neg = ''; + if (num < 0 || (negWrap && num <= 0)) { + if (negWrap) { + num = -num + 1; + } + else { + num = -num; + neg = minusSign; + } + } + let strNum = String(num); + while (strNum.length < digits) { + strNum = '0' + strNum; + } + if (trim) { + strNum = strNum.slice(strNum.length - digits); + } + return neg + strNum; + } + function formatFractionalSeconds(milliseconds, digits) { + const strMs = padNumber(milliseconds, 3); + return strMs.substring(0, digits); + } + /** + * Returns a date formatter that transforms a date into its locale digit representation + */ + function dateGetter(name, size, offset = 0, trim = false, negWrap = false) { + return function (date, locale) { + let part = getDatePart(name, date); + if (offset > 0 || part > -offset) { + part += offset; + } + if (name === DateType.Hours) { + if (part === 0 && offset === -12) { + part = 12; + } + } + else if (name === DateType.FractionalSeconds) { + return formatFractionalSeconds(part, size); + } + const localeMinus = getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign); + return padNumber(part, size, localeMinus, trim, negWrap); + }; + } + function getDatePart(part, date) { + switch (part) { + case DateType.FullYear: + return date.getFullYear(); + case DateType.Month: + return date.getMonth(); + case DateType.Date: + return date.getDate(); + case DateType.Hours: + return date.getHours(); + case DateType.Minutes: + return date.getMinutes(); + case DateType.Seconds: + return date.getSeconds(); + case DateType.FractionalSeconds: + return date.getMilliseconds(); + case DateType.Day: + return date.getDay(); + default: + throw new Error(`Unknown DateType value "${part}".`); + } + } + /** + * Returns a date formatter that transforms a date into its locale string representation + */ + function dateStrGetter(name, width, form = exports.FormStyle.Format, extended = false) { + return function (date, locale) { + return getDateTranslation(date, locale, name, width, form, extended); + }; + } + /** + * Returns the locale translation of a date for a given form, type and width + */ + function getDateTranslation(date, locale, name, width, form, extended) { + switch (name) { + case TranslationType.Months: + return getLocaleMonthNames(locale, form, width)[date.getMonth()]; + case TranslationType.Days: + return getLocaleDayNames(locale, form, width)[date.getDay()]; + case TranslationType.DayPeriods: + const currentHours = date.getHours(); + const currentMinutes = date.getMinutes(); + if (extended) { + const rules = getLocaleExtraDayPeriodRules(locale); + const dayPeriods = getLocaleExtraDayPeriods(locale, form, width); + const index = rules.findIndex(rule => { + if (Array.isArray(rule)) { + // morning, afternoon, evening, night + const [from, to] = rule; + const afterFrom = currentHours >= from.hours && currentMinutes >= from.minutes; + const beforeTo = (currentHours < to.hours || + (currentHours === to.hours && currentMinutes < to.minutes)); + // We must account for normal rules that span a period during the day (e.g. 6am-9am) + // where `from` is less (earlier) than `to`. But also rules that span midnight (e.g. + // 10pm - 5am) where `from` is greater (later!) than `to`. + // + // In the first case the current time must be BOTH after `from` AND before `to` + // (e.g. 8am is after 6am AND before 10am). + // + // In the second case the current time must be EITHER after `from` OR before `to` + // (e.g. 4am is before 5am but not after 10pm; and 11pm is not before 5am but it is + // after 10pm). + if (from.hours < to.hours) { + if (afterFrom && beforeTo) { + return true; + } + } + else if (afterFrom || beforeTo) { + return true; + } + } + else { // noon or midnight + if (rule.hours === currentHours && rule.minutes === currentMinutes) { + return true; + } + } + return false; + }); + if (index !== -1) { + return dayPeriods[index]; + } + } + // if no rules for the day periods, we use am/pm by default + return getLocaleDayPeriods(locale, form, width)[currentHours < 12 ? 0 : 1]; + case TranslationType.Eras: + return getLocaleEraNames(locale, width)[date.getFullYear() <= 0 ? 0 : 1]; + default: + // This default case is not needed by TypeScript compiler, as the switch is exhaustive. + // However Closure Compiler does not understand that and reports an error in typed mode. + // The `throw new Error` below works around the problem, and the unexpected: never variable + // makes sure tsc still checks this code is unreachable. + const unexpected = name; + throw new Error(`unexpected translation type ${unexpected}`); + } + } + /** + * Returns a date formatter that transforms a date and an offset into a timezone with ISO8601 or + * GMT format depending on the width (eg: short = +0430, short:GMT = GMT+4, long = GMT+04:30, + * extended = +04:30) + */ + function timeZoneGetter(width) { + return function (date, locale, offset) { + const zone = -1 * offset; + const minusSign = getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign); + const hours = zone > 0 ? Math.floor(zone / 60) : Math.ceil(zone / 60); + switch (width) { + case ZoneWidth.Short: + return ((zone >= 0) ? '+' : '') + padNumber(hours, 2, minusSign) + + padNumber(Math.abs(zone % 60), 2, minusSign); + case ZoneWidth.ShortGMT: + return 'GMT' + ((zone >= 0) ? '+' : '') + padNumber(hours, 1, minusSign); + case ZoneWidth.Long: + return 'GMT' + ((zone >= 0) ? '+' : '') + padNumber(hours, 2, minusSign) + ':' + + padNumber(Math.abs(zone % 60), 2, minusSign); + case ZoneWidth.Extended: + if (offset === 0) { + return 'Z'; + } + else { + return ((zone >= 0) ? '+' : '') + padNumber(hours, 2, minusSign) + ':' + + padNumber(Math.abs(zone % 60), 2, minusSign); + } + default: + throw new Error(`Unknown zone width "${width}"`); + } + }; + } + const JANUARY = 0; + const THURSDAY = 4; + function getFirstThursdayOfYear(year) { + const firstDayOfYear = createDate(year, JANUARY, 1).getDay(); + return createDate(year, 0, 1 + ((firstDayOfYear <= THURSDAY) ? THURSDAY : THURSDAY + 7) - firstDayOfYear); + } + function getThursdayThisWeek(datetime) { + return createDate(datetime.getFullYear(), datetime.getMonth(), datetime.getDate() + (THURSDAY - datetime.getDay())); + } + function weekGetter(size, monthBased = false) { + return function (date, locale) { + let result; + if (monthBased) { + const nbDaysBefore1stDayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1).getDay() - 1; + const today = date.getDate(); + result = 1 + Math.floor((today + nbDaysBefore1stDayOfMonth) / 7); + } + else { + const thisThurs = getThursdayThisWeek(date); + // Some days of a year are part of next year according to ISO 8601. + // Compute the firstThurs from the year of this week's Thursday + const firstThurs = getFirstThursdayOfYear(thisThurs.getFullYear()); + const diff = thisThurs.getTime() - firstThurs.getTime(); + result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week + } + return padNumber(result, size, getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign)); + }; + } + /** + * Returns a date formatter that provides the week-numbering year for the input date. + */ + function weekNumberingYearGetter(size, trim = false) { + return function (date, locale) { + const thisThurs = getThursdayThisWeek(date); + const weekNumberingYear = thisThurs.getFullYear(); + return padNumber(weekNumberingYear, size, getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign), trim); + }; + } + const DATE_FORMATS = {}; + // Based on CLDR formats: + // See complete list: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table + // See also explanations: http://cldr.unicode.org/translation/date-time + // TODO(ocombe): support all missing cldr formats: U, Q, D, F, e, j, J, C, A, v, V, X, x + function getDateFormatter(format) { + if (DATE_FORMATS[format]) { + return DATE_FORMATS[format]; + } + let formatter; + switch (format) { + // Era name (AD/BC) + case 'G': + case 'GG': + case 'GGG': + formatter = dateStrGetter(TranslationType.Eras, exports.TranslationWidth.Abbreviated); + break; + case 'GGGG': + formatter = dateStrGetter(TranslationType.Eras, exports.TranslationWidth.Wide); + break; + case 'GGGGG': + formatter = dateStrGetter(TranslationType.Eras, exports.TranslationWidth.Narrow); + break; + // 1 digit representation of the year, e.g. (AD 1 => 1, AD 199 => 199) + case 'y': + formatter = dateGetter(DateType.FullYear, 1, 0, false, true); + break; + // 2 digit representation of the year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + case 'yy': + formatter = dateGetter(DateType.FullYear, 2, 0, true, true); + break; + // 3 digit representation of the year, padded (000-999). (e.g. AD 2001 => 01, AD 2010 => 10) + case 'yyy': + formatter = dateGetter(DateType.FullYear, 3, 0, false, true); + break; + // 4 digit representation of the year (e.g. AD 1 => 0001, AD 2010 => 2010) + case 'yyyy': + formatter = dateGetter(DateType.FullYear, 4, 0, false, true); + break; + // 1 digit representation of the week-numbering year, e.g. (AD 1 => 1, AD 199 => 199) + case 'Y': + formatter = weekNumberingYearGetter(1); + break; + // 2 digit representation of the week-numbering year, padded (00-99). (e.g. AD 2001 => 01, AD + // 2010 => 10) + case 'YY': + formatter = weekNumberingYearGetter(2, true); + break; + // 3 digit representation of the week-numbering year, padded (000-999). (e.g. AD 1 => 001, AD + // 2010 => 2010) + case 'YYY': + formatter = weekNumberingYearGetter(3); + break; + // 4 digit representation of the week-numbering year (e.g. AD 1 => 0001, AD 2010 => 2010) + case 'YYYY': + formatter = weekNumberingYearGetter(4); + break; + // Month of the year (1-12), numeric + case 'M': + case 'L': + formatter = dateGetter(DateType.Month, 1, 1); + break; + case 'MM': + case 'LL': + formatter = dateGetter(DateType.Month, 2, 1); + break; + // Month of the year (January, ...), string, format + case 'MMM': + formatter = dateStrGetter(TranslationType.Months, exports.TranslationWidth.Abbreviated); + break; + case 'MMMM': + formatter = dateStrGetter(TranslationType.Months, exports.TranslationWidth.Wide); + break; + case 'MMMMM': + formatter = dateStrGetter(TranslationType.Months, exports.TranslationWidth.Narrow); + break; + // Month of the year (January, ...), string, standalone + case 'LLL': + formatter = + dateStrGetter(TranslationType.Months, exports.TranslationWidth.Abbreviated, exports.FormStyle.Standalone); + break; + case 'LLLL': + formatter = + dateStrGetter(TranslationType.Months, exports.TranslationWidth.Wide, exports.FormStyle.Standalone); + break; + case 'LLLLL': + formatter = + dateStrGetter(TranslationType.Months, exports.TranslationWidth.Narrow, exports.FormStyle.Standalone); + break; + // Week of the year (1, ... 52) + case 'w': + formatter = weekGetter(1); + break; + case 'ww': + formatter = weekGetter(2); + break; + // Week of the month (1, ...) + case 'W': + formatter = weekGetter(1, true); + break; + // Day of the month (1-31) + case 'd': + formatter = dateGetter(DateType.Date, 1); + break; + case 'dd': + formatter = dateGetter(DateType.Date, 2); + break; + // Day of the Week StandAlone (1, 1, Mon, Monday, M, Mo) + case 'c': + case 'cc': + formatter = dateGetter(DateType.Day, 1); + break; + case 'ccc': + formatter = + dateStrGetter(TranslationType.Days, exports.TranslationWidth.Abbreviated, exports.FormStyle.Standalone); + break; + case 'cccc': + formatter = dateStrGetter(TranslationType.Days, exports.TranslationWidth.Wide, exports.FormStyle.Standalone); + break; + case 'ccccc': + formatter = + dateStrGetter(TranslationType.Days, exports.TranslationWidth.Narrow, exports.FormStyle.Standalone); + break; + case 'cccccc': + formatter = dateStrGetter(TranslationType.Days, exports.TranslationWidth.Short, exports.FormStyle.Standalone); + break; + // Day of the Week + case 'E': + case 'EE': + case 'EEE': + formatter = dateStrGetter(TranslationType.Days, exports.TranslationWidth.Abbreviated); + break; + case 'EEEE': + formatter = dateStrGetter(TranslationType.Days, exports.TranslationWidth.Wide); + break; + case 'EEEEE': + formatter = dateStrGetter(TranslationType.Days, exports.TranslationWidth.Narrow); + break; + case 'EEEEEE': + formatter = dateStrGetter(TranslationType.Days, exports.TranslationWidth.Short); + break; + // Generic period of the day (am-pm) + case 'a': + case 'aa': + case 'aaa': + formatter = dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Abbreviated); + break; + case 'aaaa': + formatter = dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Wide); + break; + case 'aaaaa': + formatter = dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Narrow); + break; + // Extended period of the day (midnight, at night, ...), standalone + case 'b': + case 'bb': + case 'bbb': + formatter = dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Abbreviated, exports.FormStyle.Standalone, true); + break; + case 'bbbb': + formatter = dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Wide, exports.FormStyle.Standalone, true); + break; + case 'bbbbb': + formatter = dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Narrow, exports.FormStyle.Standalone, true); + break; + // Extended period of the day (midnight, night, ...), standalone + case 'B': + case 'BB': + case 'BBB': + formatter = dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Abbreviated, exports.FormStyle.Format, true); + break; + case 'BBBB': + formatter = + dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Wide, exports.FormStyle.Format, true); + break; + case 'BBBBB': + formatter = dateStrGetter(TranslationType.DayPeriods, exports.TranslationWidth.Narrow, exports.FormStyle.Format, true); + break; + // Hour in AM/PM, (1-12) + case 'h': + formatter = dateGetter(DateType.Hours, 1, -12); + break; + case 'hh': + formatter = dateGetter(DateType.Hours, 2, -12); + break; + // Hour of the day (0-23) + case 'H': + formatter = dateGetter(DateType.Hours, 1); + break; + // Hour in day, padded (00-23) + case 'HH': + formatter = dateGetter(DateType.Hours, 2); + break; + // Minute of the hour (0-59) + case 'm': + formatter = dateGetter(DateType.Minutes, 1); + break; + case 'mm': + formatter = dateGetter(DateType.Minutes, 2); + break; + // Second of the minute (0-59) + case 's': + formatter = dateGetter(DateType.Seconds, 1); + break; + case 'ss': + formatter = dateGetter(DateType.Seconds, 2); + break; + // Fractional second + case 'S': + formatter = dateGetter(DateType.FractionalSeconds, 1); + break; + case 'SS': + formatter = dateGetter(DateType.FractionalSeconds, 2); + break; + case 'SSS': + formatter = dateGetter(DateType.FractionalSeconds, 3); + break; + // Timezone ISO8601 short format (-0430) + case 'Z': + case 'ZZ': + case 'ZZZ': + formatter = timeZoneGetter(ZoneWidth.Short); + break; + // Timezone ISO8601 extended format (-04:30) + case 'ZZZZZ': + formatter = timeZoneGetter(ZoneWidth.Extended); + break; + // Timezone GMT short format (GMT+4) + case 'O': + case 'OO': + case 'OOO': + // Should be location, but fallback to format O instead because we don't have the data yet + case 'z': + case 'zz': + case 'zzz': + formatter = timeZoneGetter(ZoneWidth.ShortGMT); + break; + // Timezone GMT long format (GMT+0430) + case 'OOOO': + case 'ZZZZ': + // Should be location, but fallback to format O instead because we don't have the data yet + case 'zzzz': + formatter = timeZoneGetter(ZoneWidth.Long); + break; + default: + return null; + } + DATE_FORMATS[format] = formatter; + return formatter; + } + function timezoneToOffset(timezone, fallback) { + // Support: IE 11 only, Edge 13-15+ + // IE/Edge do not "understand" colon (`:`) in timezone + timezone = timezone.replace(/:/g, ''); + const requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; + return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; + } + function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; + } + function convertTimezoneToLocal(date, timezone, reverse) { + const reverseValue = reverse ? -1 : 1; + const dateTimezoneOffset = date.getTimezoneOffset(); + const timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverseValue * (timezoneOffset - dateTimezoneOffset)); + } + /** + * Converts a value to date. + * + * Supported input formats: + * - `Date` + * - number: timestamp + * - string: numeric (e.g. "1234"), ISO and date strings in a format supported by + * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). + * Note: ISO strings without time return a date without timeoffset. + * + * Throws if unable to convert to a date. + */ + function toDate(value) { + if (isDate(value)) { + return value; + } + if (typeof value === 'number' && !isNaN(value)) { + return new Date(value); + } + if (typeof value === 'string') { + value = value.trim(); + if (/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(value)) { + /* For ISO Strings without time the day, month and year must be extracted from the ISO String + before Date creation to avoid time offset and errors in the new Date. + If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new + date, some browsers (e.g. IE 9) will throw an invalid Date error. + If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset + is applied. + Note: ISO months are 0 for January, 1 for February, ... */ + const [y, m = 1, d = 1] = value.split('-').map((val) => +val); + return createDate(y, m - 1, d); + } + const parsedNb = parseFloat(value); + // any string that only contains numbers, like "1234" but not like "1234hello" + if (!isNaN(value - parsedNb)) { + return new Date(parsedNb); + } + let match; + if (match = value.match(ISO8601_DATE_REGEX)) { + return isoStringToDate(match); + } + } + const date = new Date(value); + if (!isDate(date)) { + throw new Error(`Unable to convert "${value}" into a date`); + } + return date; + } + /** + * Converts a date in ISO8601 to a Date. + * Used instead of `Date.parse` because of browser discrepancies. + */ + function isoStringToDate(match) { + const date = new Date(0); + let tzHour = 0; + let tzMin = 0; + // match[8] means that the string contains "Z" (UTC) or a timezone like "+01:00" or "+0100" + const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear; + const timeSetter = match[8] ? date.setUTCHours : date.setHours; + // if there is a timezone defined like "+01:00" or "+0100" + if (match[9]) { + tzHour = Number(match[9] + match[10]); + tzMin = Number(match[9] + match[11]); + } + dateSetter.call(date, Number(match[1]), Number(match[2]) - 1, Number(match[3])); + const h = Number(match[4] || 0) - tzHour; + const m = Number(match[5] || 0) - tzMin; + const s = Number(match[6] || 0); + // The ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11) + // defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms` + // becomes `999ms`. + const ms = Math.floor(parseFloat('0.' + (match[7] || 0)) * 1000); + timeSetter.call(date, h, m, s, ms); + return date; + } + function isDate(value) { + return value instanceof Date && !isNaN(value.valueOf()); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/; + const MAX_DIGITS = 22; + const DECIMAL_SEP = '.'; + const ZERO_CHAR = '0'; + const PATTERN_SEP = ';'; + const GROUP_SEP = ','; + const DIGIT_CHAR = '#'; + const CURRENCY_CHAR = '¤'; + const PERCENT_CHAR = '%'; + /** + * Transforms a number to a locale string based on a style and a format. + */ + function formatNumberToLocaleString(value, pattern, locale, groupSymbol, decimalSymbol, digitsInfo, isPercent = false) { + let formattedText = ''; + let isZero = false; + if (!isFinite(value)) { + formattedText = getLocaleNumberSymbol(locale, exports.NumberSymbol.Infinity); + } + else { + let parsedNumber = parseNumber(value); + if (isPercent) { + parsedNumber = toPercent(parsedNumber); + } + let minInt = pattern.minInt; + let minFraction = pattern.minFrac; + let maxFraction = pattern.maxFrac; + if (digitsInfo) { + const parts = digitsInfo.match(NUMBER_FORMAT_REGEXP); + if (parts === null) { + throw new Error(`${digitsInfo} is not a valid digit info`); + } + const minIntPart = parts[1]; + const minFractionPart = parts[3]; + const maxFractionPart = parts[5]; + if (minIntPart != null) { + minInt = parseIntAutoRadix(minIntPart); + } + if (minFractionPart != null) { + minFraction = parseIntAutoRadix(minFractionPart); + } + if (maxFractionPart != null) { + maxFraction = parseIntAutoRadix(maxFractionPart); + } + else if (minFractionPart != null && minFraction > maxFraction) { + maxFraction = minFraction; + } + } + roundNumber(parsedNumber, minFraction, maxFraction); + let digits = parsedNumber.digits; + let integerLen = parsedNumber.integerLen; + const exponent = parsedNumber.exponent; + let decimals = []; + isZero = digits.every(d => !d); + // pad zeros for small numbers + for (; integerLen < minInt; integerLen++) { + digits.unshift(0); + } + // pad zeros for small numbers + for (; integerLen < 0; integerLen++) { + digits.unshift(0); + } + // extract decimals digits + if (integerLen > 0) { + decimals = digits.splice(integerLen, digits.length); + } + else { + decimals = digits; + digits = [0]; + } + // format the integer digits with grouping separators + const groups = []; + if (digits.length >= pattern.lgSize) { + groups.unshift(digits.splice(-pattern.lgSize, digits.length).join('')); + } + while (digits.length > pattern.gSize) { + groups.unshift(digits.splice(-pattern.gSize, digits.length).join('')); + } + if (digits.length) { + groups.unshift(digits.join('')); + } + formattedText = groups.join(getLocaleNumberSymbol(locale, groupSymbol)); + // append the decimal digits + if (decimals.length) { + formattedText += getLocaleNumberSymbol(locale, decimalSymbol) + decimals.join(''); + } + if (exponent) { + formattedText += getLocaleNumberSymbol(locale, exports.NumberSymbol.Exponential) + '+' + exponent; + } + } + if (value < 0 && !isZero) { + formattedText = pattern.negPre + formattedText + pattern.negSuf; + } + else { + formattedText = pattern.posPre + formattedText + pattern.posSuf; + } + return formattedText; + } + /** + * @ngModule CommonModule + * @description + * + * Formats a number as currency using locale rules. + * + * @param value The number to format. + * @param locale A locale code for the locale format rules to use. + * @param currency A string containing the currency symbol or its name, + * such as "$" or "Canadian Dollar". Used in output string, but does not affect the operation + * of the function. + * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) + * currency code, such as `USD` for the US dollar and `EUR` for the euro. + * Used to determine the number of digits in the decimal part. + * @param digitsInfo Decimal representation options, specified by a string in the following format: + * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. + * + * @returns The formatted currency value. + * + * @see `formatNumber()` + * @see `DecimalPipe` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function formatCurrency(value, locale, currency, currencyCode, digitsInfo) { + const format = getLocaleNumberFormat(locale, exports.NumberFormatStyle.Currency); + const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign)); + pattern.minFrac = getNumberOfCurrencyDigits(currencyCode); + pattern.maxFrac = pattern.minFrac; + const res = formatNumberToLocaleString(value, pattern, locale, exports.NumberSymbol.CurrencyGroup, exports.NumberSymbol.CurrencyDecimal, digitsInfo); + return res + .replace(CURRENCY_CHAR, currency) + // if we have 2 time the currency character, the second one is ignored + .replace(CURRENCY_CHAR, '') + // If there is a spacing between currency character and the value and + // the currency character is suppressed by passing an empty string, the + // spacing character would remain as part of the string. Then we + // should remove it. + .trim(); + } + /** + * @ngModule CommonModule + * @description + * + * Formats a number as a percentage according to locale rules. + * + * @param value The number to format. + * @param locale A locale code for the locale format rules to use. + * @param digitsInfo Decimal representation options, specified by a string in the following format: + * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. + * + * @returns The formatted percentage value. + * + * @see `formatNumber()` + * @see `DecimalPipe` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * @publicApi + * + */ + function formatPercent(value, locale, digitsInfo) { + const format = getLocaleNumberFormat(locale, exports.NumberFormatStyle.Percent); + const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign)); + const res = formatNumberToLocaleString(value, pattern, locale, exports.NumberSymbol.Group, exports.NumberSymbol.Decimal, digitsInfo, true); + return res.replace(new RegExp(PERCENT_CHAR, 'g'), getLocaleNumberSymbol(locale, exports.NumberSymbol.PercentSign)); + } + /** + * @ngModule CommonModule + * @description + * + * Formats a number as text, with group sizing, separator, and other + * parameters based on the locale. + * + * @param value The number to format. + * @param locale A locale code for the locale format rules to use. + * @param digitsInfo Decimal representation options, specified by a string in the following format: + * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. + * + * @returns The formatted text string. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + function formatNumber(value, locale, digitsInfo) { + const format = getLocaleNumberFormat(locale, exports.NumberFormatStyle.Decimal); + const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign)); + return formatNumberToLocaleString(value, pattern, locale, exports.NumberSymbol.Group, exports.NumberSymbol.Decimal, digitsInfo); + } + function parseNumberFormat(format, minusSign = '-') { + const p = { + minInt: 1, + minFrac: 0, + maxFrac: 0, + posPre: '', + posSuf: '', + negPre: '', + negSuf: '', + gSize: 0, + lgSize: 0 + }; + const patternParts = format.split(PATTERN_SEP); + const positive = patternParts[0]; + const negative = patternParts[1]; + const positiveParts = positive.indexOf(DECIMAL_SEP) !== -1 ? + positive.split(DECIMAL_SEP) : + [ + positive.substring(0, positive.lastIndexOf(ZERO_CHAR) + 1), + positive.substring(positive.lastIndexOf(ZERO_CHAR) + 1) + ], integer = positiveParts[0], fraction = positiveParts[1] || ''; + p.posPre = integer.substring(0, integer.indexOf(DIGIT_CHAR)); + for (let i = 0; i < fraction.length; i++) { + const ch = fraction.charAt(i); + if (ch === ZERO_CHAR) { + p.minFrac = p.maxFrac = i + 1; + } + else if (ch === DIGIT_CHAR) { + p.maxFrac = i + 1; + } + else { + p.posSuf += ch; + } + } + const groups = integer.split(GROUP_SEP); + p.gSize = groups[1] ? groups[1].length : 0; + p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0; + if (negative) { + const trunkLen = positive.length - p.posPre.length - p.posSuf.length, pos = negative.indexOf(DIGIT_CHAR); + p.negPre = negative.substring(0, pos).replace(/'/g, ''); + p.negSuf = negative.slice(pos + trunkLen).replace(/'/g, ''); + } + else { + p.negPre = minusSign + p.posPre; + p.negSuf = p.posSuf; + } + return p; + } + // Transforms a parsed number into a percentage by multiplying it by 100 + function toPercent(parsedNumber) { + // if the number is 0, don't do anything + if (parsedNumber.digits[0] === 0) { + return parsedNumber; + } + // Getting the current number of decimals + const fractionLen = parsedNumber.digits.length - parsedNumber.integerLen; + if (parsedNumber.exponent) { + parsedNumber.exponent += 2; + } + else { + if (fractionLen === 0) { + parsedNumber.digits.push(0, 0); + } + else if (fractionLen === 1) { + parsedNumber.digits.push(0); + } + parsedNumber.integerLen += 2; + } + return parsedNumber; + } + /** + * Parses a number. + * Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/ + */ + function parseNumber(num) { + let numStr = Math.abs(num) + ''; + let exponent = 0, digits, integerLen; + let i, j, zeros; + // Decimal point? + if ((integerLen = numStr.indexOf(DECIMAL_SEP)) > -1) { + numStr = numStr.replace(DECIMAL_SEP, ''); + } + // Exponential form? + if ((i = numStr.search(/e/i)) > 0) { + // Work out the exponent. + if (integerLen < 0) + integerLen = i; + integerLen += +numStr.slice(i + 1); + numStr = numStr.substring(0, i); + } + else if (integerLen < 0) { + // There was no decimal point or exponent so it is an integer. + integerLen = numStr.length; + } + // Count the number of leading zeros. + for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { /* empty */ + } + if (i === (zeros = numStr.length)) { + // The digits are all zero. + digits = [0]; + integerLen = 1; + } + else { + // Count the number of trailing zeros + zeros--; + while (numStr.charAt(zeros) === ZERO_CHAR) + zeros--; + // Trailing zeros are insignificant so ignore them + integerLen -= i; + digits = []; + // Convert string to array of digits without leading/trailing zeros. + for (j = 0; i <= zeros; i++, j++) { + digits[j] = Number(numStr.charAt(i)); + } + } + // If the number overflows the maximum allowed digits then use an exponent. + if (integerLen > MAX_DIGITS) { + digits = digits.splice(0, MAX_DIGITS - 1); + exponent = integerLen - 1; + integerLen = 1; + } + return { digits, exponent, integerLen }; + } + /** + * Round the parsed number to the specified number of decimal places + * This function changes the parsedNumber in-place + */ + function roundNumber(parsedNumber, minFrac, maxFrac) { + if (minFrac > maxFrac) { + throw new Error(`The minimum number of digits after fraction (${minFrac}) is higher than the maximum (${maxFrac}).`); + } + let digits = parsedNumber.digits; + let fractionLen = digits.length - parsedNumber.integerLen; + const fractionSize = Math.min(Math.max(minFrac, fractionLen), maxFrac); + // The index of the digit to where rounding is to occur + let roundAt = fractionSize + parsedNumber.integerLen; + let digit = digits[roundAt]; + if (roundAt > 0) { + // Drop fractional digits beyond `roundAt` + digits.splice(Math.max(parsedNumber.integerLen, roundAt)); + // Set non-fractional digits beyond `roundAt` to 0 + for (let j = roundAt; j < digits.length; j++) { + digits[j] = 0; + } + } + else { + // We rounded to zero so reset the parsedNumber + fractionLen = Math.max(0, fractionLen); + parsedNumber.integerLen = 1; + digits.length = Math.max(1, roundAt = fractionSize + 1); + digits[0] = 0; + for (let i = 1; i < roundAt; i++) + digits[i] = 0; + } + if (digit >= 5) { + if (roundAt - 1 < 0) { + for (let k = 0; k > roundAt; k--) { + digits.unshift(0); + parsedNumber.integerLen++; + } + digits.unshift(1); + parsedNumber.integerLen++; + } + else { + digits[roundAt - 1]++; + } + } + // Pad out with zeros to get the required fraction length + for (; fractionLen < Math.max(0, fractionSize); fractionLen++) + digits.push(0); + let dropTrailingZeros = fractionSize !== 0; + // Minimal length = nb of decimals required + current nb of integers + // Any number besides that is optional and can be removed if it's a trailing 0 + const minLen = minFrac + parsedNumber.integerLen; + // Do any carrying, e.g. a digit was rounded up to 10 + const carry = digits.reduceRight(function (carry, d, i, digits) { + d = d + carry; + digits[i] = d < 10 ? d : d - 10; // d % 10 + if (dropTrailingZeros) { + // Do not keep meaningless fractional trailing zeros (e.g. 15.52000 --> 15.52) + if (digits[i] === 0 && i >= minLen) { + digits.pop(); + } + else { + dropTrailingZeros = false; + } + } + return d >= 10 ? 1 : 0; // Math.floor(d / 10); + }, 0); + if (carry) { + digits.unshift(carry); + parsedNumber.integerLen++; + } + } + function parseIntAutoRadix(text) { + const result = parseInt(text); + if (isNaN(result)) { + throw new Error('Invalid integer literal when parsing ' + text); + } + return result; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @publicApi + */ + class NgLocalization { + } + NgLocalization.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgLocalization, deps: [], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + NgLocalization.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgLocalization, providedIn: 'root', useFactory: (locale) => new NgLocaleLocalization(locale), deps: [{ token: i0.LOCALE_ID }] }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgLocalization, decorators: [{ + type: i0.Injectable, + args: [{ + providedIn: 'root', + useFactory: (locale) => new NgLocaleLocalization(locale), + deps: [i0.LOCALE_ID], + }] + }] }); + /** + * Returns the plural category for a given value. + * - "=value" when the case exists, + * - the plural category otherwise + */ + function getPluralCategory(value, cases, ngLocalization, locale) { + let key = `=${value}`; + if (cases.indexOf(key) > -1) { + return key; + } + key = ngLocalization.getPluralCategory(value, locale); + if (cases.indexOf(key) > -1) { + return key; + } + if (cases.indexOf('other') > -1) { + return 'other'; + } + throw new Error(`No plural message found for value "${value}"`); + } + /** + * Returns the plural case based on the locale + * + * @publicApi + */ + class NgLocaleLocalization extends NgLocalization { + constructor(locale) { + super(); + this.locale = locale; + } + getPluralCategory(value, locale) { + const plural = getLocalePluralCase(locale || this.locale)(value); + switch (plural) { + case exports.Plural.Zero: + return 'zero'; + case exports.Plural.One: + return 'one'; + case exports.Plural.Two: + return 'two'; + case exports.Plural.Few: + return 'few'; + case exports.Plural.Many: + return 'many'; + default: + return 'other'; + } + } + } + NgLocaleLocalization.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgLocaleLocalization, deps: [{ token: i0.LOCALE_ID }], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + NgLocaleLocalization.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgLocaleLocalization }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgLocaleLocalization, decorators: [{ + type: i0.Injectable + }], ctorParameters: function () { + return [{ type: undefined, decorators: [{ + type: i0.Inject, + args: [i0.LOCALE_ID] + }] }]; + } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Register global data to be used internally by Angular. See the + * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale + * data. + * + * The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1 + * + * @publicApi + */ + function registerLocaleData(data, localeId, extraData) { + return i0["ɵregisterLocaleData"](data, localeId, extraData); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + function parseCookieValue(cookieStr, name) { + name = encodeURIComponent(name); + for (const cookie of cookieStr.split(';')) { + const eqIndex = cookie.indexOf('='); + const [cookieName, cookieValue] = eqIndex == -1 ? [cookie, ''] : [cookie.slice(0, eqIndex), cookie.slice(eqIndex + 1)]; + if (cookieName.trim() === name) { + return decodeURIComponent(cookieValue); + } + } + return null; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @ngModule CommonModule + * + * @usageNotes + * ``` + * ... + * + * ... + * + * ... + * + * ... + * + * ... + * ``` + * + * @description + * + * Adds and removes CSS classes on an HTML element. + * + * The CSS classes are updated as follows, depending on the type of the expression evaluation: + * - `string` - the CSS classes listed in the string (space delimited) are added, + * - `Array` - the CSS classes declared as Array elements are added, + * - `Object` - keys are CSS classes that get added when the expression given in the value + * evaluates to a truthy value, otherwise they are removed. + * + * @publicApi + */ + class NgClass { + constructor(_iterableDiffers, _keyValueDiffers, _ngEl, _renderer) { + this._iterableDiffers = _iterableDiffers; + this._keyValueDiffers = _keyValueDiffers; + this._ngEl = _ngEl; + this._renderer = _renderer; + this._iterableDiffer = null; + this._keyValueDiffer = null; + this._initialClasses = []; + this._rawClass = null; + } + set klass(value) { + this._removeClasses(this._initialClasses); + this._initialClasses = typeof value === 'string' ? value.split(/\s+/) : []; + this._applyClasses(this._initialClasses); + this._applyClasses(this._rawClass); + } + set ngClass(value) { + this._removeClasses(this._rawClass); + this._applyClasses(this._initialClasses); + this._iterableDiffer = null; + this._keyValueDiffer = null; + this._rawClass = typeof value === 'string' ? value.split(/\s+/) : value; + if (this._rawClass) { + if (i0["ɵisListLikeIterable"](this._rawClass)) { + this._iterableDiffer = this._iterableDiffers.find(this._rawClass).create(); + } + else { + this._keyValueDiffer = this._keyValueDiffers.find(this._rawClass).create(); + } + } + } + ngDoCheck() { + if (this._iterableDiffer) { + const iterableChanges = this._iterableDiffer.diff(this._rawClass); + if (iterableChanges) { + this._applyIterableChanges(iterableChanges); + } + } + else if (this._keyValueDiffer) { + const keyValueChanges = this._keyValueDiffer.diff(this._rawClass); + if (keyValueChanges) { + this._applyKeyValueChanges(keyValueChanges); + } + } + } + _applyKeyValueChanges(changes) { + changes.forEachAddedItem((record) => this._toggleClass(record.key, record.currentValue)); + changes.forEachChangedItem((record) => this._toggleClass(record.key, record.currentValue)); + changes.forEachRemovedItem((record) => { + if (record.previousValue) { + this._toggleClass(record.key, false); + } + }); + } + _applyIterableChanges(changes) { + changes.forEachAddedItem((record) => { + if (typeof record.item === 'string') { + this._toggleClass(record.item, true); + } + else { + throw new Error(`NgClass can only toggle CSS classes expressed as strings, got ${i0["ɵstringify"](record.item)}`); + } + }); + changes.forEachRemovedItem((record) => this._toggleClass(record.item, false)); + } + /** + * Applies a collection of CSS classes to the DOM element. + * + * For argument of type Set and Array CSS class names contained in those collections are always + * added. + * For argument of type Map CSS class name in the map's key is toggled based on the value (added + * for truthy and removed for falsy). + */ + _applyClasses(rawClassVal) { + if (rawClassVal) { + if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { + rawClassVal.forEach((klass) => this._toggleClass(klass, true)); + } + else { + Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, !!rawClassVal[klass])); + } + } + } + /** + * Removes a collection of CSS classes from the DOM element. This is mostly useful for cleanup + * purposes. + */ + _removeClasses(rawClassVal) { + if (rawClassVal) { + if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { + rawClassVal.forEach((klass) => this._toggleClass(klass, false)); + } + else { + Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, false)); + } + } + } + _toggleClass(klass, enabled) { + klass = klass.trim(); + if (klass) { + klass.split(/\s+/g).forEach(klass => { + if (enabled) { + this._renderer.addClass(this._ngEl.nativeElement, klass); + } + else { + this._renderer.removeClass(this._ngEl.nativeElement, klass); + } + }); + } + } + } + NgClass.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgClass, deps: [{ token: i0__namespace.IterableDiffers }, { token: i0__namespace.KeyValueDiffers }, { token: i0__namespace.ElementRef }, { token: i0__namespace.Renderer2 }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgClass.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgClass, isStandalone: true, selector: "[ngClass]", inputs: { klass: ["class", "klass"], ngClass: "ngClass" }, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgClass, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngClass]', + standalone: true, + }] + }], ctorParameters: function () { return [{ type: i0__namespace.IterableDiffers }, { type: i0__namespace.KeyValueDiffers }, { type: i0__namespace.ElementRef }, { type: i0__namespace.Renderer2 }]; }, propDecorators: { klass: [{ + type: i0.Input, + args: ['class'] + }], ngClass: [{ + type: i0.Input, + args: ['ngClass'] + }] } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Instantiates a {@link Component} type and inserts its Host View into the current View. + * `NgComponentOutlet` provides a declarative approach for dynamic component creation. + * + * `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and + * any existing component will be destroyed. + * + * @usageNotes + * + * ### Fine tune control + * + * You can control the component creation process by using the following optional attributes: + * + * * `ngComponentOutletInjector`: Optional custom {@link Injector} that will be used as parent for + * the Component. Defaults to the injector of the current view container. + * + * * `ngComponentOutletContent`: Optional list of projectable nodes to insert into the content + * section of the component, if it exists. + * + * * `ngComponentOutletNgModule`: Optional NgModule class reference to allow loading another + * module dynamically, then loading a component from that module. + * + * * `ngComponentOutletNgModuleFactory`: Deprecated config option that allows providing optional + * NgModule factory to allow loading another module dynamically, then loading a component from that + * module. Use `ngComponentOutletNgModule` instead. + * + * ### Syntax + * + * Simple + * ``` + * + * ``` + * + * Customized injector/content + * ``` + * + * + * ``` + * + * Customized NgModule reference + * ``` + * + * + * ``` + * + * ### A simple example + * + * {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'} + * + * A more complete example with additional options: + * + * {@example common/ngComponentOutlet/ts/module.ts region='CompleteExample'} + * + * @publicApi + * @ngModule CommonModule + */ + class NgComponentOutlet { + constructor(_viewContainerRef) { + this._viewContainerRef = _viewContainerRef; + this.ngComponentOutlet = null; + } + /** @nodoc */ + ngOnChanges(changes) { + const { _viewContainerRef: viewContainerRef, ngComponentOutletNgModule: ngModule, ngComponentOutletNgModuleFactory: ngModuleFactory, } = this; + viewContainerRef.clear(); + this._componentRef = undefined; + if (this.ngComponentOutlet) { + const injector = this.ngComponentOutletInjector || viewContainerRef.parentInjector; + if (changes['ngComponentOutletNgModule'] || changes['ngComponentOutletNgModuleFactory']) { + if (this._moduleRef) + this._moduleRef.destroy(); + if (ngModule) { + this._moduleRef = i0.createNgModule(ngModule, getParentInjector(injector)); + } + else if (ngModuleFactory) { + this._moduleRef = ngModuleFactory.create(getParentInjector(injector)); + } + else { + this._moduleRef = undefined; + } + } + this._componentRef = viewContainerRef.createComponent(this.ngComponentOutlet, { + index: viewContainerRef.length, + injector, + ngModuleRef: this._moduleRef, + projectableNodes: this.ngComponentOutletContent, + }); + } + } + /** @nodoc */ + ngOnDestroy() { + if (this._moduleRef) + this._moduleRef.destroy(); + } + } + NgComponentOutlet.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgComponentOutlet, deps: [{ token: i0__namespace.ViewContainerRef }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgComponentOutlet.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgComponentOutlet, isStandalone: true, selector: "[ngComponentOutlet]", inputs: { ngComponentOutlet: "ngComponentOutlet", ngComponentOutletInjector: "ngComponentOutletInjector", ngComponentOutletContent: "ngComponentOutletContent", ngComponentOutletNgModule: "ngComponentOutletNgModule", ngComponentOutletNgModuleFactory: "ngComponentOutletNgModuleFactory" }, usesOnChanges: true, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgComponentOutlet, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngComponentOutlet]', + standalone: true, + }] + }], ctorParameters: function () { return [{ type: i0__namespace.ViewContainerRef }]; }, propDecorators: { ngComponentOutlet: [{ + type: i0.Input + }], ngComponentOutletInjector: [{ + type: i0.Input + }], ngComponentOutletContent: [{ + type: i0.Input + }], ngComponentOutletNgModule: [{ + type: i0.Input + }], ngComponentOutletNgModuleFactory: [{ + type: i0.Input + }] } }); + // Helper function that returns an Injector instance of a parent NgModule. + function getParentInjector(injector) { + const parentNgModule = injector.get(i0.NgModuleRef); + return parentNgModule.injector; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode; + /** + * @publicApi + */ + class NgForOfContext { + constructor($implicit, ngForOf, index, count) { + this.$implicit = $implicit; + this.ngForOf = ngForOf; + this.index = index; + this.count = count; + } + get first() { + return this.index === 0; + } + get last() { + return this.index === this.count - 1; + } + get even() { + return this.index % 2 === 0; + } + get odd() { + return !this.even; + } + } + /** + * A [structural directive](guide/structural-directives) that renders + * a template for each item in a collection. + * The directive is placed on an element, which becomes the parent + * of the cloned templates. + * + * The `ngForOf` directive is generally used in the + * [shorthand form](guide/structural-directives#asterisk) `*ngFor`. + * In this form, the template to be rendered for each iteration is the content + * of an anchor element containing the directive. + * + * The following example shows the shorthand syntax with some options, + * contained in an `
  • ` element. + * + * ``` + *
  • ...
  • + * ``` + * + * The shorthand form expands into a long form that uses the `ngForOf` selector + * on an `` element. + * The content of the `` element is the `
  • ` element that held the + * short-form directive. + * + * Here is the expanded version of the short-form example. + * + * ``` + * + *
  • ...
  • + *
    + * ``` + * + * Angular automatically expands the shorthand syntax as it compiles the template. + * The context for each embedded view is logically merged to the current component + * context according to its lexical position. + * + * When using the shorthand syntax, Angular allows only [one structural directive + * on an element](guide/structural-directives#one-per-element). + * If you want to iterate conditionally, for example, + * put the `*ngIf` on a container element that wraps the `*ngFor` element. + * For further discussion, see + * [Structural Directives](guide/structural-directives#one-per-element). + * + * @usageNotes + * + * ### Local variables + * + * `NgForOf` provides exported values that can be aliased to local variables. + * For example: + * + * ``` + *
  • + * {{i}}/{{users.length}}. {{user}} default + *
  • + * ``` + * + * The following exported values can be aliased to local variables: + * + * - `$implicit: T`: The value of the individual items in the iterable (`ngForOf`). + * - `ngForOf: NgIterable`: The value of the iterable expression. Useful when the expression is + * more complex then a property access, for example when using the async pipe (`userStreams | + * async`). + * - `index: number`: The index of the current item in the iterable. + * - `count: number`: The length of the iterable. + * - `first: boolean`: True when the item is the first item in the iterable. + * - `last: boolean`: True when the item is the last item in the iterable. + * - `even: boolean`: True when the item has an even index in the iterable. + * - `odd: boolean`: True when the item has an odd index in the iterable. + * + * ### Change propagation + * + * When the contents of the iterator changes, `NgForOf` makes the corresponding changes to the DOM: + * + * * When an item is added, a new instance of the template is added to the DOM. + * * When an item is removed, its template instance is removed from the DOM. + * * When items are reordered, their respective templates are reordered in the DOM. + * + * Angular uses object identity to track insertions and deletions within the iterator and reproduce + * those changes in the DOM. This has important implications for animations and any stateful + * controls that are present, such as `` elements that accept user input. Inserted rows can + * be animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state + * such as user input. + * For more on animations, see [Transitions and Triggers](guide/transition-and-triggers). + * + * The identities of elements in the iterator can change while the data does not. + * This can happen, for example, if the iterator is produced from an RPC to the server, and that + * RPC is re-run. Even if the data hasn't changed, the second response produces objects with + * different identities, and Angular must tear down the entire DOM and rebuild it (as if all old + * elements were deleted and all new elements inserted). + * + * To avoid this expensive operation, you can customize the default tracking algorithm. + * by supplying the `trackBy` option to `NgForOf`. + * `trackBy` takes a function that has two arguments: `index` and `item`. + * If `trackBy` is given, Angular tracks changes by the return value of the function. + * + * @see [Structural Directives](guide/structural-directives) + * @ngModule CommonModule + * @publicApi + */ + class NgForOf { + constructor(_viewContainer, _template, _differs) { + this._viewContainer = _viewContainer; + this._template = _template; + this._differs = _differs; + this._ngForOf = null; + this._ngForOfDirty = true; + this._differ = null; + } + /** + * The value of the iterable expression, which can be used as a + * [template input variable](guide/structural-directives#shorthand). + */ + set ngForOf(ngForOf) { + this._ngForOf = ngForOf; + this._ngForOfDirty = true; + } + /** + * Specifies a custom `TrackByFunction` to compute the identity of items in an iterable. + * + * If a custom `TrackByFunction` is not provided, `NgForOf` will use the item's [object + * identity](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) + * as the key. + * + * `NgForOf` uses the computed key to associate items in an iterable with DOM elements + * it produces for these items. + * + * A custom `TrackByFunction` is useful to provide good user experience in cases when items in an + * iterable rendered using `NgForOf` have a natural identifier (for example, custom ID or a + * primary key), and this iterable could be updated with new object instances that still + * represent the same underlying entity (for example, when data is re-fetched from the server, + * and the iterable is recreated and re-rendered, but most of the data is still the same). + * + * @see `TrackByFunction` + */ + set ngForTrackBy(fn) { + if (NG_DEV_MODE && fn != null && typeof fn !== 'function') { + // TODO(vicb): use a log service once there is a public one available + if (console && console.warn) { + console.warn(`trackBy must be a function, but received ${JSON.stringify(fn)}. ` + + `See https://angular.io/api/common/NgForOf#change-propagation for more information.`); + } + } + this._trackByFn = fn; + } + get ngForTrackBy() { + return this._trackByFn; + } + /** + * A reference to the template that is stamped out for each item in the iterable. + * @see [template reference variable](guide/template-reference-variables) + */ + set ngForTemplate(value) { + // TODO(TS2.1): make TemplateRef>> once we move to TS v2.1 + // The current type is too restrictive; a template that just uses index, for example, + // should be acceptable. + if (value) { + this._template = value; + } + } + /** + * Applies the changes when needed. + * @nodoc + */ + ngDoCheck() { + if (this._ngForOfDirty) { + this._ngForOfDirty = false; + // React on ngForOf changes only once all inputs have been initialized + const value = this._ngForOf; + if (!this._differ && value) { + if (NG_DEV_MODE) { + try { + // CAUTION: this logic is duplicated for production mode below, as the try-catch + // is only present in development builds. + this._differ = this._differs.find(value).create(this.ngForTrackBy); + } + catch (_a) { + let errorMessage = `Cannot find a differ supporting object '${value}' of type '` + + `${getTypeName(value)}'. NgFor only supports binding to Iterables, such as Arrays.`; + if (typeof value === 'object') { + errorMessage += ' Did you mean to use the keyvalue pipe?'; + } + throw new i0["ɵRuntimeError"](-2200 /* RuntimeErrorCode.NG_FOR_MISSING_DIFFER */, errorMessage); + } + } + else { + // CAUTION: this logic is duplicated for development mode above, as the try-catch + // is only present in development builds. + this._differ = this._differs.find(value).create(this.ngForTrackBy); + } + } + } + if (this._differ) { + const changes = this._differ.diff(this._ngForOf); + if (changes) + this._applyChanges(changes); + } + } + _applyChanges(changes) { + const viewContainer = this._viewContainer; + changes.forEachOperation((item, adjustedPreviousIndex, currentIndex) => { + if (item.previousIndex == null) { + // NgForOf is never "null" or "undefined" here because the differ detected + // that a new item needs to be inserted from the iterable. This implies that + // there is an iterable value for "_ngForOf". + viewContainer.createEmbeddedView(this._template, new NgForOfContext(item.item, this._ngForOf, -1, -1), currentIndex === null ? undefined : currentIndex); + } + else if (currentIndex == null) { + viewContainer.remove(adjustedPreviousIndex === null ? undefined : adjustedPreviousIndex); + } + else if (adjustedPreviousIndex !== null) { + const view = viewContainer.get(adjustedPreviousIndex); + viewContainer.move(view, currentIndex); + applyViewChange(view, item); + } + }); + for (let i = 0, ilen = viewContainer.length; i < ilen; i++) { + const viewRef = viewContainer.get(i); + const context = viewRef.context; + context.index = i; + context.count = ilen; + context.ngForOf = this._ngForOf; + } + changes.forEachIdentityChange((record) => { + const viewRef = viewContainer.get(record.currentIndex); + applyViewChange(viewRef, record); + }); + } + /** + * Asserts the correct type of the context for the template that `NgForOf` will render. + * + * The presence of this method is a signal to the Ivy template type-check compiler that the + * `NgForOf` structural directive renders its template with a specific context type. + */ + static ngTemplateContextGuard(dir, ctx) { + return true; + } + } + NgForOf.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgForOf, deps: [{ token: i0__namespace.ViewContainerRef }, { token: i0__namespace.TemplateRef }, { token: i0__namespace.IterableDiffers }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgForOf.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgForOf, isStandalone: true, selector: "[ngFor][ngForOf]", inputs: { ngForOf: "ngForOf", ngForTrackBy: "ngForTrackBy", ngForTemplate: "ngForTemplate" }, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgForOf, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngFor][ngForOf]', + standalone: true, + }] + }], ctorParameters: function () { return [{ type: i0__namespace.ViewContainerRef }, { type: i0__namespace.TemplateRef }, { type: i0__namespace.IterableDiffers }]; }, propDecorators: { ngForOf: [{ + type: i0.Input + }], ngForTrackBy: [{ + type: i0.Input + }], ngForTemplate: [{ + type: i0.Input + }] } }); + function applyViewChange(view, record) { + view.context.$implicit = record.item; + } + function getTypeName(type) { + return type['name'] || typeof type; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * A structural directive that conditionally includes a template based on the value of + * an expression coerced to Boolean. + * When the expression evaluates to true, Angular renders the template + * provided in a `then` clause, and when false or null, + * Angular renders the template provided in an optional `else` clause. The default + * template for the `else` clause is blank. + * + * A [shorthand form](guide/structural-directives#asterisk) of the directive, + * `*ngIf="condition"`, is generally used, provided + * as an attribute of the anchor element for the inserted template. + * Angular expands this into a more explicit version, in which the anchor element + * is contained in an `` element. + * + * Simple form with shorthand syntax: + * + * ``` + *
    Content to render when condition is true.
    + * ``` + * + * Simple form with expanded syntax: + * + * ``` + *
    Content to render when condition is + * true.
    + * ``` + * + * Form with an "else" block: + * + * ``` + *
    Content to render when condition is true.
    + * Content to render when condition is false. + * ``` + * + * Shorthand form with "then" and "else" blocks: + * + * ``` + *
    + * Content to render when condition is true. + * Content to render when condition is false. + * ``` + * + * Form with storing the value locally: + * + * ``` + *
    {{value}}
    + * Content to render when value is null. + * ``` + * + * @usageNotes + * + * The `*ngIf` directive is most commonly used to conditionally show an inline template, + * as seen in the following example. + * The default `else` template is blank. + * + * {@example common/ngIf/ts/module.ts region='NgIfSimple'} + * + * ### Showing an alternative template using `else` + * + * To display a template when `expression` evaluates to false, use an `else` template + * binding as shown in the following example. + * The `else` binding points to an `` element labeled `#elseBlock`. + * The template can be defined anywhere in the component view, but is typically placed right after + * `ngIf` for readability. + * + * {@example common/ngIf/ts/module.ts region='NgIfElse'} + * + * ### Using an external `then` template + * + * In the previous example, the then-clause template is specified inline, as the content of the + * tag that contains the `ngIf` directive. You can also specify a template that is defined + * externally, by referencing a labeled `` element. When you do this, you can + * change which template to use at runtime, as shown in the following example. + * + * {@example common/ngIf/ts/module.ts region='NgIfThenElse'} + * + * ### Storing a conditional result in a variable + * + * You might want to show a set of properties from the same object. If you are waiting + * for asynchronous data, the object can be undefined. + * In this case, you can use `ngIf` and store the result of the condition in a local + * variable as shown in the following example. + * + * {@example common/ngIf/ts/module.ts region='NgIfAs'} + * + * This code uses only one `AsyncPipe`, so only one subscription is created. + * The conditional statement stores the result of `userStream|async` in the local variable `user`. + * You can then bind the local `user` repeatedly. + * + * The conditional displays the data only if `userStream` returns a value, + * so you don't need to use the + * safe-navigation-operator (`?.`) + * to guard against null values when accessing properties. + * You can display an alternative template while waiting for the data. + * + * ### Shorthand syntax + * + * The shorthand syntax `*ngIf` expands into two separate template specifications + * for the "then" and "else" clauses. For example, consider the following shorthand statement, + * that is meant to show a loading page while waiting for data to be loaded. + * + * ``` + *
    + * ... + *
    + * + * + *
    Loading...
    + *
    + * ``` + * + * You can see that the "else" clause references the `` + * with the `#loading` label, and the template for the "then" clause + * is provided as the content of the anchor element. + * + * However, when Angular expands the shorthand syntax, it creates + * another `` tag, with `ngIf` and `ngIfElse` directives. + * The anchor element containing the template for the "then" clause becomes + * the content of this unlabeled `` tag. + * + * ``` + * + *
    + * ... + *
    + *
    + * + * + *
    Loading...
    + *
    + * ``` + * + * The presence of the implicit template object has implications for the nesting of + * structural directives. For more on this subject, see + * [Structural Directives](guide/structural-directives#one-per-element). + * + * @ngModule CommonModule + * @publicApi + */ + class NgIf { + constructor(_viewContainer, templateRef) { + this._viewContainer = _viewContainer; + this._context = new NgIfContext(); + this._thenTemplateRef = null; + this._elseTemplateRef = null; + this._thenViewRef = null; + this._elseViewRef = null; + this._thenTemplateRef = templateRef; + } + /** + * The Boolean expression to evaluate as the condition for showing a template. + */ + set ngIf(condition) { + this._context.$implicit = this._context.ngIf = condition; + this._updateView(); + } + /** + * A template to show if the condition expression evaluates to true. + */ + set ngIfThen(templateRef) { + assertTemplate('ngIfThen', templateRef); + this._thenTemplateRef = templateRef; + this._thenViewRef = null; // clear previous view if any. + this._updateView(); + } + /** + * A template to show if the condition expression evaluates to false. + */ + set ngIfElse(templateRef) { + assertTemplate('ngIfElse', templateRef); + this._elseTemplateRef = templateRef; + this._elseViewRef = null; // clear previous view if any. + this._updateView(); + } + _updateView() { + if (this._context.$implicit) { + if (!this._thenViewRef) { + this._viewContainer.clear(); + this._elseViewRef = null; + if (this._thenTemplateRef) { + this._thenViewRef = + this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context); + } + } + } + else { + if (!this._elseViewRef) { + this._viewContainer.clear(); + this._thenViewRef = null; + if (this._elseTemplateRef) { + this._elseViewRef = + this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context); + } + } + } + } + /** + * Asserts the correct type of the context for the template that `NgIf` will render. + * + * The presence of this method is a signal to the Ivy template type-check compiler that the + * `NgIf` structural directive renders its template with a specific context type. + */ + static ngTemplateContextGuard(dir, ctx) { + return true; + } + } + NgIf.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgIf, deps: [{ token: i0__namespace.ViewContainerRef }, { token: i0__namespace.TemplateRef }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgIf.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgIf, isStandalone: true, selector: "[ngIf]", inputs: { ngIf: "ngIf", ngIfThen: "ngIfThen", ngIfElse: "ngIfElse" }, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgIf, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngIf]', + standalone: true, + }] + }], ctorParameters: function () { return [{ type: i0__namespace.ViewContainerRef }, { type: i0__namespace.TemplateRef }]; }, propDecorators: { ngIf: [{ + type: i0.Input + }], ngIfThen: [{ + type: i0.Input + }], ngIfElse: [{ + type: i0.Input + }] } }); + /** + * @publicApi + */ + class NgIfContext { + constructor() { + this.$implicit = null; + this.ngIf = null; + } + } + function assertTemplate(property, templateRef) { + const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView); + if (!isTemplateRefOrNull) { + throw new Error(`${property} must be a TemplateRef, but received '${i0["ɵstringify"](templateRef)}'.`); + } + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + class SwitchView { + constructor(_viewContainerRef, _templateRef) { + this._viewContainerRef = _viewContainerRef; + this._templateRef = _templateRef; + this._created = false; + } + create() { + this._created = true; + this._viewContainerRef.createEmbeddedView(this._templateRef); + } + destroy() { + this._created = false; + this._viewContainerRef.clear(); + } + enforceState(created) { + if (created && !this._created) { + this.create(); + } + else if (!created && this._created) { + this.destroy(); + } + } + } + /** + * @ngModule CommonModule + * + * @description + * The `[ngSwitch]` directive on a container specifies an expression to match against. + * The expressions to match are provided by `ngSwitchCase` directives on views within the container. + * - Every view that matches is rendered. + * - If there are no matches, a view with the `ngSwitchDefault` directive is rendered. + * - Elements within the `[NgSwitch]` statement but outside of any `NgSwitchCase` + * or `ngSwitchDefault` directive are preserved at the location. + * + * @usageNotes + * Define a container element for the directive, and specify the switch expression + * to match against as an attribute: + * + * ``` + * + * ``` + * + * Within the container, `*ngSwitchCase` statements specify the match expressions + * as attributes. Include `*ngSwitchDefault` as the final case. + * + * ``` + * + * ... + * ... + * ... + * + * ``` + * + * ### Usage Examples + * + * The following example shows how to use more than one case to display the same view: + * + * ``` + * + * + * ... + * ... + * ... + * + * ... + * + * ``` + * + * The following example shows how cases can be nested: + * ``` + * + * ... + * ... + * ... + * + * + * + * + * + * ... + * + * ``` + * + * @publicApi + * @see `NgSwitchCase` + * @see `NgSwitchDefault` + * @see [Structural Directives](guide/structural-directives) + * + */ + class NgSwitch { + constructor() { + this._defaultUsed = false; + this._caseCount = 0; + this._lastCaseCheckIndex = 0; + this._lastCasesMatched = false; + } + set ngSwitch(newValue) { + this._ngSwitch = newValue; + if (this._caseCount === 0) { + this._updateDefaultCases(true); + } + } + /** @internal */ + _addCase() { + return this._caseCount++; + } + /** @internal */ + _addDefault(view) { + if (!this._defaultViews) { + this._defaultViews = []; + } + this._defaultViews.push(view); + } + /** @internal */ + _matchCase(value) { + const matched = value == this._ngSwitch; + this._lastCasesMatched = this._lastCasesMatched || matched; + this._lastCaseCheckIndex++; + if (this._lastCaseCheckIndex === this._caseCount) { + this._updateDefaultCases(!this._lastCasesMatched); + this._lastCaseCheckIndex = 0; + this._lastCasesMatched = false; + } + return matched; + } + _updateDefaultCases(useDefault) { + if (this._defaultViews && useDefault !== this._defaultUsed) { + this._defaultUsed = useDefault; + for (let i = 0; i < this._defaultViews.length; i++) { + const defaultView = this._defaultViews[i]; + defaultView.enforceState(useDefault); + } + } + } + } + NgSwitch.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgSwitch, deps: [], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgSwitch.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgSwitch, isStandalone: true, selector: "[ngSwitch]", inputs: { ngSwitch: "ngSwitch" }, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgSwitch, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngSwitch]', + standalone: true, + }] + }], propDecorators: { ngSwitch: [{ + type: i0.Input + }] } }); + /** + * @ngModule CommonModule + * + * @description + * Provides a switch case expression to match against an enclosing `ngSwitch` expression. + * When the expressions match, the given `NgSwitchCase` template is rendered. + * If multiple match expressions match the switch expression value, all of them are displayed. + * + * @usageNotes + * + * Within a switch container, `*ngSwitchCase` statements specify the match expressions + * as attributes. Include `*ngSwitchDefault` as the final case. + * + * ``` + * + * ... + * ... + * ... + * + * ``` + * + * Each switch-case statement contains an in-line HTML template or template reference + * that defines the subtree to be selected if the value of the match expression + * matches the value of the switch expression. + * + * Unlike JavaScript, which uses strict equality, Angular uses loose equality. + * This means that the empty string, `""` matches 0. + * + * @publicApi + * @see `NgSwitch` + * @see `NgSwitchDefault` + * + */ + class NgSwitchCase { + constructor(viewContainer, templateRef, ngSwitch) { + this.ngSwitch = ngSwitch; + if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) { + throwNgSwitchProviderNotFoundError('ngSwitchCase', 'NgSwitchCase'); + } + ngSwitch._addCase(); + this._view = new SwitchView(viewContainer, templateRef); + } + /** + * Performs case matching. For internal use only. + * @nodoc + */ + ngDoCheck() { + this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); + } + } + NgSwitchCase.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgSwitchCase, deps: [{ token: i0__namespace.ViewContainerRef }, { token: i0__namespace.TemplateRef }, { token: NgSwitch, host: true, optional: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgSwitchCase.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgSwitchCase, isStandalone: true, selector: "[ngSwitchCase]", inputs: { ngSwitchCase: "ngSwitchCase" }, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgSwitchCase, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngSwitchCase]', + standalone: true, + }] + }], ctorParameters: function () { + return [{ type: i0__namespace.ViewContainerRef }, { type: i0__namespace.TemplateRef }, { type: NgSwitch, decorators: [{ + type: i0.Optional + }, { + type: i0.Host + }] }]; + }, propDecorators: { ngSwitchCase: [{ + type: i0.Input + }] } }); + /** + * @ngModule CommonModule + * + * @description + * + * Creates a view that is rendered when no `NgSwitchCase` expressions + * match the `NgSwitch` expression. + * This statement should be the final case in an `NgSwitch`. + * + * @publicApi + * @see `NgSwitch` + * @see `NgSwitchCase` + * + */ + class NgSwitchDefault { + constructor(viewContainer, templateRef, ngSwitch) { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) { + throwNgSwitchProviderNotFoundError('ngSwitchDefault', 'NgSwitchDefault'); + } + ngSwitch._addDefault(new SwitchView(viewContainer, templateRef)); + } + } + NgSwitchDefault.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgSwitchDefault, deps: [{ token: i0__namespace.ViewContainerRef }, { token: i0__namespace.TemplateRef }, { token: NgSwitch, host: true, optional: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgSwitchDefault.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgSwitchDefault, isStandalone: true, selector: "[ngSwitchDefault]", ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgSwitchDefault, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngSwitchDefault]', + standalone: true, + }] + }], ctorParameters: function () { + return [{ type: i0__namespace.ViewContainerRef }, { type: i0__namespace.TemplateRef }, { type: NgSwitch, decorators: [{ + type: i0.Optional + }, { + type: i0.Host + }] }]; + } }); + function throwNgSwitchProviderNotFoundError(attrName, directiveName) { + throw new i0["ɵRuntimeError"](2000 /* RuntimeErrorCode.PARENT_NG_SWITCH_NOT_FOUND */, `An element with the "${attrName}" attribute ` + + `(matching the "${directiveName}" directive) must be located inside an element with the "ngSwitch" attribute ` + + `(matching "NgSwitch" directive)`); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @ngModule CommonModule + * + * @usageNotes + * ``` + * + * there is nothing + * there is one + * there are a few + * + * ``` + * + * @description + * + * Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization. + * + * Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees + * that match the switch expression's pluralization category. + * + * To use this directive you must provide a container element that sets the `[ngPlural]` attribute + * to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their + * expression: + * - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value + * matches the switch expression exactly, + * - otherwise, the view will be treated as a "category match", and will only display if exact + * value matches aren't found and the value maps to its category for the defined locale. + * + * See http://cldr.unicode.org/index/cldr-spec/plural-rules + * + * @publicApi + */ + class NgPlural { + constructor(_localization) { + this._localization = _localization; + this._caseViews = {}; + } + set ngPlural(value) { + this._switchValue = value; + this._updateView(); + } + addCase(value, switchView) { + this._caseViews[value] = switchView; + } + _updateView() { + this._clearViews(); + const cases = Object.keys(this._caseViews); + const key = getPluralCategory(this._switchValue, cases, this._localization); + this._activateView(this._caseViews[key]); + } + _clearViews() { + if (this._activeView) + this._activeView.destroy(); + } + _activateView(view) { + if (view) { + this._activeView = view; + this._activeView.create(); + } + } + } + NgPlural.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgPlural, deps: [{ token: NgLocalization }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgPlural.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgPlural, isStandalone: true, selector: "[ngPlural]", inputs: { ngPlural: "ngPlural" }, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgPlural, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngPlural]', + standalone: true, + }] + }], ctorParameters: function () { return [{ type: NgLocalization }]; }, propDecorators: { ngPlural: [{ + type: i0.Input + }] } }); + /** + * @ngModule CommonModule + * + * @description + * + * Creates a view that will be added/removed from the parent {@link NgPlural} when the + * given expression matches the plural expression according to CLDR rules. + * + * @usageNotes + * ``` + * + * ... + * ... + * + *``` + * + * See {@link NgPlural} for more details and example. + * + * @publicApi + */ + class NgPluralCase { + constructor(value, template, viewContainer, ngPlural) { + this.value = value; + const isANumber = !isNaN(Number(value)); + ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template)); + } + } + NgPluralCase.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgPluralCase, deps: [{ token: 'ngPluralCase', attribute: true }, { token: i0__namespace.TemplateRef }, { token: i0__namespace.ViewContainerRef }, { token: NgPlural, host: true }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgPluralCase.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgPluralCase, isStandalone: true, selector: "[ngPluralCase]", ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgPluralCase, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngPluralCase]', + standalone: true, + }] + }], ctorParameters: function () { + return [{ type: undefined, decorators: [{ + type: i0.Attribute, + args: ['ngPluralCase'] + }] }, { type: i0__namespace.TemplateRef }, { type: i0__namespace.ViewContainerRef }, { type: NgPlural, decorators: [{ + type: i0.Host + }] }]; + } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @ngModule CommonModule + * + * @usageNotes + * + * Set the font of the containing element to the result of an expression. + * + * ``` + * ... + * ``` + * + * Set the width of the containing element to a pixel value returned by an expression. + * + * ``` + * ... + * ``` + * + * Set a collection of style values using an expression that returns key-value pairs. + * + * ``` + * ... + * ``` + * + * @description + * + * An attribute directive that updates styles for the containing HTML element. + * Sets one or more style properties, specified as colon-separated key-value pairs. + * The key is a style name, with an optional `.` suffix + * (such as 'top.px', 'font-style.em'). + * The value is an expression to be evaluated. + * The resulting non-null value, expressed in the given unit, + * is assigned to the given style property. + * If the result of evaluation is null, the corresponding style is removed. + * + * @publicApi + */ + class NgStyle { + constructor(_ngEl, _differs, _renderer) { + this._ngEl = _ngEl; + this._differs = _differs; + this._renderer = _renderer; + this._ngStyle = null; + this._differ = null; + } + set ngStyle(values) { + this._ngStyle = values; + if (!this._differ && values) { + this._differ = this._differs.find(values).create(); + } + } + ngDoCheck() { + if (this._differ) { + const changes = this._differ.diff(this._ngStyle); + if (changes) { + this._applyChanges(changes); + } + } + } + _setStyle(nameAndUnit, value) { + const [name, unit] = nameAndUnit.split('.'); + const flags = name.indexOf('-') === -1 ? undefined : i0.RendererStyleFlags2.DashCase; + if (value != null) { + this._renderer.setStyle(this._ngEl.nativeElement, name, unit ? `${value}${unit}` : value, flags); + } + else { + this._renderer.removeStyle(this._ngEl.nativeElement, name, flags); + } + } + _applyChanges(changes) { + changes.forEachRemovedItem((record) => this._setStyle(record.key, null)); + changes.forEachAddedItem((record) => this._setStyle(record.key, record.currentValue)); + changes.forEachChangedItem((record) => this._setStyle(record.key, record.currentValue)); + } + } + NgStyle.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgStyle, deps: [{ token: i0__namespace.ElementRef }, { token: i0__namespace.KeyValueDiffers }, { token: i0__namespace.Renderer2 }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgStyle.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgStyle, isStandalone: true, selector: "[ngStyle]", inputs: { ngStyle: "ngStyle" }, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgStyle, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngStyle]', + standalone: true, + }] + }], ctorParameters: function () { return [{ type: i0__namespace.ElementRef }, { type: i0__namespace.KeyValueDiffers }, { type: i0__namespace.Renderer2 }]; }, propDecorators: { ngStyle: [{ + type: i0.Input, + args: ['ngStyle'] + }] } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @ngModule CommonModule + * + * @description + * + * Inserts an embedded view from a prepared `TemplateRef`. + * + * You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`. + * `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding + * by the local template `let` declarations. + * + * @usageNotes + * ``` + * + * ``` + * + * Using the key `$implicit` in the context object will set its value as default. + * + * ### Example + * + * {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'} + * + * @publicApi + */ + class NgTemplateOutlet { + constructor(_viewContainerRef) { + this._viewContainerRef = _viewContainerRef; + this._viewRef = null; + /** + * A context object to attach to the {@link EmbeddedViewRef}. This should be an + * object, the object's keys will be available for binding by the local template `let` + * declarations. + * Using the key `$implicit` in the context object will set its value as default. + */ + this.ngTemplateOutletContext = null; + /** + * A string defining the template reference and optionally the context object for the template. + */ + this.ngTemplateOutlet = null; + /** Injector to be used within the embedded view. */ + this.ngTemplateOutletInjector = null; + } + /** @nodoc */ + ngOnChanges(changes) { + if (changes['ngTemplateOutlet'] || changes['ngTemplateOutletInjector']) { + const viewContainerRef = this._viewContainerRef; + if (this._viewRef) { + viewContainerRef.remove(viewContainerRef.indexOf(this._viewRef)); + } + if (this.ngTemplateOutlet) { + const { ngTemplateOutlet: template, ngTemplateOutletContext: context, ngTemplateOutletInjector: injector } = this; + this._viewRef = viewContainerRef.createEmbeddedView(template, context, injector ? { injector } : undefined); + } + else { + this._viewRef = null; + } + } + else if (this._viewRef && changes['ngTemplateOutletContext'] && this.ngTemplateOutletContext) { + this._viewRef.context = this.ngTemplateOutletContext; + } + } + } + NgTemplateOutlet.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgTemplateOutlet, deps: [{ token: i0__namespace.ViewContainerRef }], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgTemplateOutlet.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgTemplateOutlet, isStandalone: true, selector: "[ngTemplateOutlet]", inputs: { ngTemplateOutletContext: "ngTemplateOutletContext", ngTemplateOutlet: "ngTemplateOutlet", ngTemplateOutletInjector: "ngTemplateOutletInjector" }, usesOnChanges: true, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgTemplateOutlet, decorators: [{ + type: i0.Directive, + args: [{ + selector: '[ngTemplateOutlet]', + standalone: true, + }] + }], ctorParameters: function () { return [{ type: i0__namespace.ViewContainerRef }]; }, propDecorators: { ngTemplateOutletContext: [{ + type: i0.Input + }], ngTemplateOutlet: [{ + type: i0.Input + }], ngTemplateOutletInjector: [{ + type: i0.Input + }] } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * A collection of Angular directives that are likely to be used in each and every Angular + * application. + */ + const COMMON_DIRECTIVES = [ + NgClass, + NgComponentOutlet, + NgForOf, + NgIf, + NgTemplateOutlet, + NgStyle, + NgSwitch, + NgSwitchCase, + NgSwitchDefault, + NgPlural, + NgPluralCase, + ]; + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + function invalidPipeArgumentError(type, value) { + return new i0["ɵRuntimeError"](2100 /* RuntimeErrorCode.INVALID_PIPE_ARGUMENT */, ngDevMode && `InvalidPipeArgument: '${value}' for pipe '${i0["ɵstringify"](type)}'`); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + class SubscribableStrategy { + createSubscription(async, updateLatestValue) { + return async.subscribe({ + next: updateLatestValue, + error: (e) => { + throw e; + } + }); + } + dispose(subscription) { + subscription.unsubscribe(); + } + } + class PromiseStrategy { + createSubscription(async, updateLatestValue) { + return async.then(updateLatestValue, e => { + throw e; + }); + } + dispose(subscription) { } + } + const _promiseStrategy = new PromiseStrategy(); + const _subscribableStrategy = new SubscribableStrategy(); + /** + * @ngModule CommonModule + * @description + * + * Unwraps a value from an asynchronous primitive. + * + * The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has + * emitted. When a new value is emitted, the `async` pipe marks the component to be checked for + * changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid + * potential memory leaks. When the reference of the expression changes, the `async` pipe + * automatically unsubscribes from the old `Observable` or `Promise` and subscribes to the new one. + * + * @usageNotes + * + * ### Examples + * + * This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the + * promise. + * + * {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'} + * + * It's also possible to use `async` with Observables. The example below binds the `time` Observable + * to the view. The Observable continuously updates the view with the current time. + * + * {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'} + * + * @publicApi + */ + class AsyncPipe { + constructor(ref) { + this._latestValue = null; + this._subscription = null; + this._obj = null; + this._strategy = null; + // Assign `ref` into `this._ref` manually instead of declaring `_ref` in the constructor + // parameter list, as the type of `this._ref` includes `null` unlike the type of `ref`. + this._ref = ref; + } + ngOnDestroy() { + if (this._subscription) { + this._dispose(); + } + // Clear the `ChangeDetectorRef` and its association with the view data, to mitigate + // potential memory leaks in Observables that could otherwise cause the view data to + // be retained. + // https://github.com/angular/angular/issues/17624 + this._ref = null; + } + transform(obj) { + if (!this._obj) { + if (obj) { + this._subscribe(obj); + } + return this._latestValue; + } + if (obj !== this._obj) { + this._dispose(); + return this.transform(obj); + } + return this._latestValue; + } + _subscribe(obj) { + this._obj = obj; + this._strategy = this._selectStrategy(obj); + this._subscription = this._strategy.createSubscription(obj, (value) => this._updateLatestValue(obj, value)); + } + _selectStrategy(obj) { + if (i0["ɵisPromise"](obj)) { + return _promiseStrategy; + } + if (i0["ɵisSubscribable"](obj)) { + return _subscribableStrategy; + } + throw invalidPipeArgumentError(AsyncPipe, obj); + } + _dispose() { + // Note: `dispose` is only called if a subscription has been initialized before, indicating + // that `this._strategy` is also available. + this._strategy.dispose(this._subscription); + this._latestValue = null; + this._subscription = null; + this._obj = null; + } + _updateLatestValue(async, value) { + if (async === this._obj) { + this._latestValue = value; + // Note: `this._ref` is only cleared in `ngOnDestroy` so is known to be available when a + // value is being updated. + this._ref.markForCheck(); + } + } + } + AsyncPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: AsyncPipe, deps: [{ token: i0__namespace.ChangeDetectorRef }], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + AsyncPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: AsyncPipe, isStandalone: true, name: "async", pure: false }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: AsyncPipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'async', + pure: false, + standalone: true, + }] + }], ctorParameters: function () { return [{ type: i0__namespace.ChangeDetectorRef }]; } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Transforms text to all lower case. + * + * @see `UpperCasePipe` + * @see `TitleCasePipe` + * @usageNotes + * + * The following example defines a view that allows the user to enter + * text, and then uses the pipe to convert the input text to all lower case. + * + * + * + * @ngModule CommonModule + * @publicApi + */ + class LowerCasePipe { + transform(value) { + if (value == null) + return null; + if (typeof value !== 'string') { + throw invalidPipeArgumentError(LowerCasePipe, value); + } + return value.toLowerCase(); + } + } + LowerCasePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: LowerCasePipe, deps: [], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + LowerCasePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: LowerCasePipe, isStandalone: true, name: "lowercase" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: LowerCasePipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'lowercase', + standalone: true, + }] + }] }); + // + // Regex below matches any Unicode word and number compatible with ES5. In ES2018 the same result + // can be achieved by using /[0-9\p{L}]\S*/gu and also known as Unicode Property Escapes + // (https://2ality.com/2017/07/regexp-unicode-property-escapes.html). Since there is no + // transpilation of this functionality down to ES5 without external tool, the only solution is + // to use already transpiled form. Example can be found here - + // https://mothereff.in/regexpu#input=var+regex+%3D+%2F%5B0-9%5Cp%7BL%7D%5D%5CS*%2Fgu%3B%0A%0A&unicodePropertyEscape=1 + // + const unicodeWordMatch = /(?:[0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF38\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])\S*/g; + /** + * Transforms text to title case. + * Capitalizes the first letter of each word and transforms the + * rest of the word to lower case. + * Words are delimited by any whitespace character, such as a space, tab, or line-feed character. + * + * @see `LowerCasePipe` + * @see `UpperCasePipe` + * + * @usageNotes + * The following example shows the result of transforming various strings into title case. + * + * + * + * @ngModule CommonModule + * @publicApi + */ + class TitleCasePipe { + transform(value) { + if (value == null) + return null; + if (typeof value !== 'string') { + throw invalidPipeArgumentError(TitleCasePipe, value); + } + return value.replace(unicodeWordMatch, (txt => txt[0].toUpperCase() + txt.slice(1).toLowerCase())); + } + } + TitleCasePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: TitleCasePipe, deps: [], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + TitleCasePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: TitleCasePipe, isStandalone: true, name: "titlecase" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: TitleCasePipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'titlecase', + standalone: true, + }] + }] }); + /** + * Transforms text to all upper case. + * @see `LowerCasePipe` + * @see `TitleCasePipe` + * + * @ngModule CommonModule + * @publicApi + */ + class UpperCasePipe { + transform(value) { + if (value == null) + return null; + if (typeof value !== 'string') { + throw invalidPipeArgumentError(UpperCasePipe, value); + } + return value.toUpperCase(); + } + } + UpperCasePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: UpperCasePipe, deps: [], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + UpperCasePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: UpperCasePipe, isStandalone: true, name: "uppercase" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: UpperCasePipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'uppercase', + standalone: true, + }] + }] }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * The default date format of Angular date pipe, which corresponds to the following format: + * `'MMM d,y'` (e.g. `Jun 15, 2015`) + */ + const DEFAULT_DATE_FORMAT = 'mediumDate'; + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Optionally-provided default timezone to use for all instances of `DatePipe` (such as `'+0430'`). + * If the value isn't provided, the `DatePipe` will use the end-user's local system timezone. + * + * @deprecated use DATE_PIPE_DEFAULT_OPTIONS token to configure DatePipe + */ + const DATE_PIPE_DEFAULT_TIMEZONE = new i0.InjectionToken('DATE_PIPE_DEFAULT_TIMEZONE'); + /** + * DI token that allows to provide default configuration for the `DatePipe` instances in an + * application. The value is an object which can include the following fields: + * - `dateFormat`: configures the default date format. If not provided, the `DatePipe` + * will use the 'mediumDate' as a value. + * - `timezone`: configures the default timezone. If not provided, the `DatePipe` will + * use the end-user's local system timezone. + * + * @see `DatePipeConfig` + * + * @usageNotes + * + * Various date pipe default values can be overwritten by providing this token with + * the value that has this interface. + * + * For example: + * + * Override the default date format by providing a value using the token: + * ```typescript + * providers: [ + * {provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {dateFormat: 'shortDate'}} + * ] + * ``` + * + * Override the default timezone by providing a value using the token: + * ```typescript + * providers: [ + * {provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {timezone: '-1200'}} + * ] + * ``` + */ + const DATE_PIPE_DEFAULT_OPTIONS = new i0.InjectionToken('DATE_PIPE_DEFAULT_OPTIONS'); + // clang-format off + /** + * @ngModule CommonModule + * @description + * + * Formats a date value according to locale rules. + * + * `DatePipe` is executed only when it detects a pure change to the input value. + * A pure change is either a change to a primitive input value + * (such as `String`, `Number`, `Boolean`, or `Symbol`), + * or a changed object reference (such as `Date`, `Array`, `Function`, or `Object`). + * + * Note that mutating a `Date` object does not cause the pipe to be rendered again. + * To ensure that the pipe is executed, you must create a new `Date` object. + * + * Only the `en-US` locale data comes with Angular. To localize dates + * in another language, you must import the corresponding locale data. + * See the [I18n guide](guide/i18n-common-format-data-locale) for more information. + * + * The time zone of the formatted value can be specified either by passing it in as the second + * parameter of the pipe, or by setting the default through the `DATE_PIPE_DEFAULT_TIMEZONE` + * injection token. The value that is passed in as the second parameter takes precedence over + * the one defined using the injection token. + * + * @see `formatDate()` + * + * + * @usageNotes + * + * The result of this pipe is not reevaluated when the input is mutated. To avoid the need to + * reformat the date on every change-detection cycle, treat the date as an immutable object + * and change the reference when the pipe needs to run again. + * + * ### Pre-defined format options + * + * | Option | Equivalent to | Examples (given in `en-US` locale) | + * |---------------|-------------------------------------|-------------------------------------------------| + * | `'short'` | `'M/d/yy, h:mm a'` | `6/15/15, 9:03 AM` | + * | `'medium'` | `'MMM d, y, h:mm:ss a'` | `Jun 15, 2015, 9:03:01 AM` | + * | `'long'` | `'MMMM d, y, h:mm:ss a z'` | `June 15, 2015 at 9:03:01 AM GMT+1` | + * | `'full'` | `'EEEE, MMMM d, y, h:mm:ss a zzzz'` | `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00` | + * | `'shortDate'` | `'M/d/yy'` | `6/15/15` | + * | `'mediumDate'`| `'MMM d, y'` | `Jun 15, 2015` | + * | `'longDate'` | `'MMMM d, y'` | `June 15, 2015` | + * | `'fullDate'` | `'EEEE, MMMM d, y'` | `Monday, June 15, 2015` | + * | `'shortTime'` | `'h:mm a'` | `9:03 AM` | + * | `'mediumTime'`| `'h:mm:ss a'` | `9:03:01 AM` | + * | `'longTime'` | `'h:mm:ss a z'` | `9:03:01 AM GMT+1` | + * | `'fullTime'` | `'h:mm:ss a zzzz'` | `9:03:01 AM GMT+01:00` | + * + * ### Custom format options + * + * You can construct a format string using symbols to specify the components + * of a date-time value, as described in the following table. + * Format details depend on the locale. + * Fields marked with (*) are only available in the extra data set for the given locale. + * + * | Field type | Format | Description | Example Value | + * |-------------------- |-------------|---------------------------------------------------------------|------------------------------------------------------------| + * | Era | G, GG & GGG | Abbreviated | AD | + * | | GGGG | Wide | Anno Domini | + * | | GGGGG | Narrow | A | + * | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Month | M | Numeric: 1 digit | 9, 12 | + * | | MM | Numeric: 2 digits + zero padded | 09, 12 | + * | | MMM | Abbreviated | Sep | + * | | MMMM | Wide | September | + * | | MMMMM | Narrow | S | + * | Month standalone | L | Numeric: 1 digit | 9, 12 | + * | | LL | Numeric: 2 digits + zero padded | 09, 12 | + * | | LLL | Abbreviated | Sep | + * | | LLLL | Wide | September | + * | | LLLLL | Narrow | S | + * | Week of year | w | Numeric: minimum digits | 1... 53 | + * | | ww | Numeric: 2 digits + zero padded | 01... 53 | + * | Week of month | W | Numeric: 1 digit | 1... 5 | + * | Day of month | d | Numeric: minimum digits | 1 | + * | | dd | Numeric: 2 digits + zero padded | 01 | + * | Week day | E, EE & EEE | Abbreviated | Tue | + * | | EEEE | Wide | Tuesday | + * | | EEEEE | Narrow | T | + * | | EEEEEE | Short | Tu | + * | Week day standalone | c, cc | Numeric: 1 digit | 2 | + * | | ccc | Abbreviated | Tue | + * | | cccc | Wide | Tuesday | + * | | ccccc | Narrow | T | + * | | cccccc | Short | Tu | + * | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | + * | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | + * | | aaaaa | Narrow | a/p | + * | Period* | B, BB & BBB | Abbreviated | mid. | + * | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | + * | | BBBBB | Narrow | md | + * | Period standalone* | b, bb & bbb | Abbreviated | mid. | + * | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | + * | | bbbbb | Narrow | md | + * | Hour 1-12 | h | Numeric: minimum digits | 1, 12 | + * | | hh | Numeric: 2 digits + zero padded | 01, 12 | + * | Hour 0-23 | H | Numeric: minimum digits | 0, 23 | + * | | HH | Numeric: 2 digits + zero padded | 00, 23 | + * | Minute | m | Numeric: minimum digits | 8, 59 | + * | | mm | Numeric: 2 digits + zero padded | 08, 59 | + * | Second | s | Numeric: minimum digits | 0... 59 | + * | | ss | Numeric: 2 digits + zero padded | 00... 59 | + * | Fractional seconds | S | Numeric: 1 digit | 0... 9 | + * | | SS | Numeric: 2 digits + zero padded | 00... 99 | + * | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | + * | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | + * | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | + * | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | + * | | ZZZZ | Long localized GMT format | GMT-8:00 | + * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | + * | | O, OO & OOO | Short localized GMT format | GMT-8 | + * | | OOOO | Long localized GMT format | GMT-08:00 | + * + * + * ### Format examples + * + * These examples transform a date into various formats, + * assuming that `dateObj` is a JavaScript `Date` object for + * year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11, + * given in the local time for the `en-US` locale. + * + * ``` + * {{ dateObj | date }} // output is 'Jun 15, 2015' + * {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM' + * {{ dateObj | date:'shortTime' }} // output is '9:43 PM' + * {{ dateObj | date:'mm:ss' }} // output is '43:11' + * ``` + * + * ### Usage example + * + * The following component uses a date pipe to display the current date in different formats. + * + * ``` + * @Component({ + * selector: 'date-pipe', + * template: `
    + *

    Today is {{today | date}}

    + *

    Or if you prefer, {{today | date:'fullDate'}}

    + *

    The time is {{today | date:'h:mm a z'}}

    + *
    ` + * }) + * // Get the current date and time as a date-time value. + * export class DatePipeComponent { + * today: number = Date.now(); + * } + * ``` + * + * @publicApi + */ + // clang-format on + class DatePipe { + constructor(locale, defaultTimezone, defaultOptions) { + this.locale = locale; + this.defaultTimezone = defaultTimezone; + this.defaultOptions = defaultOptions; + } + transform(value, format, timezone, locale) { + var _a, _b, _c, _d, _e; + if (value == null || value === '' || value !== value) + return null; + try { + const _format = (_b = format !== null && format !== void 0 ? format : (_a = this.defaultOptions) === null || _a === void 0 ? void 0 : _a.dateFormat) !== null && _b !== void 0 ? _b : DEFAULT_DATE_FORMAT; + const _timezone = (_e = (_d = timezone !== null && timezone !== void 0 ? timezone : (_c = this.defaultOptions) === null || _c === void 0 ? void 0 : _c.timezone) !== null && _d !== void 0 ? _d : this.defaultTimezone) !== null && _e !== void 0 ? _e : undefined; + return formatDate(value, _format, locale || this.locale, _timezone); + } + catch (error) { + throw invalidPipeArgumentError(DatePipe, error.message); + } + } + } + DatePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: DatePipe, deps: [{ token: i0.LOCALE_ID }, { token: DATE_PIPE_DEFAULT_TIMEZONE, optional: true }, { token: DATE_PIPE_DEFAULT_OPTIONS, optional: true }], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + DatePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: DatePipe, isStandalone: true, name: "date" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: DatePipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'date', + pure: true, + standalone: true, + }] + }], ctorParameters: function () { + return [{ type: undefined, decorators: [{ + type: i0.Inject, + args: [i0.LOCALE_ID] + }] }, { type: undefined, decorators: [{ + type: i0.Inject, + args: [DATE_PIPE_DEFAULT_TIMEZONE] + }, { + type: i0.Optional + }] }, { type: undefined, decorators: [{ + type: i0.Inject, + args: [DATE_PIPE_DEFAULT_OPTIONS] + }, { + type: i0.Optional + }] }]; + } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + const _INTERPOLATION_REGEXP = /#/g; + /** + * @ngModule CommonModule + * @description + * + * Maps a value to a string that pluralizes the value according to locale rules. + * + * @usageNotes + * + * ### Example + * + * {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'} + * + * @publicApi + */ + class I18nPluralPipe { + constructor(_localization) { + this._localization = _localization; + } + /** + * @param value the number to be formatted + * @param pluralMap an object that mimics the ICU format, see + * https://unicode-org.github.io/icu/userguide/format_parse/messages/. + * @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by + * default). + */ + transform(value, pluralMap, locale) { + if (value == null) + return ''; + if (typeof pluralMap !== 'object' || pluralMap === null) { + throw invalidPipeArgumentError(I18nPluralPipe, pluralMap); + } + const key = getPluralCategory(value, Object.keys(pluralMap), this._localization, locale); + return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString()); + } + } + I18nPluralPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: I18nPluralPipe, deps: [{ token: NgLocalization }], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + I18nPluralPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: I18nPluralPipe, isStandalone: true, name: "i18nPlural" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: I18nPluralPipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'i18nPlural', + pure: true, + standalone: true, + }] + }], ctorParameters: function () { return [{ type: NgLocalization }]; } }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @ngModule CommonModule + * @description + * + * Generic selector that displays the string that matches the current value. + * + * If none of the keys of the `mapping` match the `value`, then the content + * of the `other` key is returned when present, otherwise an empty string is returned. + * + * @usageNotes + * + * ### Example + * + * {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'} + * + * @publicApi + */ + class I18nSelectPipe { + /** + * @param value a string to be internationalized. + * @param mapping an object that indicates the text that should be displayed + * for different values of the provided `value`. + */ + transform(value, mapping) { + if (value == null) + return ''; + if (typeof mapping !== 'object' || typeof value !== 'string') { + throw invalidPipeArgumentError(I18nSelectPipe, mapping); + } + if (mapping.hasOwnProperty(value)) { + return mapping[value]; + } + if (mapping.hasOwnProperty('other')) { + return mapping['other']; + } + return ''; + } + } + I18nSelectPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: I18nSelectPipe, deps: [], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + I18nSelectPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: I18nSelectPipe, isStandalone: true, name: "i18nSelect" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: I18nSelectPipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'i18nSelect', + pure: true, + standalone: true, + }] + }] }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @ngModule CommonModule + * @description + * + * Converts a value into its JSON-format representation. Useful for debugging. + * + * @usageNotes + * + * The following component uses a JSON pipe to convert an object + * to JSON format, and displays the string in both formats for comparison. + * + * {@example common/pipes/ts/json_pipe.ts region='JsonPipe'} + * + * @publicApi + */ + class JsonPipe { + /** + * @param value A value of any type to convert into a JSON-format string. + */ + transform(value) { + return JSON.stringify(value, null, 2); + } + } + JsonPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: JsonPipe, deps: [], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + JsonPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: JsonPipe, isStandalone: true, name: "json", pure: false }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: JsonPipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'json', + pure: false, + standalone: true, + }] + }] }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + function makeKeyValuePair(key, value) { + return { key: key, value: value }; + } + /** + * @ngModule CommonModule + * @description + * + * Transforms Object or Map into an array of key value pairs. + * + * The output array will be ordered by keys. + * By default the comparator will be by Unicode point value. + * You can optionally pass a compareFn if your keys are complex types. + * + * @usageNotes + * ### Examples + * + * This examples show how an Object or a Map can be iterated by ngFor with the use of this + * keyvalue pipe. + * + * {@example common/pipes/ts/keyvalue_pipe.ts region='KeyValuePipe'} + * + * @publicApi + */ + class KeyValuePipe { + constructor(differs) { + this.differs = differs; + this.keyValues = []; + this.compareFn = defaultComparator; + } + transform(input, compareFn = defaultComparator) { + if (!input || (!(input instanceof Map) && typeof input !== 'object')) { + return null; + } + if (!this.differ) { + // make a differ for whatever type we've been passed in + this.differ = this.differs.find(input).create(); + } + const differChanges = this.differ.diff(input); + const compareFnChanged = compareFn !== this.compareFn; + if (differChanges) { + this.keyValues = []; + differChanges.forEachItem((r) => { + this.keyValues.push(makeKeyValuePair(r.key, r.currentValue)); + }); + } + if (differChanges || compareFnChanged) { + this.keyValues.sort(compareFn); + this.compareFn = compareFn; + } + return this.keyValues; + } + } + KeyValuePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: KeyValuePipe, deps: [{ token: i0__namespace.KeyValueDiffers }], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + KeyValuePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: KeyValuePipe, isStandalone: true, name: "keyvalue", pure: false }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: KeyValuePipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'keyvalue', + pure: false, + standalone: true, + }] + }], ctorParameters: function () { return [{ type: i0__namespace.KeyValueDiffers }]; } }); + function defaultComparator(keyValueA, keyValueB) { + const a = keyValueA.key; + const b = keyValueB.key; + // if same exit with 0; + if (a === b) + return 0; + // make sure that undefined are at the end of the sort. + if (a === undefined) + return 1; + if (b === undefined) + return -1; + // make sure that nulls are at the end of the sort. + if (a === null) + return 1; + if (b === null) + return -1; + if (typeof a == 'string' && typeof b == 'string') { + return a < b ? -1 : 1; + } + if (typeof a == 'number' && typeof b == 'number') { + return a - b; + } + if (typeof a == 'boolean' && typeof b == 'boolean') { + return a < b ? -1 : 1; + } + // `a` and `b` are of different types. Compare their string values. + const aString = String(a); + const bString = String(b); + return aString == bString ? 0 : aString < bString ? -1 : 1; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @ngModule CommonModule + * @description + * + * Formats a value according to digit options and locale rules. + * Locale determines group sizing and separator, + * decimal point character, and other locale-specific configurations. + * + * @see `formatNumber()` + * + * @usageNotes + * + * ### digitsInfo + * + * The value's decimal representation is specified by the `digitsInfo` + * parameter, written in the following format:
    + * + * ``` + * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits} + * ``` + * + * - `minIntegerDigits`: + * The minimum number of integer digits before the decimal point. + * Default is 1. + * + * - `minFractionDigits`: + * The minimum number of digits after the decimal point. + * Default is 0. + * + * - `maxFractionDigits`: + * The maximum number of digits after the decimal point. + * Default is 3. + * + * If the formatted value is truncated it will be rounded using the "to-nearest" method: + * + * ``` + * {{3.6 | number: '1.0-0'}} + * + * + * {{-3.6 | number:'1.0-0'}} + * + * ``` + * + * ### locale + * + * `locale` will format a value according to locale rules. + * Locale determines group sizing and separator, + * decimal point character, and other locale-specific configurations. + * + * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. + * + * See [Setting your app locale](guide/i18n-common-locale-id). + * + * ### Example + * + * The following code shows how the pipe transforms values + * according to various format specifications, + * where the caller's default locale is `en-US`. + * + * + * + * @publicApi + */ + class DecimalPipe { + constructor(_locale) { + this._locale = _locale; + } + /** + * @param value The value to be formatted. + * @param digitsInfo Sets digit and decimal representation. + * [See more](#digitsinfo). + * @param locale Specifies what locale format rules to use. + * [See more](#locale). + */ + transform(value, digitsInfo, locale) { + if (!isValue(value)) + return null; + locale = locale || this._locale; + try { + const num = strToNumber(value); + return formatNumber(num, locale, digitsInfo); + } + catch (error) { + throw invalidPipeArgumentError(DecimalPipe, error.message); + } + } + } + DecimalPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: DecimalPipe, deps: [{ token: i0.LOCALE_ID }], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + DecimalPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: DecimalPipe, isStandalone: true, name: "number" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: DecimalPipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'number', + standalone: true, + }] + }], ctorParameters: function () { + return [{ type: undefined, decorators: [{ + type: i0.Inject, + args: [i0.LOCALE_ID] + }] }]; + } }); + /** + * @ngModule CommonModule + * @description + * + * Transforms a number to a percentage + * string, formatted according to locale rules that determine group sizing and + * separator, decimal-point character, and other locale-specific + * configurations. + * + * @see `formatPercent()` + * + * @usageNotes + * The following code shows how the pipe transforms numbers + * into text strings, according to various format specifications, + * where the caller's default locale is `en-US`. + * + * + * + * @publicApi + */ + class PercentPipe { + constructor(_locale) { + this._locale = _locale; + } + /** + * + * @param value The number to be formatted as a percentage. + * @param digitsInfo Decimal representation options, specified by a string + * in the following format:
    + * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. + * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. + * Default is `1`. + * - `minFractionDigits`: The minimum number of digits after the decimal point. + * Default is `0`. + * - `maxFractionDigits`: The maximum number of digits after the decimal point. + * Default is `0`. + * @param locale A locale code for the locale format rules to use. + * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. + * See [Setting your app locale](guide/i18n-common-locale-id). + */ + transform(value, digitsInfo, locale) { + if (!isValue(value)) + return null; + locale = locale || this._locale; + try { + const num = strToNumber(value); + return formatPercent(num, locale, digitsInfo); + } + catch (error) { + throw invalidPipeArgumentError(PercentPipe, error.message); + } + } + } + PercentPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PercentPipe, deps: [{ token: i0.LOCALE_ID }], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + PercentPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: PercentPipe, isStandalone: true, name: "percent" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PercentPipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'percent', + standalone: true, + }] + }], ctorParameters: function () { + return [{ type: undefined, decorators: [{ + type: i0.Inject, + args: [i0.LOCALE_ID] + }] }]; + } }); + /** + * @ngModule CommonModule + * @description + * + * Transforms a number to a currency string, formatted according to locale rules + * that determine group sizing and separator, decimal-point character, + * and other locale-specific configurations. + * + * {@a currency-code-deprecation} + *
    + * + * **Deprecation notice:** + * + * The default currency code is currently always `USD` but this is deprecated from v9. + * + * **In v11 the default currency code will be taken from the current locale identified by + * the `LOCALE_ID` token. See the [i18n guide](guide/i18n-common-locale-id) for + * more information.** + * + * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in + * your application `NgModule`: + * + * ```ts + * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'} + * ``` + * + *
    + * + * @see `getCurrencySymbol()` + * @see `formatCurrency()` + * + * @usageNotes + * The following code shows how the pipe transforms numbers + * into text strings, according to various format specifications, + * where the caller's default locale is `en-US`. + * + * + * + * @publicApi + */ + class CurrencyPipe { + constructor(_locale, _defaultCurrencyCode = 'USD') { + this._locale = _locale; + this._defaultCurrencyCode = _defaultCurrencyCode; + } + /** + * + * @param value The number to be formatted as currency. + * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, + * such as `USD` for the US dollar and `EUR` for the euro. The default currency code can be + * configured using the `DEFAULT_CURRENCY_CODE` injection token. + * @param display The format for the currency indicator. One of the following: + * - `code`: Show the code (such as `USD`). + * - `symbol`(default): Show the symbol (such as `$`). + * - `symbol-narrow`: Use the narrow symbol for locales that have two symbols for their + * currency. + * For example, the Canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`. If the + * locale has no narrow symbol, uses the standard symbol for the locale. + * - String: Use the given string value instead of a code or a symbol. + * For example, an empty string will suppress the currency & symbol. + * - Boolean (marked deprecated in v5): `true` for symbol and false for `code`. + * + * @param digitsInfo Decimal representation options, specified by a string + * in the following format:
    + * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. + * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. + * Default is `1`. + * - `minFractionDigits`: The minimum number of digits after the decimal point. + * Default is `2`. + * - `maxFractionDigits`: The maximum number of digits after the decimal point. + * Default is `2`. + * If not provided, the number will be formatted with the proper amount of digits, + * depending on what the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) specifies. + * For example, the Canadian dollar has 2 digits, whereas the Chilean peso has none. + * @param locale A locale code for the locale format rules to use. + * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. + * See [Setting your app locale](guide/i18n-common-locale-id). + */ + transform(value, currencyCode = this._defaultCurrencyCode, display = 'symbol', digitsInfo, locale) { + if (!isValue(value)) + return null; + locale = locale || this._locale; + if (typeof display === 'boolean') { + if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) { + console.warn(`Warning: the currency pipe has been changed in Angular v5. The symbolDisplay option (third parameter) is now a string instead of a boolean. The accepted values are "code", "symbol" or "symbol-narrow".`); + } + display = display ? 'symbol' : 'code'; + } + let currency = currencyCode || this._defaultCurrencyCode; + if (display !== 'code') { + if (display === 'symbol' || display === 'symbol-narrow') { + currency = getCurrencySymbol(currency, display === 'symbol' ? 'wide' : 'narrow', locale); + } + else { + currency = display; + } + } + try { + const num = strToNumber(value); + return formatCurrency(num, locale, currency, currencyCode, digitsInfo); + } + catch (error) { + throw invalidPipeArgumentError(CurrencyPipe, error.message); + } + } + } + CurrencyPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: CurrencyPipe, deps: [{ token: i0.LOCALE_ID }, { token: i0.DEFAULT_CURRENCY_CODE }], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + CurrencyPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: CurrencyPipe, isStandalone: true, name: "currency" }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: CurrencyPipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'currency', + standalone: true, + }] + }], ctorParameters: function () { + return [{ type: undefined, decorators: [{ + type: i0.Inject, + args: [i0.LOCALE_ID] + }] }, { type: undefined, decorators: [{ + type: i0.Inject, + args: [i0.DEFAULT_CURRENCY_CODE] + }] }]; + } }); + function isValue(value) { + return !(value == null || value === '' || value !== value); + } + /** + * Transforms a string into a number (if needed). + */ + function strToNumber(value) { + // Convert strings to numbers + if (typeof value === 'string' && !isNaN(Number(value) - parseFloat(value))) { + return Number(value); + } + if (typeof value !== 'number') { + throw new Error(`${value} is not a number`); + } + return value; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @ngModule CommonModule + * @description + * + * Creates a new `Array` or `String` containing a subset (slice) of the elements. + * + * @usageNotes + * + * All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()` + * and `String.prototype.slice()`. + * + * When operating on an `Array`, the returned `Array` is always a copy even when all + * the elements are being returned. + * + * When operating on a blank value, the pipe returns the blank value. + * + * ### List Example + * + * This `ngFor` example: + * + * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'} + * + * produces the following: + * + * ```html + *
  • b
  • + *
  • c
  • + * ``` + * + * ### String Examples + * + * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'} + * + * @publicApi + */ + class SlicePipe { + transform(value, start, end) { + if (value == null) + return null; + if (!this.supports(value)) { + throw invalidPipeArgumentError(SlicePipe, value); + } + return value.slice(start, end); + } + supports(obj) { + return typeof obj === 'string' || Array.isArray(obj); + } + } + SlicePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: SlicePipe, deps: [], target: i0__namespace.ɵɵFactoryTarget.Pipe }); + SlicePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: SlicePipe, isStandalone: true, name: "slice", pure: false }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: SlicePipe, decorators: [{ + type: i0.Pipe, + args: [{ + name: 'slice', + pure: false, + standalone: true, + }] + }] }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * A collection of Angular pipes that are likely to be used in each and every application. + */ + const COMMON_PIPES = [ + AsyncPipe, + UpperCasePipe, + LowerCasePipe, + JsonPipe, + SlicePipe, + DecimalPipe, + PercentPipe, + TitleCasePipe, + CurrencyPipe, + DatePipe, + I18nPluralPipe, + I18nSelectPipe, + KeyValuePipe, + ]; + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + // Note: This does not contain the location providers, + // as they need some platform specific implementations to work. + /** + * Exports all the basic Angular directives and pipes, + * such as `NgIf`, `NgForOf`, `DecimalPipe`, and so on. + * Re-exported by `BrowserModule`, which is included automatically in the root + * `AppModule` when you create a new app with the CLI `new` command. + * + * @publicApi + */ + class CommonModule { + } + CommonModule.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: CommonModule, deps: [], target: i0__namespace.ɵɵFactoryTarget.NgModule }); + CommonModule.ɵmod = i0__namespace.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0__namespace, type: CommonModule, imports: [NgClass, NgComponentOutlet, NgForOf, NgIf, NgTemplateOutlet, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgPlural, NgPluralCase, AsyncPipe, UpperCasePipe, LowerCasePipe, JsonPipe, SlicePipe, DecimalPipe, PercentPipe, TitleCasePipe, CurrencyPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, KeyValuePipe], exports: [NgClass, NgComponentOutlet, NgForOf, NgIf, NgTemplateOutlet, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgPlural, NgPluralCase, AsyncPipe, UpperCasePipe, LowerCasePipe, JsonPipe, SlicePipe, DecimalPipe, PercentPipe, TitleCasePipe, CurrencyPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, KeyValuePipe] }); + CommonModule.ɵinj = i0__namespace.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: CommonModule }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: CommonModule, decorators: [{ + type: i0.NgModule, + args: [{ + imports: [COMMON_DIRECTIVES, COMMON_PIPES], + exports: [COMMON_DIRECTIVES, COMMON_PIPES], + }] + }] }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + const PLATFORM_BROWSER_ID = 'browser'; + const PLATFORM_SERVER_ID = 'server'; + const PLATFORM_WORKER_APP_ID = 'browserWorkerApp'; + const PLATFORM_WORKER_UI_ID = 'browserWorkerUi'; + /** + * Returns whether a platform id represents a browser platform. + * @publicApi + */ + function isPlatformBrowser(platformId) { + return platformId === PLATFORM_BROWSER_ID; + } + /** + * Returns whether a platform id represents a server platform. + * @publicApi + */ + function isPlatformServer(platformId) { + return platformId === PLATFORM_SERVER_ID; + } + /** + * Returns whether a platform id represents a web worker app platform. + * @publicApi + */ + function isPlatformWorkerApp(platformId) { + return platformId === PLATFORM_WORKER_APP_ID; + } + /** + * Returns whether a platform id represents a web worker UI platform. + * @publicApi + */ + function isPlatformWorkerUi(platformId) { + return platformId === PLATFORM_WORKER_UI_ID; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @publicApi + */ + const VERSION = new i0.Version('15.0.1'); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Defines a scroll position manager. Implemented by `BrowserViewportScroller`. + * + * @publicApi + */ + class ViewportScroller { + } + // De-sugared tree-shakable injection + // See #23917 + /** @nocollapse */ + ViewportScroller.ɵprov = i0["ɵɵdefineInjectable"]({ + token: ViewportScroller, + providedIn: 'root', + factory: () => new BrowserViewportScroller(i0["ɵɵinject"](DOCUMENT), window) }); - } - /** - * Retrieves locale-specific day periods, which indicate roughly how a day is broken up - * in different languages. - * For example, for `en-US`, periods are morning, noon, afternoon, evening, and midnight. - * - * This functionality is only available when you have loaded the full locale data. - * See the ["I18n guide"](guide/i18n-common-format-data-locale). - * - * @param locale A locale code for the locale format rules to use. - * @param formStyle The required grammatical form. - * @param width The required character width. - * @returns The translated day-period strings. - * @see `getLocaleExtraDayPeriodRules()` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLocaleExtraDayPeriods(locale, formStyle, width) { - const data = i0['ɵfindLocaleData'](locale); - checkFullData(data); - const dayPeriodsData = [ - data[ - i0['ɵLocaleDataIndex'].ExtraData - ][0 /* ɵExtraLocaleDataIndex.ExtraDayPeriodFormats */], - data[ - i0['ɵLocaleDataIndex'].ExtraData - ][1 /* ɵExtraLocaleDataIndex.ExtraDayPeriodStandalone */], - ]; - const dayPeriods = getLastDefinedValue(dayPeriodsData, formStyle) || []; - return getLastDefinedValue(dayPeriods, width) || []; - } - /** - * Retrieves the writing direction of a specified locale - * @param locale A locale code for the locale format rules to use. - * @publicApi - * @returns 'rtl' or 'ltr' - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - */ - function getLocaleDirection(locale) { - const data = i0['ɵfindLocaleData'](locale); - return data[i0['ɵLocaleDataIndex'].Directionality]; - } - /** - * Retrieves the first value that is defined in an array, going backwards from an index position. - * - * To avoid repeating the same data (as when the "format" and "standalone" forms are the same) - * add the first value to the locale data arrays, and add other values only if they are different. - * - * @param data The data array to retrieve from. - * @param index A 0-based index into the array to start from. - * @returns The value immediately before the given index position. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getLastDefinedValue(data, index) { - for (let i = index; i > -1; i--) { - if (typeof data[i] !== 'undefined') { - return data[i]; - } - } - throw new Error('Locale data API: locale data undefined'); - } - /** - * Extracts the hours and minutes from a string like "15:45" - */ - function extractTime(time) { - const [h, m] = time.split(':'); - return { hours: +h, minutes: +m }; - } - /** - * Retrieves the currency symbol for a given currency code. - * - * For example, for the default `en-US` locale, the code `USD` can - * be represented by the narrow symbol `$` or the wide symbol `US$`. - * - * @param code The currency code. - * @param format The format, `wide` or `narrow`. - * @param locale A locale code for the locale format rules to use. - * - * @returns The symbol, or the currency code if no symbol is available. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getCurrencySymbol(code, format, locale = 'en') { - const currency = - getLocaleCurrencies(locale)[code] || CURRENCIES_EN[code] || []; - const symbolNarrow = currency[1 /* ɵCurrencyIndex.SymbolNarrow */]; - if (format === 'narrow' && typeof symbolNarrow === 'string') { - return symbolNarrow; - } - return currency[0 /* ɵCurrencyIndex.Symbol */] || code; - } - // Most currencies have cents, that's why the default is 2 - const DEFAULT_NB_OF_CURRENCY_DIGITS = 2; - /** - * Reports the number of decimal digits for a given currency. - * The value depends upon the presence of cents in that particular currency. - * - * @param code The currency code. - * @returns The number of decimal digits, typically 0 or 2. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function getNumberOfCurrencyDigits(code) { - let digits; - const currency = CURRENCIES_EN[code]; - if (currency) { - digits = currency[2 /* ɵCurrencyIndex.NbOfDigits */]; - } - return typeof digits === 'number' ? digits : DEFAULT_NB_OF_CURRENCY_DIGITS; - } + /** + * Manages the scroll position for a browser window. + */ + class BrowserViewportScroller { + constructor(document, window) { + this.document = document; + this.window = window; + this.offset = () => [0, 0]; + } + /** + * Configures the top offset used when scrolling to an anchor. + * @param offset A position in screen coordinates (a tuple with x and y values) + * or a function that returns the top offset position. + * + */ + setOffset(offset) { + if (Array.isArray(offset)) { + this.offset = () => offset; + } + else { + this.offset = offset; + } + } + /** + * Retrieves the current scroll position. + * @returns The position in screen coordinates. + */ + getScrollPosition() { + if (this.supportsScrolling()) { + return [this.window.pageXOffset, this.window.pageYOffset]; + } + else { + return [0, 0]; + } + } + /** + * Sets the scroll position. + * @param position The new position in screen coordinates. + */ + scrollToPosition(position) { + if (this.supportsScrolling()) { + this.window.scrollTo(position[0], position[1]); + } + } + /** + * Scrolls to an element and attempts to focus the element. + * + * Note that the function name here is misleading in that the target string may be an ID for a + * non-anchor element. + * + * @param target The ID of an element or name of the anchor. + * + * @see https://html.spec.whatwg.org/#the-indicated-part-of-the-document + * @see https://html.spec.whatwg.org/#scroll-to-fragid + */ + scrollToAnchor(target) { + if (!this.supportsScrolling()) { + return; + } + const elSelected = findAnchorFromDocument(this.document, target); + if (elSelected) { + this.scrollToElement(elSelected); + // After scrolling to the element, the spec dictates that we follow the focus steps for the + // target. Rather than following the robust steps, simply attempt focus. + // + // @see https://html.spec.whatwg.org/#get-the-focusable-area + // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus + // @see https://html.spec.whatwg.org/#focusable-area + elSelected.focus(); + } + } + /** + * Disables automatic scroll restoration provided by the browser. + */ + setHistoryScrollRestoration(scrollRestoration) { + if (this.supportScrollRestoration()) { + const history = this.window.history; + if (history && history.scrollRestoration) { + history.scrollRestoration = scrollRestoration; + } + } + } + /** + * Scrolls to an element using the native offset and the specified offset set on this scroller. + * + * The offset can be used when we know that there is a floating header and scrolling naively to an + * element (ex: `scrollIntoView`) leaves the element hidden behind the floating header. + */ + scrollToElement(el) { + const rect = el.getBoundingClientRect(); + const left = rect.left + this.window.pageXOffset; + const top = rect.top + this.window.pageYOffset; + const offset = this.offset(); + this.window.scrollTo(left - offset[0], top - offset[1]); + } + /** + * We only support scroll restoration when we can get a hold of window. + * This means that we do not support this behavior when running in a web worker. + * + * Lifting this restriction right now would require more changes in the dom adapter. + * Since webworkers aren't widely used, we will lift it once RouterScroller is + * battle-tested. + */ + supportScrollRestoration() { + try { + if (!this.supportsScrolling()) { + return false; + } + // The `scrollRestoration` property could be on the `history` instance or its prototype. + const scrollRestorationDescriptor = getScrollRestorationProperty(this.window.history) || + getScrollRestorationProperty(Object.getPrototypeOf(this.window.history)); + // We can write to the `scrollRestoration` property if it is a writable data field or it has a + // setter function. + return !!scrollRestorationDescriptor && + !!(scrollRestorationDescriptor.writable || scrollRestorationDescriptor.set); + } + catch (_a) { + return false; + } + } + supportsScrolling() { + try { + return !!this.window && !!this.window.scrollTo && 'pageXOffset' in this.window; + } + catch (_a) { + return false; + } + } + } + function getScrollRestorationProperty(obj) { + return Object.getOwnPropertyDescriptor(obj, 'scrollRestoration'); + } + function findAnchorFromDocument(document, target) { + const documentResult = document.getElementById(target) || document.getElementsByName(target)[0]; + if (documentResult) { + return documentResult; + } + // `getElementById` and `getElementsByName` won't pierce through the shadow DOM so we + // have to traverse the DOM manually and do the lookup through the shadow roots. + if (typeof document.createTreeWalker === 'function' && document.body && + (document.body.createShadowRoot || document.body.attachShadow)) { + const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT); + let currentNode = treeWalker.currentNode; + while (currentNode) { + const shadowRoot = currentNode.shadowRoot; + if (shadowRoot) { + // Note that `ShadowRoot` doesn't support `getElementsByName` + // so we have to fall back to `querySelector`. + const result = shadowRoot.getElementById(target) || shadowRoot.querySelector(`[name="${target}"]`); + if (result) { + return result; + } + } + currentNode = treeWalker.nextNode(); + } + } + return null; + } + /** + * Provides an empty implementation of the viewport scroller. + */ + class NullViewportScroller { + /** + * Empty implementation + */ + setOffset(offset) { } + /** + * Empty implementation + */ + getScrollPosition() { + return [0, 0]; + } + /** + * Empty implementation + */ + scrollToPosition(position) { } + /** + * Empty implementation + */ + scrollToAnchor(anchor) { } + /** + * Empty implementation + */ + setHistoryScrollRestoration(scrollRestoration) { } + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * A wrapper around the `XMLHttpRequest` constructor. + * + * @publicApi + */ + class XhrFactory { + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + // Converts a string that represents a URL into a URL class instance. + function getUrl(src, win) { + // Don't use a base URL is the URL is absolute. + return isAbsoluteUrl(src) ? new URL(src) : new URL(src, win.location.href); + } + // Checks whether a URL is absolute (i.e. starts with `http://` or `https://`). + function isAbsoluteUrl(src) { + return /^https?:\/\//.test(src); + } + // Given a URL, extract the hostname part. + // If a URL is a relative one - the URL is returned as is. + function extractHostname(url) { + return isAbsoluteUrl(url) ? (new URL(url)).hostname : url; + } + function isValidPath(path) { + const isString = typeof path === 'string'; + if (!isString || path.trim() === '') { + return false; + } + // Calling new URL() will throw if the path string is malformed + try { + const url = new URL(path); + return true; + } + catch (_a) { + return false; + } + } + function normalizePath(path) { + return path.endsWith('/') ? path.slice(0, -1) : path; + } + function normalizeSrc(src) { + return src.startsWith('/') ? src.slice(1) : src; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Noop image loader that does no transformation to the original src and just returns it as is. + * This loader is used as a default one if more specific logic is not provided in an app config. + * + * @see `ImageLoader` + * @see `NgOptimizedImage` + */ + const noopImageLoader = (config) => config.src; + /** + * Injection token that configures the image loader function. + * + * @see `ImageLoader` + * @see `NgOptimizedImage` + * @publicApi + */ + const IMAGE_LOADER = new i0.InjectionToken('ImageLoader', { + providedIn: 'root', + factory: () => noopImageLoader, + }); + /** + * Internal helper function that makes it easier to introduce custom image loaders for the + * `NgOptimizedImage` directive. It is enough to specify a URL builder function to obtain full DI + * configuration for a given loader: a DI token corresponding to the actual loader function, plus DI + * tokens managing preconnect check functionality. + * @param buildUrlFn a function returning a full URL based on loader's configuration + * @param exampleUrls example of full URLs for a given loader (used in error messages) + * @returns a set of DI providers corresponding to the configured image loader + */ + function createImageLoader(buildUrlFn, exampleUrls) { + return function provideImageLoader(path) { + if (!isValidPath(path)) { + throwInvalidPathError(path, exampleUrls || []); + } + // The trailing / is stripped (if provided) to make URL construction (concatenation) easier in + // the individual loader functions. + path = normalizePath(path); + const loaderFn = (config) => { + if (isAbsoluteUrl(config.src)) { + // Image loader functions expect an image file name (e.g. `my-image.png`) + // or a relative path + a file name (e.g. `/a/b/c/my-image.png`) as an input, + // so the final absolute URL can be constructed. + // When an absolute URL is provided instead - the loader can not + // build a final URL, thus the error is thrown to indicate that. + throwUnexpectedAbsoluteUrlError(path, config.src); + } + return buildUrlFn(path, Object.assign(Object.assign({}, config), { src: normalizeSrc(config.src) })); + }; + const providers = [{ provide: IMAGE_LOADER, useValue: loaderFn }]; + return providers; + }; + } + function throwInvalidPathError(path, exampleUrls) { + throw new i0["ɵRuntimeError"](2959 /* RuntimeErrorCode.INVALID_LOADER_ARGUMENTS */, ngDevMode && + `Image loader has detected an invalid path (\`${path}\`). ` + + `To fix this, supply a path using one of the following formats: ${exampleUrls.join(' or ')}`); + } + function throwUnexpectedAbsoluteUrlError(path, url) { + throw new i0["ɵRuntimeError"](2959 /* RuntimeErrorCode.INVALID_LOADER_ARGUMENTS */, ngDevMode && + `Image loader has detected a \`\` tag with an invalid \`ngSrc\` attribute: ${url}. ` + + `This image loader expects \`ngSrc\` to be a relative URL - ` + + `however the provided value is an absolute URL. ` + + `To fix this, provide \`ngSrc\` as a path relative to the base URL ` + + `configured for this loader (\`${path}\`).`); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Function that generates an ImageLoader for [Cloudflare Image + * Resizing](https://developers.cloudflare.com/images/image-resizing/) and turns it into an Angular + * provider. Note: Cloudflare has multiple image products - this provider is specifically for + * Cloudflare Image Resizing; it will not work with Cloudflare Images or Cloudflare Polish. + * + * @param path Your domain name, e.g. https://mysite.com + * @returns Provider that provides an ImageLoader function + * + * @publicApi + */ + const provideCloudflareLoader = createImageLoader(createCloudflareUrl, ngDevMode ? ['https:///cdn-cgi/image//'] : undefined); + function createCloudflareUrl(path, config) { + let params = `format=auto`; + if (config.width) { + params += `,width=${config.width}`; + } + // Cloudflare image URLs format: + // https://developers.cloudflare.com/images/image-resizing/url-format/ + return `${path}/cdn-cgi/image/${params}/${config.src}`; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Name and URL tester for Cloudinary. + */ + const cloudinaryLoaderInfo = { + name: 'Cloudinary', + testUrl: isCloudinaryUrl + }; + const CLOUDINARY_LOADER_REGEX = /https?\:\/\/[^\/]+\.cloudinary\.com\/.+/; + /** + * Tests whether a URL is from Cloudinary CDN. + */ + function isCloudinaryUrl(url) { + return CLOUDINARY_LOADER_REGEX.test(url); + } + /** + * Function that generates an ImageLoader for Cloudinary and turns it into an Angular provider. + * + * @param path Base URL of your Cloudinary images + * This URL should match one of the following formats: + * https://res.cloudinary.com/mysite + * https://mysite.cloudinary.com + * https://subdomain.mysite.com + * @returns Set of providers to configure the Cloudinary loader. + * + * @publicApi + */ + const provideCloudinaryLoader = createImageLoader(createCloudinaryUrl, ngDevMode ? + [ + 'https://res.cloudinary.com/mysite', 'https://mysite.cloudinary.com', + 'https://subdomain.mysite.com' + ] : + undefined); + function createCloudinaryUrl(path, config) { + // Cloudinary image URLformat: + // https://cloudinary.com/documentation/image_transformations#transformation_url_structure + // Example of a Cloudinary image URL: + // https://res.cloudinary.com/mysite/image/upload/c_scale,f_auto,q_auto,w_600/marketing/tile-topics-m.png + let params = `f_auto,q_auto`; // sets image format and quality to "auto" + if (config.width) { + params += `,w_${config.width}`; + } + return `${path}/image/upload/${params}/${config.src}`; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Name and URL tester for ImageKit. + */ + const imageKitLoaderInfo = { + name: 'ImageKit', + testUrl: isImageKitUrl + }; + const IMAGE_KIT_LOADER_REGEX = /https?\:\/\/[^\/]+\.imagekit\.io\/.+/; + /** + * Tests whether a URL is from ImageKit CDN. + */ + function isImageKitUrl(url) { + return IMAGE_KIT_LOADER_REGEX.test(url); + } + /** + * Function that generates an ImageLoader for ImageKit and turns it into an Angular provider. + * + * @param path Base URL of your ImageKit images + * This URL should match one of the following formats: + * https://ik.imagekit.io/myaccount + * https://subdomain.mysite.com + * @returns Set of providers to configure the ImageKit loader. + * + * @publicApi + */ + const provideImageKitLoader = createImageLoader(createImagekitUrl, ngDevMode ? ['https://ik.imagekit.io/mysite', 'https://subdomain.mysite.com'] : undefined); + function createImagekitUrl(path, config) { + // Example of an ImageKit image URL: + // https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg + let params = `tr:q-auto`; // applies the "auto quality" transformation + if (config.width) { + params += `,w-${config.width}`; + } + return `${path}/${params}/${config.src}`; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Name and URL tester for Imgix. + */ + const imgixLoaderInfo = { + name: 'Imgix', + testUrl: isImgixUrl + }; + const IMGIX_LOADER_REGEX = /https?\:\/\/[^\/]+\.imgix\.net\/.+/; + /** + * Tests whether a URL is from Imgix CDN. + */ + function isImgixUrl(url) { + return IMGIX_LOADER_REGEX.test(url); + } + /** + * Function that generates an ImageLoader for Imgix and turns it into an Angular provider. + * + * @param path path to the desired Imgix origin, + * e.g. https://somepath.imgix.net or https://images.mysite.com + * @returns Set of providers to configure the Imgix loader. + * + * @publicApi + */ + const provideImgixLoader = createImageLoader(createImgixUrl, ngDevMode ? ['https://somepath.imgix.net/'] : undefined); + function createImgixUrl(path, config) { + const url = new URL(`${path}/${config.src}`); + // This setting ensures the smallest allowable format is set. + url.searchParams.set('auto', 'format'); + if (config.width) { + url.searchParams.set('w', config.width.toString()); + } + return url.href; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + // Assembles directive details string, useful for error messages. + function imgDirectiveDetails(ngSrc, includeNgSrc = true) { + const ngSrcInfo = includeNgSrc ? `(activated on an element with the \`ngSrc="${ngSrc}"\`) ` : ''; + return `The NgOptimizedImage directive ${ngSrcInfo}has detected that`; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Asserts that the application is in development mode. Throws an error if the application is in + * production mode. This assert can be used to make sure that there is no dev-mode code invoked in + * the prod mode accidentally. + */ + function assertDevMode(checkName) { + if (!ngDevMode) { + throw new i0["ɵRuntimeError"](2958 /* RuntimeErrorCode.UNEXPECTED_DEV_MODE_CHECK_IN_PROD_MODE */, `Unexpected invocation of the ${checkName} in the prod mode. ` + + `Please make sure that the prod mode is enabled for production builds.`); + } + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Observer that detects whether an image with `NgOptimizedImage` + * is treated as a Largest Contentful Paint (LCP) element. If so, + * asserts that the image has the `priority` attribute. + * + * Note: this is a dev-mode only class and it does not appear in prod bundles, + * thus there is no `ngDevMode` use in the code. + * + * Based on https://web.dev/lcp/#measure-lcp-in-javascript. + */ + class LCPImageObserver { + constructor() { + // Map of full image URLs -> original `ngSrc` values. + this.images = new Map(); + // Keep track of images for which `console.warn` was produced. + this.alreadyWarned = new Set(); + this.window = null; + this.observer = null; + assertDevMode('LCP checker'); + const win = i0.inject(DOCUMENT).defaultView; + if (typeof win !== 'undefined' && typeof PerformanceObserver !== 'undefined') { + this.window = win; + this.observer = this.initPerformanceObserver(); + } + } + /** + * Inits PerformanceObserver and subscribes to LCP events. + * Based on https://web.dev/lcp/#measure-lcp-in-javascript + */ + initPerformanceObserver() { + const observer = new PerformanceObserver((entryList) => { + var _a, _b; + const entries = entryList.getEntries(); + if (entries.length === 0) + return; + // We use the latest entry produced by the `PerformanceObserver` as the best + // signal on which element is actually an LCP one. As an example, the first image to load on + // a page, by virtue of being the only thing on the page so far, is often a LCP candidate + // and gets reported by PerformanceObserver, but isn't necessarily the LCP element. + const lcpElement = entries[entries.length - 1]; + // Cast to `any` due to missing `element` on the `LargestContentfulPaint` type of entry. + // See https://developer.mozilla.org/en-US/docs/Web/API/LargestContentfulPaint + const imgSrc = (_b = (_a = lcpElement.element) === null || _a === void 0 ? void 0 : _a.src) !== null && _b !== void 0 ? _b : ''; + // Exclude `data:` and `blob:` URLs, since they are not supported by the directive. + if (imgSrc.startsWith('data:') || imgSrc.startsWith('blob:')) + return; + const imgNgSrc = this.images.get(imgSrc); + if (imgNgSrc && !this.alreadyWarned.has(imgSrc)) { + this.alreadyWarned.add(imgSrc); + logMissingPriorityWarning(imgSrc); + } + }); + observer.observe({ type: 'largest-contentful-paint', buffered: true }); + return observer; + } + registerImage(rewrittenSrc, originalNgSrc) { + if (!this.observer) + return; + this.images.set(getUrl(rewrittenSrc, this.window).href, originalNgSrc); + } + unregisterImage(rewrittenSrc) { + if (!this.observer) + return; + this.images.delete(getUrl(rewrittenSrc, this.window).href); + } + ngOnDestroy() { + if (!this.observer) + return; + this.observer.disconnect(); + this.images.clear(); + this.alreadyWarned.clear(); + } + } + LCPImageObserver.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: LCPImageObserver, deps: [], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + LCPImageObserver.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: LCPImageObserver, providedIn: 'root' }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: LCPImageObserver, decorators: [{ + type: i0.Injectable, + args: [{ providedIn: 'root' }] + }], ctorParameters: function () { return []; } }); + function logMissingPriorityWarning(ngSrc) { + const directiveDetails = imgDirectiveDetails(ngSrc); + console.warn(i0["ɵformatRuntimeError"](2955 /* RuntimeErrorCode.LCP_IMG_MISSING_PRIORITY */, `${directiveDetails} this image is the Largest Contentful Paint (LCP) ` + + `element but was not marked "priority". This image should be marked ` + + `"priority" in order to prioritize its loading. ` + + `To fix this, add the "priority" attribute.`)); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + // Set of origins that are always excluded from the preconnect checks. + const INTERNAL_PRECONNECT_CHECK_BLOCKLIST = new Set(['localhost', '127.0.0.1', '0.0.0.0']); + /** + * Injection token to configure which origins should be excluded + * from the preconnect checks. It can either be a single string or an array of strings + * to represent a group of origins, for example: + * + * ```typescript + * {provide: PRECONNECT_CHECK_BLOCKLIST, useValue: 'https://your-domain.com'} + * ``` + * + * or: + * + * ```typescript + * {provide: PRECONNECT_CHECK_BLOCKLIST, + * useValue: ['https://your-domain-1.com', 'https://your-domain-2.com']} + * ``` + * + * @publicApi + */ + const PRECONNECT_CHECK_BLOCKLIST = new i0.InjectionToken('PRECONNECT_CHECK_BLOCKLIST'); + /** + * Contains the logic to detect whether an image, marked with the "priority" attribute + * has a corresponding `` tag in the `document.head`. + * + * Note: this is a dev-mode only class, which should not appear in prod bundles, + * thus there is no `ngDevMode` use in the code. + */ + class PreconnectLinkChecker { + constructor() { + this.document = i0.inject(DOCUMENT); + /** + * Set of tags found on this page. + * The `null` value indicates that there was no DOM query operation performed. + */ + this.preconnectLinks = null; + /* + * Keep track of all already seen origin URLs to avoid repeating the same check. + */ + this.alreadySeen = new Set(); + this.window = null; + this.blocklist = new Set(INTERNAL_PRECONNECT_CHECK_BLOCKLIST); + assertDevMode('preconnect link checker'); + const win = this.document.defaultView; + if (typeof win !== 'undefined') { + this.window = win; + } + const blocklist = i0.inject(PRECONNECT_CHECK_BLOCKLIST, { optional: true }); + if (blocklist) { + this.populateBlocklist(blocklist); + } + } + populateBlocklist(origins) { + if (Array.isArray(origins)) { + deepForEach(origins, origin => { + this.blocklist.add(extractHostname(origin)); + }); + } + else { + this.blocklist.add(extractHostname(origins)); + } + } + /** + * Checks that a preconnect resource hint exists in the head fo rthe + * given src. + * + * @param rewrittenSrc src formatted with loader + * @param originalNgSrc ngSrc value + */ + assertPreconnect(rewrittenSrc, originalNgSrc) { + if (!this.window) + return; + const imgUrl = getUrl(rewrittenSrc, this.window); + if (this.blocklist.has(imgUrl.hostname) || this.alreadySeen.has(imgUrl.origin)) + return; + // Register this origin as seen, so we don't check it again later. + this.alreadySeen.add(imgUrl.origin); + if (!this.preconnectLinks) { + // Note: we query for preconnect links only *once* and cache the results + // for the entire lifespan of an application, since it's unlikely that the + // list would change frequently. This allows to make sure there are no + // performance implications of making extra DOM lookups for each image. + this.preconnectLinks = this.queryPreconnectLinks(); + } + if (!this.preconnectLinks.has(imgUrl.origin)) { + console.warn(i0["ɵformatRuntimeError"](2956 /* RuntimeErrorCode.PRIORITY_IMG_MISSING_PRECONNECT_TAG */, `${imgDirectiveDetails(originalNgSrc)} there is no preconnect tag present for this ` + + `image. Preconnecting to the origin(s) that serve priority images ensures that these ` + + `images are delivered as soon as possible. To fix this, please add the following ` + + `element into the of the document:\n` + + ` `)); + } + } + queryPreconnectLinks() { + const preconnectUrls = new Set(); + const selector = 'link[rel=preconnect]'; + const links = Array.from(this.document.querySelectorAll(selector)); + for (let link of links) { + const url = getUrl(link.href, this.window); + preconnectUrls.add(url.origin); + } + return preconnectUrls; + } + ngOnDestroy() { + var _a; + (_a = this.preconnectLinks) === null || _a === void 0 ? void 0 : _a.clear(); + this.alreadySeen.clear(); + } + } + PreconnectLinkChecker.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PreconnectLinkChecker, deps: [], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + PreconnectLinkChecker.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PreconnectLinkChecker, providedIn: 'root' }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PreconnectLinkChecker, decorators: [{ + type: i0.Injectable, + args: [{ providedIn: 'root' }] + }], ctorParameters: function () { return []; } }); + /** + * Invokes a callback for each element in the array. Also invokes a callback + * recursively for each nested array. + */ + function deepForEach(input, fn) { + for (let value of input) { + Array.isArray(value) ? deepForEach(value, fn) : fn(value); + } + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * In SSR scenarios, a preload `` element is generated for priority images. + * Having a large number of preload tags may negatively affect the performance, + * so we warn developers (by throwing an error) if the number of preloaded images + * is above a certain threshold. This const specifies this threshold. + */ + const DEFAULT_PRELOADED_IMAGES_LIMIT = 5; + /** + * Helps to keep track of priority images that already have a corresponding + * preload tag (to avoid generating multiple preload tags with the same URL). + * + * This Set tracks the original src passed into the `ngSrc` input not the src after it has been + * run through the specified `IMAGE_LOADER`. + */ + const PRELOADED_IMAGES = new i0.InjectionToken('NG_OPTIMIZED_PRELOADED_IMAGES', { providedIn: 'root', factory: () => new Set() }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * @description Contains the logic needed to track and add preload link tags to the `` tag. It + * will also track what images have already had preload link tags added so as to not duplicate link + * tags. + * + * In dev mode this service will validate that the number of preloaded images does not exceed the + * configured default preloaded images limit: {@link DEFAULT_PRELOADED_IMAGES_LIMIT}. + */ + class PreloadLinkCreator { + constructor() { + this.preloadedImages = i0.inject(PRELOADED_IMAGES); + this.document = i0.inject(DOCUMENT); + } + /** + * @description Add a preload `` to the `` of the `index.html` that is served from the + * server while using Angular Universal and SSR to kick off image loads for high priority images. + * + * The `sizes` (passed in from the user) and `srcset` (parsed and formatted from `ngSrcset`) + * properties used to set the corresponding attributes, `imagesizes` and `imagesrcset` + * respectively, on the preload `` tag so that the correctly sized image is preloaded from + * the CDN. + * + * {@link https://web.dev/preload-responsive-images/#imagesrcset-and-imagesizes} + * + * @param renderer The `Renderer2` passed in from the directive + * @param src The original src of the image that is set on the `ngSrc` input. + * @param srcset The parsed and formatted srcset created from the `ngSrcset` input + * @param sizes The value of the `sizes` attribute passed in to the `` tag + */ + createPreloadLinkTag(renderer, src, srcset, sizes) { + if (ngDevMode) { + if (this.preloadedImages.size >= DEFAULT_PRELOADED_IMAGES_LIMIT) { + throw new i0["ɵRuntimeError"](2961 /* RuntimeErrorCode.TOO_MANY_PRELOADED_IMAGES */, ngDevMode && + `The \`NgOptimizedImage\` directive has detected that more than ` + + `${DEFAULT_PRELOADED_IMAGES_LIMIT} images were marked as priority. ` + + `This might negatively affect an overall performance of the page. ` + + `To fix this, remove the "priority" attribute from images with less priority.`); + } + } + if (this.preloadedImages.has(src)) { + return; + } + this.preloadedImages.add(src); + const preload = renderer.createElement('link'); + renderer.setAttribute(preload, 'as', 'image'); + renderer.setAttribute(preload, 'href', src); + renderer.setAttribute(preload, 'rel', 'preload'); + renderer.setAttribute(preload, 'fetchpriority', 'high'); + if (sizes) { + renderer.setAttribute(preload, 'imageSizes', sizes); + } + if (srcset) { + renderer.setAttribute(preload, 'imageSrcset', srcset); + } + renderer.appendChild(this.document.head, preload); + } + } + PreloadLinkCreator.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PreloadLinkCreator, deps: [], target: i0__namespace.ɵɵFactoryTarget.Injectable }); + PreloadLinkCreator.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PreloadLinkCreator, providedIn: 'root' }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: PreloadLinkCreator, decorators: [{ + type: i0.Injectable, + args: [{ providedIn: 'root' }] + }] }); - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - const ISO8601_DATE_REGEX = - /^(\d{4,})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; - // 1 2 3 4 5 6 7 8 9 10 11 - const NAMED_FORMATS = {}; - const DATE_FORMATS_SPLIT = - /((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; - var ZoneWidth; - (function (ZoneWidth) { - ZoneWidth[(ZoneWidth['Short'] = 0)] = 'Short'; - ZoneWidth[(ZoneWidth['ShortGMT'] = 1)] = 'ShortGMT'; - ZoneWidth[(ZoneWidth['Long'] = 2)] = 'Long'; - ZoneWidth[(ZoneWidth['Extended'] = 3)] = 'Extended'; - })(ZoneWidth || (ZoneWidth = {})); - var DateType; - (function (DateType) { - DateType[(DateType['FullYear'] = 0)] = 'FullYear'; - DateType[(DateType['Month'] = 1)] = 'Month'; - DateType[(DateType['Date'] = 2)] = 'Date'; - DateType[(DateType['Hours'] = 3)] = 'Hours'; - DateType[(DateType['Minutes'] = 4)] = 'Minutes'; - DateType[(DateType['Seconds'] = 5)] = 'Seconds'; - DateType[(DateType['FractionalSeconds'] = 6)] = 'FractionalSeconds'; - DateType[(DateType['Day'] = 7)] = 'Day'; - })(DateType || (DateType = {})); - var TranslationType; - (function (TranslationType) { - TranslationType[(TranslationType['DayPeriods'] = 0)] = 'DayPeriods'; - TranslationType[(TranslationType['Days'] = 1)] = 'Days'; - TranslationType[(TranslationType['Months'] = 2)] = 'Months'; - TranslationType[(TranslationType['Eras'] = 3)] = 'Eras'; - })(TranslationType || (TranslationType = {})); - /** - * @ngModule CommonModule - * @description - * - * Formats a date according to locale rules. - * - * @param value The date to format, as a Date, or a number (milliseconds since UTC epoch) - * or an [ISO date-time string](https://www.w3.org/TR/NOTE-datetime). - * @param format The date-time components to include. See `DatePipe` for details. - * @param locale A locale code for the locale format rules to use. - * @param timezone The time zone. A time zone offset from GMT (such as `'+0430'`), - * or a standard UTC/GMT or continental US time zone abbreviation. - * If not specified, uses host system settings. - * - * @returns The formatted date string. - * - * @see `DatePipe` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function formatDate(value, format, locale, timezone) { - let date = toDate(value); - const namedFormat = getNamedFormat(locale, format); - format = namedFormat || format; - let parts = []; - let match; - while (format) { - match = DATE_FORMATS_SPLIT.exec(format); - if (match) { - parts = parts.concat(match.slice(1)); - const part = parts.pop(); - if (!part) { - break; - } - format = part; - } else { - parts.push(format); - break; - } - } - let dateTimezoneOffset = date.getTimezoneOffset(); - if (timezone) { - dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); - date = convertTimezoneToLocal(date, timezone, true); - } - let text = ''; - parts.forEach((value) => { - const dateFormatter = getDateFormatter(value); - text += dateFormatter - ? dateFormatter(date, locale, dateTimezoneOffset) - : value === "''" - ? "'" - : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); - }); - return text; - } - /** - * Create a new Date object with the given date value, and the time set to midnight. - * - * We cannot use `new Date(year, month, date)` because it maps years between 0 and 99 to 1900-1999. - * See: https://github.com/angular/angular/issues/40377 - * - * Note that this function returns a Date object whose time is midnight in the current locale's - * timezone. In the future we might want to change this to be midnight in UTC, but this would be a - * considerable breaking change. - */ - function createDate(year, month, date) { - // The `newDate` is set to midnight (UTC) on January 1st 1970. - // - In PST this will be December 31st 1969 at 4pm. - // - In GMT this will be January 1st 1970 at 1am. - // Note that they even have different years, dates and months! - const newDate = new Date(0); - // `setFullYear()` allows years like 0001 to be set correctly. This function does not - // change the internal time of the date. - // Consider calling `setFullYear(2019, 8, 20)` (September 20, 2019). - // - In PST this will now be September 20, 2019 at 4pm - // - In GMT this will now be September 20, 2019 at 1am - newDate.setFullYear(year, month, date); - // We want the final date to be at local midnight, so we reset the time. - // - In PST this will now be September 20, 2019 at 12am - // - In GMT this will now be September 20, 2019 at 12am - newDate.setHours(0, 0, 0); - return newDate; - } - function getNamedFormat(locale, format) { - const localeId = getLocaleId(locale); - NAMED_FORMATS[localeId] = NAMED_FORMATS[localeId] || {}; - if (NAMED_FORMATS[localeId][format]) { - return NAMED_FORMATS[localeId][format]; - } - let formatValue = ''; - switch (format) { - case 'shortDate': - formatValue = getLocaleDateFormat(locale, exports.FormatWidth.Short); - break; - case 'mediumDate': - formatValue = getLocaleDateFormat(locale, exports.FormatWidth.Medium); - break; - case 'longDate': - formatValue = getLocaleDateFormat(locale, exports.FormatWidth.Long); - break; - case 'fullDate': - formatValue = getLocaleDateFormat(locale, exports.FormatWidth.Full); - break; - case 'shortTime': - formatValue = getLocaleTimeFormat(locale, exports.FormatWidth.Short); - break; - case 'mediumTime': - formatValue = getLocaleTimeFormat(locale, exports.FormatWidth.Medium); - break; - case 'longTime': - formatValue = getLocaleTimeFormat(locale, exports.FormatWidth.Long); - break; - case 'fullTime': - formatValue = getLocaleTimeFormat(locale, exports.FormatWidth.Full); - break; - case 'short': - const shortTime = getNamedFormat(locale, 'shortTime'); - const shortDate = getNamedFormat(locale, 'shortDate'); - formatValue = formatDateTime( - getLocaleDateTimeFormat(locale, exports.FormatWidth.Short), - [shortTime, shortDate] - ); - break; - case 'medium': - const mediumTime = getNamedFormat(locale, 'mediumTime'); - const mediumDate = getNamedFormat(locale, 'mediumDate'); - formatValue = formatDateTime( - getLocaleDateTimeFormat(locale, exports.FormatWidth.Medium), - [mediumTime, mediumDate] - ); - break; - case 'long': - const longTime = getNamedFormat(locale, 'longTime'); - const longDate = getNamedFormat(locale, 'longDate'); - formatValue = formatDateTime( - getLocaleDateTimeFormat(locale, exports.FormatWidth.Long), - [longTime, longDate] - ); - break; - case 'full': - const fullTime = getNamedFormat(locale, 'fullTime'); - const fullDate = getNamedFormat(locale, 'fullDate'); - formatValue = formatDateTime( - getLocaleDateTimeFormat(locale, exports.FormatWidth.Full), - [fullTime, fullDate] - ); - break; - } - if (formatValue) { - NAMED_FORMATS[localeId][format] = formatValue; - } - return formatValue; - } - function formatDateTime(str, opt_values) { - if (opt_values) { - str = str.replace(/\{([^}]+)}/g, function (match, key) { - return opt_values != null && key in opt_values - ? opt_values[key] - : match; - }); - } - return str; - } - function padNumber(num, digits, minusSign = '-', trim, negWrap) { - let neg = ''; - if (num < 0 || (negWrap && num <= 0)) { - if (negWrap) { - num = -num + 1; - } else { - num = -num; - neg = minusSign; - } - } - let strNum = String(num); - while (strNum.length < digits) { - strNum = '0' + strNum; - } - if (trim) { - strNum = strNum.slice(strNum.length - digits); - } - return neg + strNum; - } - function formatFractionalSeconds(milliseconds, digits) { - const strMs = padNumber(milliseconds, 3); - return strMs.substring(0, digits); - } - /** - * Returns a date formatter that transforms a date into its locale digit representation - */ - function dateGetter(name, size, offset = 0, trim = false, negWrap = false) { - return function (date, locale) { - let part = getDatePart(name, date); - if (offset > 0 || part > -offset) { - part += offset; - } - if (name === DateType.Hours) { - if (part === 0 && offset === -12) { - part = 12; - } - } else if (name === DateType.FractionalSeconds) { - return formatFractionalSeconds(part, size); - } - const localeMinus = getLocaleNumberSymbol( - locale, - exports.NumberSymbol.MinusSign - ); - return padNumber(part, size, localeMinus, trim, negWrap); - }; - } - function getDatePart(part, date) { - switch (part) { - case DateType.FullYear: - return date.getFullYear(); - case DateType.Month: - return date.getMonth(); - case DateType.Date: - return date.getDate(); - case DateType.Hours: - return date.getHours(); - case DateType.Minutes: - return date.getMinutes(); - case DateType.Seconds: - return date.getSeconds(); - case DateType.FractionalSeconds: - return date.getMilliseconds(); - case DateType.Day: - return date.getDay(); - default: - throw new Error(`Unknown DateType value "${part}".`); - } - } - /** - * Returns a date formatter that transforms a date into its locale string representation - */ - function dateStrGetter( - name, - width, - form = exports.FormStyle.Format, - extended = false - ) { - return function (date, locale) { - return getDateTranslation(date, locale, name, width, form, extended); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * When a Base64-encoded image is passed as an input to the `NgOptimizedImage` directive, + * an error is thrown. The image content (as a string) might be very long, thus making + * it hard to read an error message if the entire string is included. This const defines + * the number of characters that should be included into the error message. The rest + * of the content is truncated. + */ + const BASE64_IMG_MAX_LENGTH_IN_ERROR = 50; + /** + * RegExpr to determine whether a src in a srcset is using width descriptors. + * Should match something like: "100w, 200w". + */ + const VALID_WIDTH_DESCRIPTOR_SRCSET = /^((\s*\d+w\s*(,|$)){1,})$/; + /** + * RegExpr to determine whether a src in a srcset is using density descriptors. + * Should match something like: "1x, 2x, 50x". Also supports decimals like "1.5x, 1.50x". + */ + const VALID_DENSITY_DESCRIPTOR_SRCSET = /^((\s*\d+(\.\d+)?x\s*(,|$)){1,})$/; + /** + * Srcset values with a density descriptor higher than this value will actively + * throw an error. Such densities are not permitted as they cause image sizes + * to be unreasonably large and slow down LCP. + */ + const ABSOLUTE_SRCSET_DENSITY_CAP = 3; + /** + * Used only in error message text to communicate best practices, as we will + * only throw based on the slightly more conservative ABSOLUTE_SRCSET_DENSITY_CAP. + */ + const RECOMMENDED_SRCSET_DENSITY_CAP = 2; + /** + * Used in generating automatic density-based srcsets + */ + const DENSITY_SRCSET_MULTIPLIERS = [1, 2]; + /** + * Used to determine which breakpoints to use on full-width images + */ + const VIEWPORT_BREAKPOINT_CUTOFF = 640; + /** + * Used to determine whether two aspect ratios are similar in value. + */ + const ASPECT_RATIO_TOLERANCE = .1; + /** + * Used to determine whether the image has been requested at an overly + * large size compared to the actual rendered image size (after taking + * into account a typical device pixel ratio). In pixels. + */ + const OVERSIZED_IMAGE_TOLERANCE = 1000; + /** Info about built-in loaders we can test for. */ + const BUILT_IN_LOADERS = [imgixLoaderInfo, imageKitLoaderInfo, cloudinaryLoaderInfo]; + const defaultConfig = { + breakpoints: [16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840], }; - } - /** - * Returns the locale translation of a date for a given form, type and width - */ - function getDateTranslation(date, locale, name, width, form, extended) { - switch (name) { - case TranslationType.Months: - return getLocaleMonthNames(locale, form, width)[date.getMonth()]; - case TranslationType.Days: - return getLocaleDayNames(locale, form, width)[date.getDay()]; - case TranslationType.DayPeriods: - const currentHours = date.getHours(); - const currentMinutes = date.getMinutes(); - if (extended) { - const rules = getLocaleExtraDayPeriodRules(locale); - const dayPeriods = getLocaleExtraDayPeriods(locale, form, width); - const index = rules.findIndex((rule) => { - if (Array.isArray(rule)) { - // morning, afternoon, evening, night - const [from, to] = rule; - const afterFrom = - currentHours >= from.hours && currentMinutes >= from.minutes; - const beforeTo = - currentHours < to.hours || - (currentHours === to.hours && currentMinutes < to.minutes); - // We must account for normal rules that span a period during the day (e.g. 6am-9am) - // where `from` is less (earlier) than `to`. But also rules that span midnight (e.g. - // 10pm - 5am) where `from` is greater (later!) than `to`. - // - // In the first case the current time must be BOTH after `from` AND before `to` - // (e.g. 8am is after 6am AND before 10am). - // - // In the second case the current time must be EITHER after `from` OR before `to` - // (e.g. 4am is before 5am but not after 10pm; and 11pm is not before 5am but it is - // after 10pm). - if (from.hours < to.hours) { - if (afterFrom && beforeTo) { - return true; + /** + * Injection token that configures the image optimized image functionality. + * + * @see `NgOptimizedImage` + * @publicApi + * @developerPreview + */ + const IMAGE_CONFIG = new i0.InjectionToken('ImageConfig', { providedIn: 'root', factory: () => defaultConfig }); + /** + * Directive that improves image loading performance by enforcing best practices. + * + * `NgOptimizedImage` ensures that the loading of the Largest Contentful Paint (LCP) image is + * prioritized by: + * - Automatically setting the `fetchpriority` attribute on the `` tag + * - Lazy loading non-priority images by default + * - Asserting that there is a corresponding preconnect link tag in the document head + * + * In addition, the directive: + * - Generates appropriate asset URLs if a corresponding `ImageLoader` function is provided + * - Automatically generates a srcset + * - Requires that `width` and `height` are set + * - Warns if `width` or `height` have been set incorrectly + * - Warns if the image will be visually distorted when rendered + * + * @usageNotes + * The `NgOptimizedImage` directive is marked as [standalone](guide/standalone-components) and can + * be imported directly. + * + * Follow the steps below to enable and use the directive: + * 1. Import it into the necessary NgModule or a standalone Component. + * 2. Optionally provide an `ImageLoader` if you use an image hosting service. + * 3. Update the necessary `` tags in templates and replace `src` attributes with `ngSrc`. + * Using a `ngSrc` allows the directive to control when the `src` gets set, which triggers an image + * download. + * + * Step 1: import the `NgOptimizedImage` directive. + * + * ```typescript + * import { NgOptimizedImage } from '@angular/common'; + * + * // Include it into the necessary NgModule + * @NgModule({ + * imports: [NgOptimizedImage], + * }) + * class AppModule {} + * + * // ... or a standalone Component + * @Component({ + * standalone: true + * imports: [NgOptimizedImage], + * }) + * class MyStandaloneComponent {} + * ``` + * + * Step 2: configure a loader. + * + * To use the **default loader**: no additional code changes are necessary. The URL returned by the + * generic loader will always match the value of "src". In other words, this loader applies no + * transformations to the resource URL and the value of the `ngSrc` attribute will be used as is. + * + * To use an existing loader for a **third-party image service**: add the provider factory for your + * chosen service to the `providers` array. In the example below, the Imgix loader is used: + * + * ```typescript + * import {provideImgixLoader} from '@angular/common'; + * + * // Call the function and add the result to the `providers` array: + * providers: [ + * provideImgixLoader("https://my.base.url/"), + * ], + * ``` + * + * The `NgOptimizedImage` directive provides the following functions: + * - `provideCloudflareLoader` + * - `provideCloudinaryLoader` + * - `provideImageKitLoader` + * - `provideImgixLoader` + * + * If you use a different image provider, you can create a custom loader function as described + * below. + * + * To use a **custom loader**: provide your loader function as a value for the `IMAGE_LOADER` DI + * token. + * + * ```typescript + * import {IMAGE_LOADER, ImageLoaderConfig} from '@angular/common'; + * + * // Configure the loader using the `IMAGE_LOADER` token. + * providers: [ + * { + * provide: IMAGE_LOADER, + * useValue: (config: ImageLoaderConfig) => { + * return `https://example.com/${config.src}-${config.width}.jpg}`; + * } + * }, + * ], + * ``` + * + * Step 3: update `` tags in templates to use `ngSrc` instead of `src`. + * + * ``` + * + * ``` + * + * @publicApi + */ + class NgOptimizedImage { + constructor() { + this.imageLoader = i0.inject(IMAGE_LOADER); + this.config = processConfig(i0.inject(IMAGE_CONFIG)); + this.renderer = i0.inject(i0.Renderer2); + this.imgElement = i0.inject(i0.ElementRef).nativeElement; + this.injector = i0.inject(i0.Injector); + this.isServer = isPlatformServer(i0.inject(i0.PLATFORM_ID)); + this.preloadLinkChecker = i0.inject(PreloadLinkCreator); + // a LCP image observer - should be injected only in the dev mode + this.lcpObserver = ngDevMode ? this.injector.get(LCPImageObserver) : null; + /** + * Calculate the rewritten `src` once and store it. + * This is needed to avoid repetitive calculations and make sure the directive cleanup in the + * `ngOnDestroy` does not rely on the `IMAGE_LOADER` logic (which in turn can rely on some other + * instance that might be already destroyed). + */ + this._renderedSrc = null; + this._priority = false; + this._disableOptimizedSrcset = false; + this._fill = false; + } + /** + * For responsive images: the intrinsic width of the image in pixels. + * For fixed size images: the desired rendered width of the image in pixels. + */ + set width(value) { + ngDevMode && assertGreaterThanZero(this, value, 'width'); + this._width = inputToInteger(value); + } + get width() { + return this._width; + } + /** + * For responsive images: the intrinsic height of the image in pixels. + * For fixed size images: the desired rendered height of the image in pixels.* The intrinsic + * height of the image in pixels. + */ + set height(value) { + ngDevMode && assertGreaterThanZero(this, value, 'height'); + this._height = inputToInteger(value); + } + get height() { + return this._height; + } + /** + * Indicates whether this image should have a high priority. + */ + set priority(value) { + this._priority = inputToBoolean(value); + } + get priority() { + return this._priority; + } + /** + * Disables automatic srcset generation for this image. + */ + set disableOptimizedSrcset(value) { + this._disableOptimizedSrcset = inputToBoolean(value); + } + get disableOptimizedSrcset() { + return this._disableOptimizedSrcset; + } + /** + * Sets the image to "fill mode", which eliminates the height/width requirement and adds + * styles such that the image fills its containing element. + * + * @developerPreview + */ + set fill(value) { + this._fill = inputToBoolean(value); + } + get fill() { + return this._fill; + } + /** @nodoc */ + ngOnInit() { + if (ngDevMode) { + assertNonEmptyInput(this, 'ngSrc', this.ngSrc); + assertValidNgSrcset(this, this.ngSrcset); + assertNoConflictingSrc(this); + if (this.ngSrcset) { + assertNoConflictingSrcset(this); + } + assertNotBase64Image(this); + assertNotBlobUrl(this); + if (this.fill) { + assertEmptyWidthAndHeight(this); + assertNonZeroRenderedHeight(this, this.imgElement, this.renderer); + } + else { + assertNonEmptyWidthAndHeight(this); + // Only check for distorted images when not in fill mode, where + // images may be intentionally stretched, cropped or letterboxed. + assertNoImageDistortion(this, this.imgElement, this.renderer); + } + assertValidLoadingInput(this); + if (!this.ngSrcset) { + assertNoComplexSizes(this); + } + assertNotMissingBuiltInLoader(this.ngSrc, this.imageLoader); + if (this.priority) { + const checker = this.injector.get(PreconnectLinkChecker); + checker.assertPreconnect(this.getRewrittenSrc(), this.ngSrc); + } + else { + // Monitor whether an image is an LCP element only in case + // the `priority` attribute is missing. Otherwise, an image + // has the necessary settings and no extra checks are required. + if (this.lcpObserver !== null) { + const ngZone = this.injector.get(i0.NgZone); + ngZone.runOutsideAngular(() => { + this.lcpObserver.registerImage(this.getRewrittenSrc(), this.ngSrc); + }); + } } - } else if (afterFrom || beforeTo) { - return true; - } - } else { - // noon or midnight - if ( - rule.hours === currentHours && - rule.minutes === currentMinutes - ) { - return true; - } } - return false; - }); - if (index !== -1) { - return dayPeriods[index]; - } - } - // if no rules for the day periods, we use am/pm by default - return getLocaleDayPeriods( - locale, - form, - width - )[currentHours < 12 ? 0 : 1]; - case TranslationType.Eras: - return getLocaleEraNames( - locale, - width - )[date.getFullYear() <= 0 ? 0 : 1]; - default: - // This default case is not needed by TypeScript compiler, as the switch is exhaustive. - // However Closure Compiler does not understand that and reports an error in typed mode. - // The `throw new Error` below works around the problem, and the unexpected: never variable - // makes sure tsc still checks this code is unreachable. - const unexpected = name; - throw new Error(`unexpected translation type ${unexpected}`); - } - } - /** - * Returns a date formatter that transforms a date and an offset into a timezone with ISO8601 or - * GMT format depending on the width (eg: short = +0430, short:GMT = GMT+4, long = GMT+04:30, - * extended = +04:30) - */ - function timeZoneGetter(width) { - return function (date, locale, offset) { - const zone = -1 * offset; - const minusSign = getLocaleNumberSymbol( - locale, - exports.NumberSymbol.MinusSign - ); - const hours = zone > 0 ? Math.floor(zone / 60) : Math.ceil(zone / 60); - switch (width) { - case ZoneWidth.Short: - return ( - (zone >= 0 ? '+' : '') + - padNumber(hours, 2, minusSign) + - padNumber(Math.abs(zone % 60), 2, minusSign) - ); - case ZoneWidth.ShortGMT: - return ( - 'GMT' + (zone >= 0 ? '+' : '') + padNumber(hours, 1, minusSign) - ); - case ZoneWidth.Long: - return ( - 'GMT' + - (zone >= 0 ? '+' : '') + - padNumber(hours, 2, minusSign) + - ':' + - padNumber(Math.abs(zone % 60), 2, minusSign) - ); - case ZoneWidth.Extended: - if (offset === 0) { - return 'Z'; - } else { - return ( - (zone >= 0 ? '+' : '') + - padNumber(hours, 2, minusSign) + - ':' + - padNumber(Math.abs(zone % 60), 2, minusSign) - ); - } - default: - throw new Error(`Unknown zone width "${width}"`); - } - }; - } - const JANUARY = 0; - const THURSDAY = 4; - function getFirstThursdayOfYear(year) { - const firstDayOfYear = createDate(year, JANUARY, 1).getDay(); - return createDate( - year, - 0, - 1 + - (firstDayOfYear <= THURSDAY ? THURSDAY : THURSDAY + 7) - - firstDayOfYear - ); - } - function getThursdayThisWeek(datetime) { - return createDate( - datetime.getFullYear(), - datetime.getMonth(), - datetime.getDate() + (THURSDAY - datetime.getDay()) - ); - } - function weekGetter(size, monthBased = false) { - return function (date, locale) { - let result; - if (monthBased) { - const nbDaysBefore1stDayOfMonth = - new Date(date.getFullYear(), date.getMonth(), 1).getDay() - 1; - const today = date.getDate(); - result = 1 + Math.floor((today + nbDaysBefore1stDayOfMonth) / 7); - } else { - const thisThurs = getThursdayThisWeek(date); - // Some days of a year are part of next year according to ISO 8601. - // Compute the firstThurs from the year of this week's Thursday - const firstThurs = getFirstThursdayOfYear(thisThurs.getFullYear()); - const diff = thisThurs.getTime() - firstThurs.getTime(); - result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week - } - return padNumber( - result, - size, - getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign) - ); - }; - } - /** - * Returns a date formatter that provides the week-numbering year for the input date. - */ - function weekNumberingYearGetter(size, trim = false) { - return function (date, locale) { - const thisThurs = getThursdayThisWeek(date); - const weekNumberingYear = thisThurs.getFullYear(); - return padNumber( - weekNumberingYear, - size, - getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign), - trim - ); - }; - } - const DATE_FORMATS = {}; - // Based on CLDR formats: - // See complete list: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table - // See also explanations: http://cldr.unicode.org/translation/date-time - // TODO(ocombe): support all missing cldr formats: U, Q, D, F, e, j, J, C, A, v, V, X, x - function getDateFormatter(format) { - if (DATE_FORMATS[format]) { - return DATE_FORMATS[format]; - } - let formatter; - switch (format) { - // Era name (AD/BC) - case 'G': - case 'GG': - case 'GGG': - formatter = dateStrGetter( - TranslationType.Eras, - exports.TranslationWidth.Abbreviated - ); - break; - case 'GGGG': - formatter = dateStrGetter( - TranslationType.Eras, - exports.TranslationWidth.Wide - ); - break; - case 'GGGGG': - formatter = dateStrGetter( - TranslationType.Eras, - exports.TranslationWidth.Narrow - ); - break; - // 1 digit representation of the year, e.g. (AD 1 => 1, AD 199 => 199) - case 'y': - formatter = dateGetter(DateType.FullYear, 1, 0, false, true); - break; - // 2 digit representation of the year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) - case 'yy': - formatter = dateGetter(DateType.FullYear, 2, 0, true, true); - break; - // 3 digit representation of the year, padded (000-999). (e.g. AD 2001 => 01, AD 2010 => 10) - case 'yyy': - formatter = dateGetter(DateType.FullYear, 3, 0, false, true); - break; - // 4 digit representation of the year (e.g. AD 1 => 0001, AD 2010 => 2010) - case 'yyyy': - formatter = dateGetter(DateType.FullYear, 4, 0, false, true); - break; - // 1 digit representation of the week-numbering year, e.g. (AD 1 => 1, AD 199 => 199) - case 'Y': - formatter = weekNumberingYearGetter(1); - break; - // 2 digit representation of the week-numbering year, padded (00-99). (e.g. AD 2001 => 01, AD - // 2010 => 10) - case 'YY': - formatter = weekNumberingYearGetter(2, true); - break; - // 3 digit representation of the week-numbering year, padded (000-999). (e.g. AD 1 => 001, AD - // 2010 => 2010) - case 'YYY': - formatter = weekNumberingYearGetter(3); - break; - // 4 digit representation of the week-numbering year (e.g. AD 1 => 0001, AD 2010 => 2010) - case 'YYYY': - formatter = weekNumberingYearGetter(4); - break; - // Month of the year (1-12), numeric - case 'M': - case 'L': - formatter = dateGetter(DateType.Month, 1, 1); - break; - case 'MM': - case 'LL': - formatter = dateGetter(DateType.Month, 2, 1); - break; - // Month of the year (January, ...), string, format - case 'MMM': - formatter = dateStrGetter( - TranslationType.Months, - exports.TranslationWidth.Abbreviated - ); - break; - case 'MMMM': - formatter = dateStrGetter( - TranslationType.Months, - exports.TranslationWidth.Wide - ); - break; - case 'MMMMM': - formatter = dateStrGetter( - TranslationType.Months, - exports.TranslationWidth.Narrow - ); - break; - // Month of the year (January, ...), string, standalone - case 'LLL': - formatter = dateStrGetter( - TranslationType.Months, - exports.TranslationWidth.Abbreviated, - exports.FormStyle.Standalone - ); - break; - case 'LLLL': - formatter = dateStrGetter( - TranslationType.Months, - exports.TranslationWidth.Wide, - exports.FormStyle.Standalone - ); - break; - case 'LLLLL': - formatter = dateStrGetter( - TranslationType.Months, - exports.TranslationWidth.Narrow, - exports.FormStyle.Standalone - ); - break; - // Week of the year (1, ... 52) - case 'w': - formatter = weekGetter(1); - break; - case 'ww': - formatter = weekGetter(2); - break; - // Week of the month (1, ...) - case 'W': - formatter = weekGetter(1, true); - break; - // Day of the month (1-31) - case 'd': - formatter = dateGetter(DateType.Date, 1); - break; - case 'dd': - formatter = dateGetter(DateType.Date, 2); - break; - // Day of the Week StandAlone (1, 1, Mon, Monday, M, Mo) - case 'c': - case 'cc': - formatter = dateGetter(DateType.Day, 1); - break; - case 'ccc': - formatter = dateStrGetter( - TranslationType.Days, - exports.TranslationWidth.Abbreviated, - exports.FormStyle.Standalone - ); - break; - case 'cccc': - formatter = dateStrGetter( - TranslationType.Days, - exports.TranslationWidth.Wide, - exports.FormStyle.Standalone - ); - break; - case 'ccccc': - formatter = dateStrGetter( - TranslationType.Days, - exports.TranslationWidth.Narrow, - exports.FormStyle.Standalone - ); - break; - case 'cccccc': - formatter = dateStrGetter( - TranslationType.Days, - exports.TranslationWidth.Short, - exports.FormStyle.Standalone - ); - break; - // Day of the Week - case 'E': - case 'EE': - case 'EEE': - formatter = dateStrGetter( - TranslationType.Days, - exports.TranslationWidth.Abbreviated - ); - break; - case 'EEEE': - formatter = dateStrGetter( - TranslationType.Days, - exports.TranslationWidth.Wide - ); - break; - case 'EEEEE': - formatter = dateStrGetter( - TranslationType.Days, - exports.TranslationWidth.Narrow - ); - break; - case 'EEEEEE': - formatter = dateStrGetter( - TranslationType.Days, - exports.TranslationWidth.Short - ); - break; - // Generic period of the day (am-pm) - case 'a': - case 'aa': - case 'aaa': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Abbreviated - ); - break; - case 'aaaa': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Wide - ); - break; - case 'aaaaa': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Narrow - ); - break; - // Extended period of the day (midnight, at night, ...), standalone - case 'b': - case 'bb': - case 'bbb': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Abbreviated, - exports.FormStyle.Standalone, - true - ); - break; - case 'bbbb': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Wide, - exports.FormStyle.Standalone, - true - ); - break; - case 'bbbbb': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Narrow, - exports.FormStyle.Standalone, - true - ); - break; - // Extended period of the day (midnight, night, ...), standalone - case 'B': - case 'BB': - case 'BBB': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Abbreviated, - exports.FormStyle.Format, - true - ); - break; - case 'BBBB': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Wide, - exports.FormStyle.Format, - true - ); - break; - case 'BBBBB': - formatter = dateStrGetter( - TranslationType.DayPeriods, - exports.TranslationWidth.Narrow, - exports.FormStyle.Format, - true - ); - break; - // Hour in AM/PM, (1-12) - case 'h': - formatter = dateGetter(DateType.Hours, 1, -12); - break; - case 'hh': - formatter = dateGetter(DateType.Hours, 2, -12); - break; - // Hour of the day (0-23) - case 'H': - formatter = dateGetter(DateType.Hours, 1); - break; - // Hour in day, padded (00-23) - case 'HH': - formatter = dateGetter(DateType.Hours, 2); - break; - // Minute of the hour (0-59) - case 'm': - formatter = dateGetter(DateType.Minutes, 1); - break; - case 'mm': - formatter = dateGetter(DateType.Minutes, 2); - break; - // Second of the minute (0-59) - case 's': - formatter = dateGetter(DateType.Seconds, 1); - break; - case 'ss': - formatter = dateGetter(DateType.Seconds, 2); - break; - // Fractional second - case 'S': - formatter = dateGetter(DateType.FractionalSeconds, 1); - break; - case 'SS': - formatter = dateGetter(DateType.FractionalSeconds, 2); - break; - case 'SSS': - formatter = dateGetter(DateType.FractionalSeconds, 3); - break; - // Timezone ISO8601 short format (-0430) - case 'Z': - case 'ZZ': - case 'ZZZ': - formatter = timeZoneGetter(ZoneWidth.Short); - break; - // Timezone ISO8601 extended format (-04:30) - case 'ZZZZZ': - formatter = timeZoneGetter(ZoneWidth.Extended); - break; - // Timezone GMT short format (GMT+4) - case 'O': - case 'OO': - case 'OOO': - // Should be location, but fallback to format O instead because we don't have the data yet - case 'z': - case 'zz': - case 'zzz': - formatter = timeZoneGetter(ZoneWidth.ShortGMT); - break; - // Timezone GMT long format (GMT+0430) - case 'OOOO': - case 'ZZZZ': - // Should be location, but fallback to format O instead because we don't have the data yet - case 'zzzz': - formatter = timeZoneGetter(ZoneWidth.Long); - break; - default: - return null; + this.setHostAttributes(); + } + setHostAttributes() { + // Must set width/height explicitly in case they are bound (in which case they will + // only be reflected and not found by the browser) + if (this.fill) { + if (!this.sizes) { + this.sizes = '100vw'; + } + } + else { + this.setHostAttribute('width', this.width.toString()); + this.setHostAttribute('height', this.height.toString()); + } + this.setHostAttribute('loading', this.getLoadingBehavior()); + this.setHostAttribute('fetchpriority', this.getFetchPriority()); + // The `src` and `srcset` attributes should be set last since other attributes + // could affect the image's loading behavior. + const rewrittenSrc = this.getRewrittenSrc(); + this.setHostAttribute('src', rewrittenSrc); + let rewrittenSrcset = undefined; + if (this.sizes) { + this.setHostAttribute('sizes', this.sizes); + } + if (this.ngSrcset) { + rewrittenSrcset = this.getRewrittenSrcset(); + } + else if (!this._disableOptimizedSrcset && !this.srcset && this.imageLoader !== noopImageLoader) { + rewrittenSrcset = this.getAutomaticSrcset(); + } + if (rewrittenSrcset) { + this.setHostAttribute('srcset', rewrittenSrcset); + } + if (this.isServer && this.priority) { + this.preloadLinkChecker.createPreloadLinkTag(this.renderer, rewrittenSrc, rewrittenSrcset, this.sizes); + } + } + /** @nodoc */ + ngOnChanges(changes) { + if (ngDevMode) { + assertNoPostInitInputChange(this, changes, [ + 'ngSrc', + 'ngSrcset', + 'width', + 'height', + 'priority', + 'fill', + 'loading', + 'sizes', + 'disableOptimizedSrcset', + ]); + } + } + getLoadingBehavior() { + if (!this.priority && this.loading !== undefined) { + return this.loading; + } + return this.priority ? 'eager' : 'lazy'; + } + getFetchPriority() { + return this.priority ? 'high' : 'auto'; + } + getRewrittenSrc() { + // ImageLoaderConfig supports setting a width property. However, we're not setting width here + // because if the developer uses rendered width instead of intrinsic width in the HTML width + // attribute, the image requested may be too small for 2x+ screens. + if (!this._renderedSrc) { + const imgConfig = { src: this.ngSrc }; + // Cache calculated image src to reuse it later in the code. + this._renderedSrc = this.imageLoader(imgConfig); + } + return this._renderedSrc; + } + getRewrittenSrcset() { + const widthSrcSet = VALID_WIDTH_DESCRIPTOR_SRCSET.test(this.ngSrcset); + const finalSrcs = this.ngSrcset.split(',').filter(src => src !== '').map(srcStr => { + srcStr = srcStr.trim(); + const width = widthSrcSet ? parseFloat(srcStr) : parseFloat(srcStr) * this.width; + return `${this.imageLoader({ src: this.ngSrc, width })} ${srcStr}`; + }); + return finalSrcs.join(', '); + } + getAutomaticSrcset() { + if (this.sizes) { + return this.getResponsiveSrcset(); + } + else { + return this.getFixedSrcset(); + } + } + getResponsiveSrcset() { + var _a; + const { breakpoints } = this.config; + let filteredBreakpoints = breakpoints; + if (((_a = this.sizes) === null || _a === void 0 ? void 0 : _a.trim()) === '100vw') { + // Since this is a full-screen-width image, our srcset only needs to include + // breakpoints with full viewport widths. + filteredBreakpoints = breakpoints.filter(bp => bp >= VIEWPORT_BREAKPOINT_CUTOFF); + } + const finalSrcs = filteredBreakpoints.map(bp => `${this.imageLoader({ src: this.ngSrc, width: bp })} ${bp}w`); + return finalSrcs.join(', '); + } + getFixedSrcset() { + const finalSrcs = DENSITY_SRCSET_MULTIPLIERS.map(multiplier => `${this.imageLoader({ src: this.ngSrc, width: this.width * multiplier })} ${multiplier}x`); + return finalSrcs.join(', '); + } + /** @nodoc */ + ngOnDestroy() { + if (ngDevMode) { + if (!this.priority && this._renderedSrc !== null && this.lcpObserver !== null) { + this.lcpObserver.unregisterImage(this._renderedSrc); + } + } + } + setHostAttribute(name, value) { + this.renderer.setAttribute(this.imgElement, name, value); + } } - DATE_FORMATS[format] = formatter; - return formatter; - } - function timezoneToOffset(timezone, fallback) { - // Support: IE 11 only, Edge 13-15+ - // IE/Edge do not "understand" colon (`:`) in timezone - timezone = timezone.replace(/:/g, ''); - const requestedTimezoneOffset = - Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; - return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; - } - function addDateMinutes(date, minutes) { - date = new Date(date.getTime()); - date.setMinutes(date.getMinutes() + minutes); - return date; - } - function convertTimezoneToLocal(date, timezone, reverse) { - const reverseValue = reverse ? -1 : 1; - const dateTimezoneOffset = date.getTimezoneOffset(); - const timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); - return addDateMinutes( - date, - reverseValue * (timezoneOffset - dateTimezoneOffset) - ); - } - /** - * Converts a value to date. - * - * Supported input formats: - * - `Date` - * - number: timestamp - * - string: numeric (e.g. "1234"), ISO and date strings in a format supported by - * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). - * Note: ISO strings without time return a date without timeoffset. - * - * Throws if unable to convert to a date. - */ - function toDate(value) { - if (isDate(value)) { - return value; - } - if (typeof value === 'number' && !isNaN(value)) { - return new Date(value); - } - if (typeof value === 'string') { - value = value.trim(); - if (/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(value)) { - /* For ISO Strings without time the day, month and year must be extracted from the ISO String - before Date creation to avoid time offset and errors in the new Date. - If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new - date, some browsers (e.g. IE 9) will throw an invalid Date error. - If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset - is applied. - Note: ISO months are 0 for January, 1 for February, ... */ - const [y, m = 1, d = 1] = value.split('-').map((val) => +val); - return createDate(y, m - 1, d); - } - const parsedNb = parseFloat(value); - // any string that only contains numbers, like "1234" but not like "1234hello" - if (!isNaN(value - parsedNb)) { - return new Date(parsedNb); - } - let match; - if ((match = value.match(ISO8601_DATE_REGEX))) { - return isoStringToDate(match); - } - } - const date = new Date(value); - if (!isDate(date)) { - throw new Error(`Unable to convert "${value}" into a date`); - } - return date; - } - /** - * Converts a date in ISO8601 to a Date. - * Used instead of `Date.parse` because of browser discrepancies. - */ - function isoStringToDate(match) { - const date = new Date(0); - let tzHour = 0; - let tzMin = 0; - // match[8] means that the string contains "Z" (UTC) or a timezone like "+01:00" or "+0100" - const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear; - const timeSetter = match[8] ? date.setUTCHours : date.setHours; - // if there is a timezone defined like "+01:00" or "+0100" - if (match[9]) { - tzHour = Number(match[9] + match[10]); - tzMin = Number(match[9] + match[11]); - } - dateSetter.call( - date, - Number(match[1]), - Number(match[2]) - 1, - Number(match[3]) - ); - const h = Number(match[4] || 0) - tzHour; - const m = Number(match[5] || 0) - tzMin; - const s = Number(match[6] || 0); - // The ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11) - // defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms` - // becomes `999ms`. - const ms = Math.floor(parseFloat('0.' + (match[7] || 0)) * 1000); - timeSetter.call(date, h, m, s, ms); - return date; - } - function isDate(value) { - return value instanceof Date && !isNaN(value.valueOf()); - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/; - const MAX_DIGITS = 22; - const DECIMAL_SEP = '.'; - const ZERO_CHAR = '0'; - const PATTERN_SEP = ';'; - const GROUP_SEP = ','; - const DIGIT_CHAR = '#'; - const CURRENCY_CHAR = '¤'; - const PERCENT_CHAR = '%'; - /** - * Transforms a number to a locale string based on a style and a format. - */ - function formatNumberToLocaleString( - value, - pattern, - locale, - groupSymbol, - decimalSymbol, - digitsInfo, - isPercent = false - ) { - let formattedText = ''; - let isZero = false; - if (!isFinite(value)) { - formattedText = getLocaleNumberSymbol( - locale, - exports.NumberSymbol.Infinity - ); - } else { - let parsedNumber = parseNumber(value); - if (isPercent) { - parsedNumber = toPercent(parsedNumber); - } - let minInt = pattern.minInt; - let minFraction = pattern.minFrac; - let maxFraction = pattern.maxFrac; - if (digitsInfo) { - const parts = digitsInfo.match(NUMBER_FORMAT_REGEXP); - if (parts === null) { - throw new Error(`${digitsInfo} is not a valid digit info`); - } - const minIntPart = parts[1]; - const minFractionPart = parts[3]; - const maxFractionPart = parts[5]; - if (minIntPart != null) { - minInt = parseIntAutoRadix(minIntPart); - } - if (minFractionPart != null) { - minFraction = parseIntAutoRadix(minFractionPart); - } - if (maxFractionPart != null) { - maxFraction = parseIntAutoRadix(maxFractionPart); - } else if (minFractionPart != null && minFraction > maxFraction) { - maxFraction = minFraction; - } - } - roundNumber(parsedNumber, minFraction, maxFraction); - let digits = parsedNumber.digits; - let integerLen = parsedNumber.integerLen; - const exponent = parsedNumber.exponent; - let decimals = []; - isZero = digits.every((d) => !d); - // pad zeros for small numbers - for (; integerLen < minInt; integerLen++) { - digits.unshift(0); - } - // pad zeros for small numbers - for (; integerLen < 0; integerLen++) { - digits.unshift(0); - } - // extract decimals digits - if (integerLen > 0) { - decimals = digits.splice(integerLen, digits.length); - } else { - decimals = digits; - digits = [0]; - } - // format the integer digits with grouping separators - const groups = []; - if (digits.length >= pattern.lgSize) { - groups.unshift(digits.splice(-pattern.lgSize, digits.length).join('')); - } - while (digits.length > pattern.gSize) { - groups.unshift(digits.splice(-pattern.gSize, digits.length).join('')); - } - if (digits.length) { - groups.unshift(digits.join('')); - } - formattedText = groups.join(getLocaleNumberSymbol(locale, groupSymbol)); - // append the decimal digits - if (decimals.length) { - formattedText += - getLocaleNumberSymbol(locale, decimalSymbol) + decimals.join(''); - } - if (exponent) { - formattedText += - getLocaleNumberSymbol(locale, exports.NumberSymbol.Exponential) + - '+' + - exponent; - } - } - if (value < 0 && !isZero) { - formattedText = pattern.negPre + formattedText + pattern.negSuf; - } else { - formattedText = pattern.posPre + formattedText + pattern.posSuf; - } - return formattedText; - } - /** - * @ngModule CommonModule - * @description - * - * Formats a number as currency using locale rules. - * - * @param value The number to format. - * @param locale A locale code for the locale format rules to use. - * @param currency A string containing the currency symbol or its name, - * such as "$" or "Canadian Dollar". Used in output string, but does not affect the operation - * of the function. - * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) - * currency code, such as `USD` for the US dollar and `EUR` for the euro. - * Used to determine the number of digits in the decimal part. - * @param digitsInfo Decimal representation options, specified by a string in the following format: - * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. - * - * @returns The formatted currency value. - * - * @see `formatNumber()` - * @see `DecimalPipe` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function formatCurrency(value, locale, currency, currencyCode, digitsInfo) { - const format = getLocaleNumberFormat( - locale, - exports.NumberFormatStyle.Currency - ); - const pattern = parseNumberFormat( - format, - getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign) - ); - pattern.minFrac = getNumberOfCurrencyDigits(currencyCode); - pattern.maxFrac = pattern.minFrac; - const res = formatNumberToLocaleString( - value, - pattern, - locale, - exports.NumberSymbol.CurrencyGroup, - exports.NumberSymbol.CurrencyDecimal, - digitsInfo - ); - return ( - res - .replace(CURRENCY_CHAR, currency) - // if we have 2 time the currency character, the second one is ignored - .replace(CURRENCY_CHAR, '') - // If there is a spacing between currency character and the value and - // the currency character is suppressed by passing an empty string, the - // spacing character would remain as part of the string. Then we - // should remove it. - .trim() - ); - } - /** - * @ngModule CommonModule - * @description - * - * Formats a number as a percentage according to locale rules. - * - * @param value The number to format. - * @param locale A locale code for the locale format rules to use. - * @param digitsInfo Decimal representation options, specified by a string in the following format: - * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. - * - * @returns The formatted percentage value. - * - * @see `formatNumber()` - * @see `DecimalPipe` - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * @publicApi - * - */ - function formatPercent(value, locale, digitsInfo) { - const format = getLocaleNumberFormat( - locale, - exports.NumberFormatStyle.Percent - ); - const pattern = parseNumberFormat( - format, - getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign) - ); - const res = formatNumberToLocaleString( - value, - pattern, - locale, - exports.NumberSymbol.Group, - exports.NumberSymbol.Decimal, - digitsInfo, - true - ); - return res.replace( - new RegExp(PERCENT_CHAR, 'g'), - getLocaleNumberSymbol(locale, exports.NumberSymbol.PercentSign) - ); - } - /** - * @ngModule CommonModule - * @description - * - * Formats a number as text, with group sizing, separator, and other - * parameters based on the locale. - * - * @param value The number to format. - * @param locale A locale code for the locale format rules to use. - * @param digitsInfo Decimal representation options, specified by a string in the following format: - * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. - * - * @returns The formatted text string. - * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) - * - * @publicApi - */ - function formatNumber(value, locale, digitsInfo) { - const format = getLocaleNumberFormat( - locale, - exports.NumberFormatStyle.Decimal - ); - const pattern = parseNumberFormat( - format, - getLocaleNumberSymbol(locale, exports.NumberSymbol.MinusSign) - ); - return formatNumberToLocaleString( - value, - pattern, - locale, - exports.NumberSymbol.Group, - exports.NumberSymbol.Decimal, - digitsInfo - ); - } - function parseNumberFormat(format, minusSign = '-') { - const p = { - minInt: 1, - minFrac: 0, - maxFrac: 0, - posPre: '', - posSuf: '', - negPre: '', - negSuf: '', - gSize: 0, - lgSize: 0, - }; - const patternParts = format.split(PATTERN_SEP); - const positive = patternParts[0]; - const negative = patternParts[1]; - const positiveParts = - positive.indexOf(DECIMAL_SEP) !== -1 - ? positive.split(DECIMAL_SEP) - : [ - positive.substring(0, positive.lastIndexOf(ZERO_CHAR) + 1), - positive.substring(positive.lastIndexOf(ZERO_CHAR) + 1), - ], - integer = positiveParts[0], - fraction = positiveParts[1] || ''; - p.posPre = integer.substring(0, integer.indexOf(DIGIT_CHAR)); - for (let i = 0; i < fraction.length; i++) { - const ch = fraction.charAt(i); - if (ch === ZERO_CHAR) { - p.minFrac = p.maxFrac = i + 1; - } else if (ch === DIGIT_CHAR) { - p.maxFrac = i + 1; - } else { - p.posSuf += ch; - } - } - const groups = integer.split(GROUP_SEP); - p.gSize = groups[1] ? groups[1].length : 0; - p.lgSize = groups[2] || groups[1] ? (groups[2] || groups[1]).length : 0; - if (negative) { - const trunkLen = positive.length - p.posPre.length - p.posSuf.length, - pos = negative.indexOf(DIGIT_CHAR); - p.negPre = negative.substring(0, pos).replace(/'/g, ''); - p.negSuf = negative.slice(pos + trunkLen).replace(/'/g, ''); - } else { - p.negPre = minusSign + p.posPre; - p.negSuf = p.posSuf; - } - return p; - } - // Transforms a parsed number into a percentage by multiplying it by 100 - function toPercent(parsedNumber) { - // if the number is 0, don't do anything - if (parsedNumber.digits[0] === 0) { - return parsedNumber; - } - // Getting the current number of decimals - const fractionLen = parsedNumber.digits.length - parsedNumber.integerLen; - if (parsedNumber.exponent) { - parsedNumber.exponent += 2; - } else { - if (fractionLen === 0) { - parsedNumber.digits.push(0, 0); - } else if (fractionLen === 1) { - parsedNumber.digits.push(0); - } - parsedNumber.integerLen += 2; - } - return parsedNumber; - } - /** - * Parses a number. - * Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/ - */ - function parseNumber(num) { - let numStr = Math.abs(num) + ''; - let exponent = 0, - digits, - integerLen; - let i, j, zeros; - // Decimal point? - if ((integerLen = numStr.indexOf(DECIMAL_SEP)) > -1) { - numStr = numStr.replace(DECIMAL_SEP, ''); - } - // Exponential form? - if ((i = numStr.search(/e/i)) > 0) { - // Work out the exponent. - if (integerLen < 0) integerLen = i; - integerLen += +numStr.slice(i + 1); - numStr = numStr.substring(0, i); - } else if (integerLen < 0) { - // There was no decimal point or exponent so it is an integer. - integerLen = numStr.length; - } - // Count the number of leading zeros. - for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { - /* empty */ - } - if (i === (zeros = numStr.length)) { - // The digits are all zero. - digits = [0]; - integerLen = 1; - } else { - // Count the number of trailing zeros - zeros--; - while (numStr.charAt(zeros) === ZERO_CHAR) zeros--; - // Trailing zeros are insignificant so ignore them - integerLen -= i; - digits = []; - // Convert string to array of digits without leading/trailing zeros. - for (j = 0; i <= zeros; i++, j++) { - digits[j] = Number(numStr.charAt(i)); - } - } - // If the number overflows the maximum allowed digits then use an exponent. - if (integerLen > MAX_DIGITS) { - digits = digits.splice(0, MAX_DIGITS - 1); - exponent = integerLen - 1; - integerLen = 1; - } - return { digits, exponent, integerLen }; - } - /** - * Round the parsed number to the specified number of decimal places - * This function changes the parsedNumber in-place - */ - function roundNumber(parsedNumber, minFrac, maxFrac) { - if (minFrac > maxFrac) { - throw new Error( - `The minimum number of digits after fraction (${minFrac}) is higher than the maximum (${maxFrac}).` - ); - } - let digits = parsedNumber.digits; - let fractionLen = digits.length - parsedNumber.integerLen; - const fractionSize = Math.min(Math.max(minFrac, fractionLen), maxFrac); - // The index of the digit to where rounding is to occur - let roundAt = fractionSize + parsedNumber.integerLen; - let digit = digits[roundAt]; - if (roundAt > 0) { - // Drop fractional digits beyond `roundAt` - digits.splice(Math.max(parsedNumber.integerLen, roundAt)); - // Set non-fractional digits beyond `roundAt` to 0 - for (let j = roundAt; j < digits.length; j++) { - digits[j] = 0; - } - } else { - // We rounded to zero so reset the parsedNumber - fractionLen = Math.max(0, fractionLen); - parsedNumber.integerLen = 1; - digits.length = Math.max(1, (roundAt = fractionSize + 1)); - digits[0] = 0; - for (let i = 1; i < roundAt; i++) digits[i] = 0; - } - if (digit >= 5) { - if (roundAt - 1 < 0) { - for (let k = 0; k > roundAt; k--) { - digits.unshift(0); - parsedNumber.integerLen++; - } - digits.unshift(1); - parsedNumber.integerLen++; - } else { - digits[roundAt - 1]++; - } - } - // Pad out with zeros to get the required fraction length - for (; fractionLen < Math.max(0, fractionSize); fractionLen++) - digits.push(0); - let dropTrailingZeros = fractionSize !== 0; - // Minimal length = nb of decimals required + current nb of integers - // Any number besides that is optional and can be removed if it's a trailing 0 - const minLen = minFrac + parsedNumber.integerLen; - // Do any carrying, e.g. a digit was rounded up to 10 - const carry = digits.reduceRight(function (carry, d, i, digits) { - d = d + carry; - digits[i] = d < 10 ? d : d - 10; // d % 10 - if (dropTrailingZeros) { - // Do not keep meaningless fractional trailing zeros (e.g. 15.52000 --> 15.52) - if (digits[i] === 0 && i >= minLen) { - digits.pop(); - } else { - dropTrailingZeros = false; - } - } - return d >= 10 ? 1 : 0; // Math.floor(d / 10); - }, 0); - if (carry) { - digits.unshift(carry); - parsedNumber.integerLen++; - } - } - function parseIntAutoRadix(text) { - const result = parseInt(text); - if (isNaN(result)) { - throw new Error('Invalid integer literal when parsing ' + text); - } - return result; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @publicApi - */ - class NgLocalization {} - NgLocalization.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgLocalization, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - NgLocalization.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgLocalization, - providedIn: 'root', - useFactory: (locale) => new NgLocaleLocalization(locale), - deps: [{ token: i0.LOCALE_ID }], - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgLocalization, - decorators: [ - { - type: i0.Injectable, - args: [ - { - providedIn: 'root', - useFactory: (locale) => new NgLocaleLocalization(locale), - deps: [i0.LOCALE_ID], - }, - ], - }, - ], - }); - /** - * Returns the plural category for a given value. - * - "=value" when the case exists, - * - the plural category otherwise - */ - function getPluralCategory(value, cases, ngLocalization, locale) { - let key = `=${value}`; - if (cases.indexOf(key) > -1) { - return key; - } - key = ngLocalization.getPluralCategory(value, locale); - if (cases.indexOf(key) > -1) { - return key; - } - if (cases.indexOf('other') > -1) { - return 'other'; - } - throw new Error(`No plural message found for value "${value}"`); - } - /** - * Returns the plural case based on the locale - * - * @publicApi - */ - class NgLocaleLocalization extends NgLocalization { - constructor(locale) { - super(); - this.locale = locale; - } - getPluralCategory(value, locale) { - const plural = getLocalePluralCase(locale || this.locale)(value); - switch (plural) { - case exports.Plural.Zero: - return 'zero'; - case exports.Plural.One: - return 'one'; - case exports.Plural.Two: - return 'two'; - case exports.Plural.Few: - return 'few'; - case exports.Plural.Many: - return 'many'; - default: - return 'other'; - } - } - } - NgLocaleLocalization.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgLocaleLocalization, - deps: [{ token: i0.LOCALE_ID }], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - NgLocaleLocalization.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgLocaleLocalization, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgLocaleLocalization, - decorators: [ - { - type: i0.Injectable, - }, - ], - ctorParameters: function () { - return [ - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [i0.LOCALE_ID], - }, - ], - }, - ]; - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Register global data to be used internally by Angular. See the - * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale - * data. - * - * The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1 - * - * @publicApi - */ - function registerLocaleData(data, localeId, extraData) { - return i0['ɵregisterLocaleData'](data, localeId, extraData); - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - function parseCookieValue(cookieStr, name) { - name = encodeURIComponent(name); - for (const cookie of cookieStr.split(';')) { - const eqIndex = cookie.indexOf('='); - const [cookieName, cookieValue] = - eqIndex == -1 - ? [cookie, ''] - : [cookie.slice(0, eqIndex), cookie.slice(eqIndex + 1)]; - if (cookieName.trim() === name) { - return decodeURIComponent(cookieValue); - } - } - return null; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @ngModule CommonModule - * - * @usageNotes - * ``` - * ... - * - * ... - * - * ... - * - * ... - * - * ... - * ``` - * - * @description - * - * Adds and removes CSS classes on an HTML element. - * - * The CSS classes are updated as follows, depending on the type of the expression evaluation: - * - `string` - the CSS classes listed in the string (space delimited) are added, - * - `Array` - the CSS classes declared as Array elements are added, - * - `Object` - keys are CSS classes that get added when the expression given in the value - * evaluates to a truthy value, otherwise they are removed. - * - * @publicApi - */ - class NgClass { - constructor(_iterableDiffers, _keyValueDiffers, _ngEl, _renderer) { - this._iterableDiffers = _iterableDiffers; - this._keyValueDiffers = _keyValueDiffers; - this._ngEl = _ngEl; - this._renderer = _renderer; - this._iterableDiffer = null; - this._keyValueDiffer = null; - this._initialClasses = []; - this._rawClass = null; - } - set klass(value) { - this._removeClasses(this._initialClasses); - this._initialClasses = - typeof value === 'string' ? value.split(/\s+/) : []; - this._applyClasses(this._initialClasses); - this._applyClasses(this._rawClass); - } - set ngClass(value) { - this._removeClasses(this._rawClass); - this._applyClasses(this._initialClasses); - this._iterableDiffer = null; - this._keyValueDiffer = null; - this._rawClass = typeof value === 'string' ? value.split(/\s+/) : value; - if (this._rawClass) { - if (i0['ɵisListLikeIterable'](this._rawClass)) { - this._iterableDiffer = this._iterableDiffers - .find(this._rawClass) - .create(); - } else { - this._keyValueDiffer = this._keyValueDiffers - .find(this._rawClass) - .create(); - } - } - } - ngDoCheck() { - if (this._iterableDiffer) { - const iterableChanges = this._iterableDiffer.diff(this._rawClass); - if (iterableChanges) { - this._applyIterableChanges(iterableChanges); - } - } else if (this._keyValueDiffer) { - const keyValueChanges = this._keyValueDiffer.diff(this._rawClass); - if (keyValueChanges) { - this._applyKeyValueChanges(keyValueChanges); - } - } - } - _applyKeyValueChanges(changes) { - changes.forEachAddedItem((record) => - this._toggleClass(record.key, record.currentValue) - ); - changes.forEachChangedItem((record) => - this._toggleClass(record.key, record.currentValue) - ); - changes.forEachRemovedItem((record) => { - if (record.previousValue) { - this._toggleClass(record.key, false); - } - }); - } - _applyIterableChanges(changes) { - changes.forEachAddedItem((record) => { - if (typeof record.item === 'string') { - this._toggleClass(record.item, true); - } else { - throw new Error( - `NgClass can only toggle CSS classes expressed as strings, got ${i0[ - 'ɵstringify' - ](record.item)}` - ); - } - }); - changes.forEachRemovedItem((record) => - this._toggleClass(record.item, false) - ); - } - /** - * Applies a collection of CSS classes to the DOM element. - * - * For argument of type Set and Array CSS class names contained in those collections are always - * added. - * For argument of type Map CSS class name in the map's key is toggled based on the value (added - * for truthy and removed for falsy). - */ - _applyClasses(rawClassVal) { - if (rawClassVal) { - if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { - rawClassVal.forEach((klass) => this._toggleClass(klass, true)); - } else { - Object.keys(rawClassVal).forEach((klass) => - this._toggleClass(klass, !!rawClassVal[klass]) - ); - } - } - } - /** - * Removes a collection of CSS classes from the DOM element. This is mostly useful for cleanup - * purposes. - */ - _removeClasses(rawClassVal) { - if (rawClassVal) { - if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { - rawClassVal.forEach((klass) => this._toggleClass(klass, false)); - } else { - Object.keys(rawClassVal).forEach((klass) => - this._toggleClass(klass, false) - ); - } - } - } - _toggleClass(klass, enabled) { - klass = klass.trim(); - if (klass) { - klass.split(/\s+/g).forEach((klass) => { - if (enabled) { - this._renderer.addClass(this._ngEl.nativeElement, klass); - } else { - this._renderer.removeClass(this._ngEl.nativeElement, klass); - } - }); - } - } - } - NgClass.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgClass, - deps: [ - { token: i0__namespace.IterableDiffers }, - { token: i0__namespace.KeyValueDiffers }, - { token: i0__namespace.ElementRef }, - { token: i0__namespace.Renderer2 }, - ], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgClass.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgClass, - isStandalone: true, - selector: '[ngClass]', - inputs: { klass: ['class', 'klass'], ngClass: 'ngClass' }, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgClass, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngClass]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { type: i0__namespace.IterableDiffers }, - { type: i0__namespace.KeyValueDiffers }, - { type: i0__namespace.ElementRef }, - { type: i0__namespace.Renderer2 }, - ]; - }, - propDecorators: { - klass: [ - { - type: i0.Input, - args: ['class'], - }, - ], - ngClass: [ - { - type: i0.Input, - args: ['ngClass'], - }, - ], - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Instantiates a {@link Component} type and inserts its Host View into the current View. - * `NgComponentOutlet` provides a declarative approach for dynamic component creation. - * - * `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and - * any existing component will be destroyed. - * - * @usageNotes - * - * ### Fine tune control - * - * You can control the component creation process by using the following optional attributes: - * - * * `ngComponentOutletInjector`: Optional custom {@link Injector} that will be used as parent for - * the Component. Defaults to the injector of the current view container. - * - * * `ngComponentOutletContent`: Optional list of projectable nodes to insert into the content - * section of the component, if it exists. - * - * * `ngComponentOutletNgModule`: Optional NgModule class reference to allow loading another - * module dynamically, then loading a component from that module. - * - * * `ngComponentOutletNgModuleFactory`: Deprecated config option that allows providing optional - * NgModule factory to allow loading another module dynamically, then loading a component from that - * module. Use `ngComponentOutletNgModule` instead. - * - * ### Syntax - * - * Simple - * ``` - * - * ``` - * - * Customized injector/content - * ``` - * - * - * ``` - * - * Customized NgModule reference - * ``` - * - * - * ``` - * - * ### A simple example - * - * {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'} - * - * A more complete example with additional options: - * - * {@example common/ngComponentOutlet/ts/module.ts region='CompleteExample'} - * - * @publicApi - * @ngModule CommonModule - */ - class NgComponentOutlet { - constructor(_viewContainerRef) { - this._viewContainerRef = _viewContainerRef; - this.ngComponentOutlet = null; - } - /** @nodoc */ - ngOnChanges(changes) { - const { - _viewContainerRef: viewContainerRef, - ngComponentOutletNgModule: ngModule, - ngComponentOutletNgModuleFactory: ngModuleFactory, - } = this; - viewContainerRef.clear(); - this._componentRef = undefined; - if (this.ngComponentOutlet) { - const injector = - this.ngComponentOutletInjector || viewContainerRef.parentInjector; - if ( - changes['ngComponentOutletNgModule'] || - changes['ngComponentOutletNgModuleFactory'] - ) { - if (this._moduleRef) this._moduleRef.destroy(); - if (ngModule) { - this._moduleRef = i0.createNgModule( - ngModule, - getParentInjector(injector) - ); - } else if (ngModuleFactory) { - this._moduleRef = ngModuleFactory.create( - getParentInjector(injector) - ); - } else { - this._moduleRef = undefined; - } - } - this._componentRef = viewContainerRef.createComponent( - this.ngComponentOutlet, - { - index: viewContainerRef.length, - injector, - ngModuleRef: this._moduleRef, - projectableNodes: this.ngComponentOutletContent, - } - ); - } - } - /** @nodoc */ - ngOnDestroy() { - if (this._moduleRef) this._moduleRef.destroy(); - } - } - NgComponentOutlet.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgComponentOutlet, - deps: [{ token: i0__namespace.ViewContainerRef }], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgComponentOutlet.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgComponentOutlet, - isStandalone: true, - selector: '[ngComponentOutlet]', - inputs: { - ngComponentOutlet: 'ngComponentOutlet', - ngComponentOutletInjector: 'ngComponentOutletInjector', - ngComponentOutletContent: 'ngComponentOutletContent', - ngComponentOutletNgModule: 'ngComponentOutletNgModule', - ngComponentOutletNgModuleFactory: 'ngComponentOutletNgModuleFactory', - }, - usesOnChanges: true, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgComponentOutlet, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngComponentOutlet]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [{ type: i0__namespace.ViewContainerRef }]; - }, - propDecorators: { - ngComponentOutlet: [ - { - type: i0.Input, - }, - ], - ngComponentOutletInjector: [ - { - type: i0.Input, - }, - ], - ngComponentOutletContent: [ - { - type: i0.Input, - }, - ], - ngComponentOutletNgModule: [ - { - type: i0.Input, - }, - ], - ngComponentOutletNgModuleFactory: [ - { - type: i0.Input, - }, - ], - }, - }); - // Helper function that returns an Injector instance of a parent NgModule. - function getParentInjector(injector) { - const parentNgModule = injector.get(i0.NgModuleRef); - return parentNgModule.injector; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode; - /** - * @publicApi - */ - class NgForOfContext { - constructor($implicit, ngForOf, index, count) { - this.$implicit = $implicit; - this.ngForOf = ngForOf; - this.index = index; - this.count = count; - } - get first() { - return this.index === 0; - } - get last() { - return this.index === this.count - 1; - } - get even() { - return this.index % 2 === 0; - } - get odd() { - return !this.even; - } - } - /** - * A [structural directive](guide/structural-directives) that renders - * a template for each item in a collection. - * The directive is placed on an element, which becomes the parent - * of the cloned templates. - * - * The `ngForOf` directive is generally used in the - * [shorthand form](guide/structural-directives#asterisk) `*ngFor`. - * In this form, the template to be rendered for each iteration is the content - * of an anchor element containing the directive. - * - * The following example shows the shorthand syntax with some options, - * contained in an `
  • ` element. - * - * ``` - *
  • ...
  • - * ``` - * - * The shorthand form expands into a long form that uses the `ngForOf` selector - * on an `` element. - * The content of the `` element is the `
  • ` element that held the - * short-form directive. - * - * Here is the expanded version of the short-form example. - * - * ``` - * - *
  • ...
  • - *
    - * ``` - * - * Angular automatically expands the shorthand syntax as it compiles the template. - * The context for each embedded view is logically merged to the current component - * context according to its lexical position. - * - * When using the shorthand syntax, Angular allows only [one structural directive - * on an element](guide/structural-directives#one-per-element). - * If you want to iterate conditionally, for example, - * put the `*ngIf` on a container element that wraps the `*ngFor` element. - * For further discussion, see - * [Structural Directives](guide/structural-directives#one-per-element). - * - * @usageNotes - * - * ### Local variables - * - * `NgForOf` provides exported values that can be aliased to local variables. - * For example: - * - * ``` - *
  • - * {{i}}/{{users.length}}. {{user}} default - *
  • - * ``` - * - * The following exported values can be aliased to local variables: - * - * - `$implicit: T`: The value of the individual items in the iterable (`ngForOf`). - * - `ngForOf: NgIterable`: The value of the iterable expression. Useful when the expression is - * more complex then a property access, for example when using the async pipe (`userStreams | - * async`). - * - `index: number`: The index of the current item in the iterable. - * - `count: number`: The length of the iterable. - * - `first: boolean`: True when the item is the first item in the iterable. - * - `last: boolean`: True when the item is the last item in the iterable. - * - `even: boolean`: True when the item has an even index in the iterable. - * - `odd: boolean`: True when the item has an odd index in the iterable. - * - * ### Change propagation - * - * When the contents of the iterator changes, `NgForOf` makes the corresponding changes to the DOM: - * - * * When an item is added, a new instance of the template is added to the DOM. - * * When an item is removed, its template instance is removed from the DOM. - * * When items are reordered, their respective templates are reordered in the DOM. - * - * Angular uses object identity to track insertions and deletions within the iterator and reproduce - * those changes in the DOM. This has important implications for animations and any stateful - * controls that are present, such as `` elements that accept user input. Inserted rows can - * be animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state - * such as user input. - * For more on animations, see [Transitions and Triggers](guide/transition-and-triggers). - * - * The identities of elements in the iterator can change while the data does not. - * This can happen, for example, if the iterator is produced from an RPC to the server, and that - * RPC is re-run. Even if the data hasn't changed, the second response produces objects with - * different identities, and Angular must tear down the entire DOM and rebuild it (as if all old - * elements were deleted and all new elements inserted). - * - * To avoid this expensive operation, you can customize the default tracking algorithm. - * by supplying the `trackBy` option to `NgForOf`. - * `trackBy` takes a function that has two arguments: `index` and `item`. - * If `trackBy` is given, Angular tracks changes by the return value of the function. - * - * @see [Structural Directives](guide/structural-directives) - * @ngModule CommonModule - * @publicApi - */ - class NgForOf { - constructor(_viewContainer, _template, _differs) { - this._viewContainer = _viewContainer; - this._template = _template; - this._differs = _differs; - this._ngForOf = null; - this._ngForOfDirty = true; - this._differ = null; - } - /** - * The value of the iterable expression, which can be used as a - * [template input variable](guide/structural-directives#shorthand). - */ - set ngForOf(ngForOf) { - this._ngForOf = ngForOf; - this._ngForOfDirty = true; - } - /** - * Specifies a custom `TrackByFunction` to compute the identity of items in an iterable. - * - * If a custom `TrackByFunction` is not provided, `NgForOf` will use the item's [object - * identity](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) - * as the key. - * - * `NgForOf` uses the computed key to associate items in an iterable with DOM elements - * it produces for these items. - * - * A custom `TrackByFunction` is useful to provide good user experience in cases when items in an - * iterable rendered using `NgForOf` have a natural identifier (for example, custom ID or a - * primary key), and this iterable could be updated with new object instances that still - * represent the same underlying entity (for example, when data is re-fetched from the server, - * and the iterable is recreated and re-rendered, but most of the data is still the same). - * - * @see `TrackByFunction` - */ - set ngForTrackBy(fn) { - if (NG_DEV_MODE && fn != null && typeof fn !== 'function') { - // TODO(vicb): use a log service once there is a public one available - if (console && console.warn) { - console.warn( - `trackBy must be a function, but received ${JSON.stringify(fn)}. ` + - `See https://angular.io/api/common/NgForOf#change-propagation for more information.` - ); - } - } - this._trackByFn = fn; - } - get ngForTrackBy() { - return this._trackByFn; - } - /** - * A reference to the template that is stamped out for each item in the iterable. - * @see [template reference variable](guide/template-reference-variables) - */ - set ngForTemplate(value) { - // TODO(TS2.1): make TemplateRef>> once we move to TS v2.1 - // The current type is too restrictive; a template that just uses index, for example, - // should be acceptable. - if (value) { - this._template = value; - } - } - /** - * Applies the changes when needed. - * @nodoc - */ - ngDoCheck() { - if (this._ngForOfDirty) { - this._ngForOfDirty = false; - // React on ngForOf changes only once all inputs have been initialized - const value = this._ngForOf; - if (!this._differ && value) { - if (NG_DEV_MODE) { - try { - // CAUTION: this logic is duplicated for production mode below, as the try-catch - // is only present in development builds. - this._differ = this._differs - .find(value) - .create(this.ngForTrackBy); - } catch (_a) { - let errorMessage = - `Cannot find a differ supporting object '${value}' of type '` + - `${getTypeName( - value - )}'. NgFor only supports binding to Iterables, such as Arrays.`; - if (typeof value === 'object') { - errorMessage += ' Did you mean to use the keyvalue pipe?'; - } - throw new i0['ɵRuntimeError']( - -2200 /* RuntimeErrorCode.NG_FOR_MISSING_DIFFER */, - errorMessage - ); - } - } else { - // CAUTION: this logic is duplicated for development mode above, as the try-catch - // is only present in development builds. - this._differ = this._differs.find(value).create(this.ngForTrackBy); - } - } - } - if (this._differ) { - const changes = this._differ.diff(this._ngForOf); - if (changes) this._applyChanges(changes); - } - } - _applyChanges(changes) { - const viewContainer = this._viewContainer; - changes.forEachOperation((item, adjustedPreviousIndex, currentIndex) => { - if (item.previousIndex == null) { - // NgForOf is never "null" or "undefined" here because the differ detected - // that a new item needs to be inserted from the iterable. This implies that - // there is an iterable value for "_ngForOf". - viewContainer.createEmbeddedView( - this._template, - new NgForOfContext(item.item, this._ngForOf, -1, -1), - currentIndex === null ? undefined : currentIndex - ); - } else if (currentIndex == null) { - viewContainer.remove( - adjustedPreviousIndex === null ? undefined : adjustedPreviousIndex - ); - } else if (adjustedPreviousIndex !== null) { - const view = viewContainer.get(adjustedPreviousIndex); - viewContainer.move(view, currentIndex); - applyViewChange(view, item); - } - }); - for (let i = 0, ilen = viewContainer.length; i < ilen; i++) { - const viewRef = viewContainer.get(i); - const context = viewRef.context; - context.index = i; - context.count = ilen; - context.ngForOf = this._ngForOf; - } - changes.forEachIdentityChange((record) => { - const viewRef = viewContainer.get(record.currentIndex); - applyViewChange(viewRef, record); - }); - } - /** - * Asserts the correct type of the context for the template that `NgForOf` will render. - * - * The presence of this method is a signal to the Ivy template type-check compiler that the - * `NgForOf` structural directive renders its template with a specific context type. - */ - static ngTemplateContextGuard(dir, ctx) { - return true; - } - } - NgForOf.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgForOf, - deps: [ - { token: i0__namespace.ViewContainerRef }, - { token: i0__namespace.TemplateRef }, - { token: i0__namespace.IterableDiffers }, - ], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgForOf.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgForOf, - isStandalone: true, - selector: '[ngFor][ngForOf]', - inputs: { - ngForOf: 'ngForOf', - ngForTrackBy: 'ngForTrackBy', - ngForTemplate: 'ngForTemplate', - }, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgForOf, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngFor][ngForOf]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { type: i0__namespace.ViewContainerRef }, - { type: i0__namespace.TemplateRef }, - { type: i0__namespace.IterableDiffers }, - ]; - }, - propDecorators: { - ngForOf: [ - { - type: i0.Input, - }, - ], - ngForTrackBy: [ - { - type: i0.Input, - }, - ], - ngForTemplate: [ - { - type: i0.Input, - }, - ], - }, - }); - function applyViewChange(view, record) { - view.context.$implicit = record.item; - } - function getTypeName(type) { - return type['name'] || typeof type; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * A structural directive that conditionally includes a template based on the value of - * an expression coerced to Boolean. - * When the expression evaluates to true, Angular renders the template - * provided in a `then` clause, and when false or null, - * Angular renders the template provided in an optional `else` clause. The default - * template for the `else` clause is blank. - * - * A [shorthand form](guide/structural-directives#asterisk) of the directive, - * `*ngIf="condition"`, is generally used, provided - * as an attribute of the anchor element for the inserted template. - * Angular expands this into a more explicit version, in which the anchor element - * is contained in an `` element. - * - * Simple form with shorthand syntax: - * - * ``` - *
    Content to render when condition is true.
    - * ``` - * - * Simple form with expanded syntax: - * - * ``` - *
    Content to render when condition is - * true.
    - * ``` - * - * Form with an "else" block: - * - * ``` - *
    Content to render when condition is true.
    - * Content to render when condition is false. - * ``` - * - * Shorthand form with "then" and "else" blocks: - * - * ``` - *
    - * Content to render when condition is true. - * Content to render when condition is false. - * ``` - * - * Form with storing the value locally: - * - * ``` - *
    {{value}}
    - * Content to render when value is null. - * ``` - * - * @usageNotes - * - * The `*ngIf` directive is most commonly used to conditionally show an inline template, - * as seen in the following example. - * The default `else` template is blank. - * - * {@example common/ngIf/ts/module.ts region='NgIfSimple'} - * - * ### Showing an alternative template using `else` - * - * To display a template when `expression` evaluates to false, use an `else` template - * binding as shown in the following example. - * The `else` binding points to an `` element labeled `#elseBlock`. - * The template can be defined anywhere in the component view, but is typically placed right after - * `ngIf` for readability. - * - * {@example common/ngIf/ts/module.ts region='NgIfElse'} - * - * ### Using an external `then` template - * - * In the previous example, the then-clause template is specified inline, as the content of the - * tag that contains the `ngIf` directive. You can also specify a template that is defined - * externally, by referencing a labeled `` element. When you do this, you can - * change which template to use at runtime, as shown in the following example. - * - * {@example common/ngIf/ts/module.ts region='NgIfThenElse'} - * - * ### Storing a conditional result in a variable - * - * You might want to show a set of properties from the same object. If you are waiting - * for asynchronous data, the object can be undefined. - * In this case, you can use `ngIf` and store the result of the condition in a local - * variable as shown in the following example. - * - * {@example common/ngIf/ts/module.ts region='NgIfAs'} - * - * This code uses only one `AsyncPipe`, so only one subscription is created. - * The conditional statement stores the result of `userStream|async` in the local variable `user`. - * You can then bind the local `user` repeatedly. - * - * The conditional displays the data only if `userStream` returns a value, - * so you don't need to use the - * safe-navigation-operator (`?.`) - * to guard against null values when accessing properties. - * You can display an alternative template while waiting for the data. - * - * ### Shorthand syntax - * - * The shorthand syntax `*ngIf` expands into two separate template specifications - * for the "then" and "else" clauses. For example, consider the following shorthand statement, - * that is meant to show a loading page while waiting for data to be loaded. - * - * ``` - *
    - * ... - *
    - * - * - *
    Loading...
    - *
    - * ``` - * - * You can see that the "else" clause references the `` - * with the `#loading` label, and the template for the "then" clause - * is provided as the content of the anchor element. - * - * However, when Angular expands the shorthand syntax, it creates - * another `` tag, with `ngIf` and `ngIfElse` directives. - * The anchor element containing the template for the "then" clause becomes - * the content of this unlabeled `` tag. - * - * ``` - * - *
    - * ... - *
    - *
    - * - * - *
    Loading...
    - *
    - * ``` - * - * The presence of the implicit template object has implications for the nesting of - * structural directives. For more on this subject, see - * [Structural Directives](guide/structural-directives#one-per-element). - * - * @ngModule CommonModule - * @publicApi - */ - class NgIf { - constructor(_viewContainer, templateRef) { - this._viewContainer = _viewContainer; - this._context = new NgIfContext(); - this._thenTemplateRef = null; - this._elseTemplateRef = null; - this._thenViewRef = null; - this._elseViewRef = null; - this._thenTemplateRef = templateRef; - } - /** - * The Boolean expression to evaluate as the condition for showing a template. - */ - set ngIf(condition) { - this._context.$implicit = this._context.ngIf = condition; - this._updateView(); - } - /** - * A template to show if the condition expression evaluates to true. - */ - set ngIfThen(templateRef) { - assertTemplate('ngIfThen', templateRef); - this._thenTemplateRef = templateRef; - this._thenViewRef = null; // clear previous view if any. - this._updateView(); - } - /** - * A template to show if the condition expression evaluates to false. - */ - set ngIfElse(templateRef) { - assertTemplate('ngIfElse', templateRef); - this._elseTemplateRef = templateRef; - this._elseViewRef = null; // clear previous view if any. - this._updateView(); - } - _updateView() { - if (this._context.$implicit) { - if (!this._thenViewRef) { - this._viewContainer.clear(); - this._elseViewRef = null; - if (this._thenTemplateRef) { - this._thenViewRef = this._viewContainer.createEmbeddedView( - this._thenTemplateRef, - this._context - ); - } - } - } else { - if (!this._elseViewRef) { - this._viewContainer.clear(); - this._thenViewRef = null; - if (this._elseTemplateRef) { - this._elseViewRef = this._viewContainer.createEmbeddedView( - this._elseTemplateRef, - this._context - ); - } - } - } - } - /** - * Asserts the correct type of the context for the template that `NgIf` will render. - * - * The presence of this method is a signal to the Ivy template type-check compiler that the - * `NgIf` structural directive renders its template with a specific context type. - */ - static ngTemplateContextGuard(dir, ctx) { - return true; - } - } - NgIf.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgIf, - deps: [ - { token: i0__namespace.ViewContainerRef }, - { token: i0__namespace.TemplateRef }, - ], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgIf.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgIf, - isStandalone: true, - selector: '[ngIf]', - inputs: { ngIf: 'ngIf', ngIfThen: 'ngIfThen', ngIfElse: 'ngIfElse' }, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgIf, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngIf]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { type: i0__namespace.ViewContainerRef }, - { type: i0__namespace.TemplateRef }, - ]; - }, - propDecorators: { - ngIf: [ - { - type: i0.Input, - }, - ], - ngIfThen: [ - { - type: i0.Input, - }, - ], - ngIfElse: [ - { - type: i0.Input, - }, - ], - }, - }); - /** - * @publicApi - */ - class NgIfContext { - constructor() { - this.$implicit = null; - this.ngIf = null; - } - } - function assertTemplate(property, templateRef) { - const isTemplateRefOrNull = !!( - !templateRef || templateRef.createEmbeddedView - ); - if (!isTemplateRefOrNull) { - throw new Error( - `${property} must be a TemplateRef, but received '${i0['ɵstringify']( - templateRef - )}'.` - ); - } - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - class SwitchView { - constructor(_viewContainerRef, _templateRef) { - this._viewContainerRef = _viewContainerRef; - this._templateRef = _templateRef; - this._created = false; - } - create() { - this._created = true; - this._viewContainerRef.createEmbeddedView(this._templateRef); - } - destroy() { - this._created = false; - this._viewContainerRef.clear(); - } - enforceState(created) { - if (created && !this._created) { - this.create(); - } else if (!created && this._created) { - this.destroy(); - } - } - } - /** - * @ngModule CommonModule - * - * @description - * The `[ngSwitch]` directive on a container specifies an expression to match against. - * The expressions to match are provided by `ngSwitchCase` directives on views within the container. - * - Every view that matches is rendered. - * - If there are no matches, a view with the `ngSwitchDefault` directive is rendered. - * - Elements within the `[NgSwitch]` statement but outside of any `NgSwitchCase` - * or `ngSwitchDefault` directive are preserved at the location. - * - * @usageNotes - * Define a container element for the directive, and specify the switch expression - * to match against as an attribute: - * - * ``` - * - * ``` - * - * Within the container, `*ngSwitchCase` statements specify the match expressions - * as attributes. Include `*ngSwitchDefault` as the final case. - * - * ``` - * - * ... - * ... - * ... - * - * ``` - * - * ### Usage Examples - * - * The following example shows how to use more than one case to display the same view: - * - * ``` - * - * - * ... - * ... - * ... - * - * ... - * - * ``` - * - * The following example shows how cases can be nested: - * ``` - * - * ... - * ... - * ... - * - * - * - * - * - * ... - * - * ``` - * - * @publicApi - * @see `NgSwitchCase` - * @see `NgSwitchDefault` - * @see [Structural Directives](guide/structural-directives) - * - */ - class NgSwitch { - constructor() { - this._defaultUsed = false; - this._caseCount = 0; - this._lastCaseCheckIndex = 0; - this._lastCasesMatched = false; - } - set ngSwitch(newValue) { - this._ngSwitch = newValue; - if (this._caseCount === 0) { - this._updateDefaultCases(true); - } + NgOptimizedImage.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgOptimizedImage, deps: [], target: i0__namespace.ɵɵFactoryTarget.Directive }); + NgOptimizedImage.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgOptimizedImage, isStandalone: true, selector: "img[ngSrc]", inputs: { ngSrc: "ngSrc", ngSrcset: "ngSrcset", sizes: "sizes", width: "width", height: "height", loading: "loading", priority: "priority", disableOptimizedSrcset: "disableOptimizedSrcset", fill: "fill", src: "src", srcset: "srcset" }, host: { properties: { "style.position": "fill ? \"absolute\" : null", "style.width": "fill ? \"100%\" : null", "style.height": "fill ? \"100%\" : null", "style.inset": "fill ? \"0px\" : null" } }, usesOnChanges: true, ngImport: i0__namespace }); + i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0__namespace, type: NgOptimizedImage, decorators: [{ + type: i0.Directive, + args: [{ + standalone: true, + selector: 'img[ngSrc]', + host: { + '[style.position]': 'fill ? "absolute" : null', + '[style.width]': 'fill ? "100%" : null', + '[style.height]': 'fill ? "100%" : null', + '[style.inset]': 'fill ? "0px" : null' + } + }] + }], propDecorators: { ngSrc: [{ + type: i0.Input + }], ngSrcset: [{ + type: i0.Input + }], sizes: [{ + type: i0.Input + }], width: [{ + type: i0.Input + }], height: [{ + type: i0.Input + }], loading: [{ + type: i0.Input + }], priority: [{ + type: i0.Input + }], disableOptimizedSrcset: [{ + type: i0.Input + }], fill: [{ + type: i0.Input + }], src: [{ + type: i0.Input + }], srcset: [{ + type: i0.Input + }] } }); + /***** Helpers *****/ + /** + * Convert input value to integer. + */ + function inputToInteger(value) { + return typeof value === 'string' ? parseInt(value, 10) : value; } - /** @internal */ - _addCase() { - return this._caseCount++; + /** + * Convert input value to boolean. + */ + function inputToBoolean(value) { + return value != null && `${value}` !== 'false'; } - /** @internal */ - _addDefault(view) { - if (!this._defaultViews) { - this._defaultViews = []; - } - this._defaultViews.push(view); + /** + * Sorts provided config breakpoints and uses defaults. + */ + function processConfig(config) { + let sortedBreakpoints = {}; + if (config.breakpoints) { + sortedBreakpoints.breakpoints = config.breakpoints.sort((a, b) => a - b); + } + return Object.assign({}, defaultConfig, config, sortedBreakpoints); } - /** @internal */ - _matchCase(value) { - const matched = value == this._ngSwitch; - this._lastCasesMatched = this._lastCasesMatched || matched; - this._lastCaseCheckIndex++; - if (this._lastCaseCheckIndex === this._caseCount) { - this._updateDefaultCases(!this._lastCasesMatched); - this._lastCaseCheckIndex = 0; - this._lastCasesMatched = false; - } - return matched; - } - _updateDefaultCases(useDefault) { - if (this._defaultViews && useDefault !== this._defaultUsed) { - this._defaultUsed = useDefault; - for (let i = 0; i < this._defaultViews.length; i++) { - const defaultView = this._defaultViews[i]; - defaultView.enforceState(useDefault); - } - } - } - } - NgSwitch.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgSwitch, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgSwitch.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgSwitch, - isStandalone: true, - selector: '[ngSwitch]', - inputs: { ngSwitch: 'ngSwitch' }, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgSwitch, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngSwitch]', - standalone: true, - }, - ], - }, - ], - propDecorators: { - ngSwitch: [ - { - type: i0.Input, - }, - ], - }, - }); - /** - * @ngModule CommonModule - * - * @description - * Provides a switch case expression to match against an enclosing `ngSwitch` expression. - * When the expressions match, the given `NgSwitchCase` template is rendered. - * If multiple match expressions match the switch expression value, all of them are displayed. - * - * @usageNotes - * - * Within a switch container, `*ngSwitchCase` statements specify the match expressions - * as attributes. Include `*ngSwitchDefault` as the final case. - * - * ``` - * - * ... - * ... - * ... - * - * ``` - * - * Each switch-case statement contains an in-line HTML template or template reference - * that defines the subtree to be selected if the value of the match expression - * matches the value of the switch expression. - * - * Unlike JavaScript, which uses strict equality, Angular uses loose equality. - * This means that the empty string, `""` matches 0. - * - * @publicApi - * @see `NgSwitch` - * @see `NgSwitchDefault` - * - */ - class NgSwitchCase { - constructor(viewContainer, templateRef, ngSwitch) { - this.ngSwitch = ngSwitch; - if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) { - throwNgSwitchProviderNotFoundError('ngSwitchCase', 'NgSwitchCase'); - } - ngSwitch._addCase(); - this._view = new SwitchView(viewContainer, templateRef); - } - /** - * Performs case matching. For internal use only. - * @nodoc - */ - ngDoCheck() { - this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); - } - } - NgSwitchCase.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgSwitchCase, - deps: [ - { token: i0__namespace.ViewContainerRef }, - { token: i0__namespace.TemplateRef }, - { token: NgSwitch, host: true, optional: true }, - ], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgSwitchCase.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgSwitchCase, - isStandalone: true, - selector: '[ngSwitchCase]', - inputs: { ngSwitchCase: 'ngSwitchCase' }, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgSwitchCase, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngSwitchCase]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { type: i0__namespace.ViewContainerRef }, - { type: i0__namespace.TemplateRef }, - { - type: NgSwitch, - decorators: [ - { - type: i0.Optional, - }, - { - type: i0.Host, - }, - ], - }, - ]; - }, - propDecorators: { - ngSwitchCase: [ - { - type: i0.Input, - }, - ], - }, - }); - /** - * @ngModule CommonModule - * - * @description - * - * Creates a view that is rendered when no `NgSwitchCase` expressions - * match the `NgSwitch` expression. - * This statement should be the final case in an `NgSwitch`. - * - * @publicApi - * @see `NgSwitch` - * @see `NgSwitchCase` - * - */ - class NgSwitchDefault { - constructor(viewContainer, templateRef, ngSwitch) { - if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) { - throwNgSwitchProviderNotFoundError( - 'ngSwitchDefault', - 'NgSwitchDefault' - ); - } - ngSwitch._addDefault(new SwitchView(viewContainer, templateRef)); - } - } - NgSwitchDefault.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgSwitchDefault, - deps: [ - { token: i0__namespace.ViewContainerRef }, - { token: i0__namespace.TemplateRef }, - { token: NgSwitch, host: true, optional: true }, - ], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgSwitchDefault.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgSwitchDefault, - isStandalone: true, - selector: '[ngSwitchDefault]', - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgSwitchDefault, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngSwitchDefault]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { type: i0__namespace.ViewContainerRef }, - { type: i0__namespace.TemplateRef }, - { - type: NgSwitch, - decorators: [ - { - type: i0.Optional, - }, - { - type: i0.Host, - }, - ], - }, - ]; - }, - }); - function throwNgSwitchProviderNotFoundError(attrName, directiveName) { - throw new i0[ - 'ɵRuntimeError' - ](2000 /* RuntimeErrorCode.PARENT_NG_SWITCH_NOT_FOUND */, `An element with the "${attrName}" attribute ` + `(matching the "${directiveName}" directive) must be located inside an element with the "ngSwitch" attribute ` + `(matching "NgSwitch" directive)`); - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @ngModule CommonModule - * - * @usageNotes - * ``` - * - * there is nothing - * there is one - * there are a few - * - * ``` - * - * @description - * - * Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization. - * - * Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees - * that match the switch expression's pluralization category. - * - * To use this directive you must provide a container element that sets the `[ngPlural]` attribute - * to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their - * expression: - * - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value - * matches the switch expression exactly, - * - otherwise, the view will be treated as a "category match", and will only display if exact - * value matches aren't found and the value maps to its category for the defined locale. - * - * See http://cldr.unicode.org/index/cldr-spec/plural-rules - * - * @publicApi - */ - class NgPlural { - constructor(_localization) { - this._localization = _localization; - this._caseViews = {}; - } - set ngPlural(value) { - this._switchValue = value; - this._updateView(); - } - addCase(value, switchView) { - this._caseViews[value] = switchView; - } - _updateView() { - this._clearViews(); - const cases = Object.keys(this._caseViews); - const key = getPluralCategory( - this._switchValue, - cases, - this._localization - ); - this._activateView(this._caseViews[key]); - } - _clearViews() { - if (this._activeView) this._activeView.destroy(); - } - _activateView(view) { - if (view) { - this._activeView = view; - this._activeView.create(); - } - } - } - NgPlural.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgPlural, - deps: [{ token: NgLocalization }], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgPlural.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgPlural, - isStandalone: true, - selector: '[ngPlural]', - inputs: { ngPlural: 'ngPlural' }, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgPlural, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngPlural]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [{ type: NgLocalization }]; - }, - propDecorators: { - ngPlural: [ - { - type: i0.Input, - }, - ], - }, - }); - /** - * @ngModule CommonModule - * - * @description - * - * Creates a view that will be added/removed from the parent {@link NgPlural} when the - * given expression matches the plural expression according to CLDR rules. - * - * @usageNotes - * ``` - * - * ... - * ... - * - *``` - * - * See {@link NgPlural} for more details and example. - * - * @publicApi - */ - class NgPluralCase { - constructor(value, template, viewContainer, ngPlural) { - this.value = value; - const isANumber = !isNaN(Number(value)); - ngPlural.addCase( - isANumber ? `=${value}` : value, - new SwitchView(viewContainer, template) - ); - } - } - NgPluralCase.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgPluralCase, - deps: [ - { token: 'ngPluralCase', attribute: true }, - { token: i0__namespace.TemplateRef }, - { token: i0__namespace.ViewContainerRef }, - { token: NgPlural, host: true }, - ], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgPluralCase.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgPluralCase, - isStandalone: true, - selector: '[ngPluralCase]', - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgPluralCase, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngPluralCase]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { - type: undefined, - decorators: [ - { - type: i0.Attribute, - args: ['ngPluralCase'], - }, - ], - }, - { type: i0__namespace.TemplateRef }, - { type: i0__namespace.ViewContainerRef }, - { - type: NgPlural, - decorators: [ - { - type: i0.Host, - }, - ], - }, - ]; - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @ngModule CommonModule - * - * @usageNotes - * - * Set the font of the containing element to the result of an expression. - * - * ``` - * ... - * ``` - * - * Set the width of the containing element to a pixel value returned by an expression. - * - * ``` - * ... - * ``` - * - * Set a collection of style values using an expression that returns key-value pairs. - * - * ``` - * ... - * ``` - * - * @description - * - * An attribute directive that updates styles for the containing HTML element. - * Sets one or more style properties, specified as colon-separated key-value pairs. - * The key is a style name, with an optional `.` suffix - * (such as 'top.px', 'font-style.em'). - * The value is an expression to be evaluated. - * The resulting non-null value, expressed in the given unit, - * is assigned to the given style property. - * If the result of evaluation is null, the corresponding style is removed. - * - * @publicApi - */ - class NgStyle { - constructor(_ngEl, _differs, _renderer) { - this._ngEl = _ngEl; - this._differs = _differs; - this._renderer = _renderer; - this._ngStyle = null; - this._differ = null; - } - set ngStyle(values) { - this._ngStyle = values; - if (!this._differ && values) { - this._differ = this._differs.find(values).create(); - } - } - ngDoCheck() { - if (this._differ) { - const changes = this._differ.diff(this._ngStyle); - if (changes) { - this._applyChanges(changes); - } - } - } - _setStyle(nameAndUnit, value) { - const [name, unit] = nameAndUnit.split('.'); - const flags = - name.indexOf('-') === -1 ? undefined : i0.RendererStyleFlags2.DashCase; - if (value != null) { - this._renderer.setStyle( - this._ngEl.nativeElement, - name, - unit ? `${value}${unit}` : value, - flags - ); - } else { - this._renderer.removeStyle(this._ngEl.nativeElement, name, flags); - } - } - _applyChanges(changes) { - changes.forEachRemovedItem((record) => this._setStyle(record.key, null)); - changes.forEachAddedItem((record) => - this._setStyle(record.key, record.currentValue) - ); - changes.forEachChangedItem((record) => - this._setStyle(record.key, record.currentValue) - ); - } - } - NgStyle.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgStyle, - deps: [ - { token: i0__namespace.ElementRef }, - { token: i0__namespace.KeyValueDiffers }, - { token: i0__namespace.Renderer2 }, - ], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgStyle.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgStyle, - isStandalone: true, - selector: '[ngStyle]', - inputs: { ngStyle: 'ngStyle' }, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgStyle, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngStyle]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { type: i0__namespace.ElementRef }, - { type: i0__namespace.KeyValueDiffers }, - { type: i0__namespace.Renderer2 }, - ]; - }, - propDecorators: { - ngStyle: [ - { - type: i0.Input, - args: ['ngStyle'], - }, - ], - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @ngModule CommonModule - * - * @description - * - * Inserts an embedded view from a prepared `TemplateRef`. - * - * You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`. - * `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding - * by the local template `let` declarations. - * - * @usageNotes - * ``` - * - * ``` - * - * Using the key `$implicit` in the context object will set its value as default. - * - * ### Example - * - * {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'} - * - * @publicApi - */ - class NgTemplateOutlet { - constructor(_viewContainerRef) { - this._viewContainerRef = _viewContainerRef; - this._viewRef = null; - /** - * A context object to attach to the {@link EmbeddedViewRef}. This should be an - * object, the object's keys will be available for binding by the local template `let` - * declarations. - * Using the key `$implicit` in the context object will set its value as default. - */ - this.ngTemplateOutletContext = null; - /** - * A string defining the template reference and optionally the context object for the template. - */ - this.ngTemplateOutlet = null; - /** Injector to be used within the embedded view. */ - this.ngTemplateOutletInjector = null; - } - /** @nodoc */ - ngOnChanges(changes) { - if (changes['ngTemplateOutlet'] || changes['ngTemplateOutletInjector']) { - const viewContainerRef = this._viewContainerRef; - if (this._viewRef) { - viewContainerRef.remove(viewContainerRef.indexOf(this._viewRef)); - } - if (this.ngTemplateOutlet) { - const { - ngTemplateOutlet: template, - ngTemplateOutletContext: context, - ngTemplateOutletInjector: injector, - } = this; - this._viewRef = viewContainerRef.createEmbeddedView( - template, - context, - injector ? { injector } : undefined - ); - } else { - this._viewRef = null; - } - } else if ( - this._viewRef && - changes['ngTemplateOutletContext'] && - this.ngTemplateOutletContext - ) { - this._viewRef.context = this.ngTemplateOutletContext; - } - } - } - NgTemplateOutlet.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgTemplateOutlet, - deps: [{ token: i0__namespace.ViewContainerRef }], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgTemplateOutlet.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgTemplateOutlet, - isStandalone: true, - selector: '[ngTemplateOutlet]', - inputs: { - ngTemplateOutletContext: 'ngTemplateOutletContext', - ngTemplateOutlet: 'ngTemplateOutlet', - ngTemplateOutletInjector: 'ngTemplateOutletInjector', - }, - usesOnChanges: true, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgTemplateOutlet, - decorators: [ - { - type: i0.Directive, - args: [ - { - selector: '[ngTemplateOutlet]', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [{ type: i0__namespace.ViewContainerRef }]; - }, - propDecorators: { - ngTemplateOutletContext: [ - { - type: i0.Input, - }, - ], - ngTemplateOutlet: [ - { - type: i0.Input, - }, - ], - ngTemplateOutletInjector: [ - { - type: i0.Input, - }, - ], - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * A collection of Angular directives that are likely to be used in each and every Angular - * application. - */ - const COMMON_DIRECTIVES = [ - NgClass, - NgComponentOutlet, - NgForOf, - NgIf, - NgTemplateOutlet, - NgStyle, - NgSwitch, - NgSwitchCase, - NgSwitchDefault, - NgPlural, - NgPluralCase, - ]; - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - function invalidPipeArgumentError(type, value) { - return new i0[ - 'ɵRuntimeError' - ](2100 /* RuntimeErrorCode.INVALID_PIPE_ARGUMENT */, ngDevMode && `InvalidPipeArgument: '${value}' for pipe '${i0['ɵstringify'](type)}'`); - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - class SubscribableStrategy { - createSubscription(async, updateLatestValue) { - return async.subscribe({ - next: updateLatestValue, - error: (e) => { - throw e; - }, - }); - } - dispose(subscription) { - subscription.unsubscribe(); - } - } - class PromiseStrategy { - createSubscription(async, updateLatestValue) { - return async.then(updateLatestValue, (e) => { - throw e; - }); - } - dispose(subscription) {} - } - const _promiseStrategy = new PromiseStrategy(); - const _subscribableStrategy = new SubscribableStrategy(); - /** - * @ngModule CommonModule - * @description - * - * Unwraps a value from an asynchronous primitive. - * - * The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has - * emitted. When a new value is emitted, the `async` pipe marks the component to be checked for - * changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid - * potential memory leaks. When the reference of the expression changes, the `async` pipe - * automatically unsubscribes from the old `Observable` or `Promise` and subscribes to the new one. - * - * @usageNotes - * - * ### Examples - * - * This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the - * promise. - * - * {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'} - * - * It's also possible to use `async` with Observables. The example below binds the `time` Observable - * to the view. The Observable continuously updates the view with the current time. - * - * {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'} - * - * @publicApi - */ - class AsyncPipe { - constructor(ref) { - this._latestValue = null; - this._subscription = null; - this._obj = null; - this._strategy = null; - // Assign `ref` into `this._ref` manually instead of declaring `_ref` in the constructor - // parameter list, as the type of `this._ref` includes `null` unlike the type of `ref`. - this._ref = ref; - } - ngOnDestroy() { - if (this._subscription) { - this._dispose(); - } - // Clear the `ChangeDetectorRef` and its association with the view data, to mitigate - // potential memory leaks in Observables that could otherwise cause the view data to - // be retained. - // https://github.com/angular/angular/issues/17624 - this._ref = null; - } - transform(obj) { - if (!this._obj) { - if (obj) { - this._subscribe(obj); - } - return this._latestValue; - } - if (obj !== this._obj) { - this._dispose(); - return this.transform(obj); - } - return this._latestValue; - } - _subscribe(obj) { - this._obj = obj; - this._strategy = this._selectStrategy(obj); - this._subscription = this._strategy.createSubscription(obj, (value) => - this._updateLatestValue(obj, value) - ); - } - _selectStrategy(obj) { - if (i0['ɵisPromise'](obj)) { - return _promiseStrategy; - } - if (i0['ɵisSubscribable'](obj)) { - return _subscribableStrategy; - } - throw invalidPipeArgumentError(AsyncPipe, obj); - } - _dispose() { - // Note: `dispose` is only called if a subscription has been initialized before, indicating - // that `this._strategy` is also available. - this._strategy.dispose(this._subscription); - this._latestValue = null; - this._subscription = null; - this._obj = null; - } - _updateLatestValue(async, value) { - if (async === this._obj) { - this._latestValue = value; - // Note: `this._ref` is only cleared in `ngOnDestroy` so is known to be available when a - // value is being updated. - this._ref.markForCheck(); - } - } - } - AsyncPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: AsyncPipe, - deps: [{ token: i0__namespace.ChangeDetectorRef }], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - AsyncPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: AsyncPipe, - isStandalone: true, - name: 'async', - pure: false, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: AsyncPipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'async', - pure: false, - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [{ type: i0__namespace.ChangeDetectorRef }]; - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Transforms text to all lower case. - * - * @see `UpperCasePipe` - * @see `TitleCasePipe` - * @usageNotes - * - * The following example defines a view that allows the user to enter - * text, and then uses the pipe to convert the input text to all lower case. - * - * - * - * @ngModule CommonModule - * @publicApi - */ - class LowerCasePipe { - transform(value) { - if (value == null) return null; - if (typeof value !== 'string') { - throw invalidPipeArgumentError(LowerCasePipe, value); - } - return value.toLowerCase(); - } - } - LowerCasePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LowerCasePipe, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - LowerCasePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LowerCasePipe, - isStandalone: true, - name: 'lowercase', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LowerCasePipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'lowercase', - standalone: true, - }, - ], - }, - ], - }); - // - // Regex below matches any Unicode word and number compatible with ES5. In ES2018 the same result - // can be achieved by using /[0-9\p{L}]\S*/gu and also known as Unicode Property Escapes - // (https://2ality.com/2017/07/regexp-unicode-property-escapes.html). Since there is no - // transpilation of this functionality down to ES5 without external tool, the only solution is - // to use already transpiled form. Example can be found here - - // https://mothereff.in/regexpu#input=var+regex+%3D+%2F%5B0-9%5Cp%7BL%7D%5D%5CS*%2Fgu%3B%0A%0A&unicodePropertyEscape=1 - // - const unicodeWordMatch = - /(?:[0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF38\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])\S*/g; - /** - * Transforms text to title case. - * Capitalizes the first letter of each word and transforms the - * rest of the word to lower case. - * Words are delimited by any whitespace character, such as a space, tab, or line-feed character. - * - * @see `LowerCasePipe` - * @see `UpperCasePipe` - * - * @usageNotes - * The following example shows the result of transforming various strings into title case. - * - * - * - * @ngModule CommonModule - * @publicApi - */ - class TitleCasePipe { - transform(value) { - if (value == null) return null; - if (typeof value !== 'string') { - throw invalidPipeArgumentError(TitleCasePipe, value); - } - return value.replace( - unicodeWordMatch, - (txt) => txt[0].toUpperCase() + txt.slice(1).toLowerCase() - ); - } - } - TitleCasePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: TitleCasePipe, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - TitleCasePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: TitleCasePipe, - isStandalone: true, - name: 'titlecase', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: TitleCasePipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'titlecase', - standalone: true, - }, - ], - }, - ], - }); - /** - * Transforms text to all upper case. - * @see `LowerCasePipe` - * @see `TitleCasePipe` - * - * @ngModule CommonModule - * @publicApi - */ - class UpperCasePipe { - transform(value) { - if (value == null) return null; - if (typeof value !== 'string') { - throw invalidPipeArgumentError(UpperCasePipe, value); - } - return value.toUpperCase(); - } - } - UpperCasePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: UpperCasePipe, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - UpperCasePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: UpperCasePipe, - isStandalone: true, - name: 'uppercase', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: UpperCasePipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'uppercase', - standalone: true, - }, - ], - }, - ], - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * The default date format of Angular date pipe, which corresponds to the following format: - * `'MMM d,y'` (e.g. `Jun 15, 2015`) - */ - const DEFAULT_DATE_FORMAT = 'mediumDate'; - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Optionally-provided default timezone to use for all instances of `DatePipe` (such as `'+0430'`). - * If the value isn't provided, the `DatePipe` will use the end-user's local system timezone. - * - * @deprecated use DATE_PIPE_DEFAULT_OPTIONS token to configure DatePipe - */ - const DATE_PIPE_DEFAULT_TIMEZONE = new i0.InjectionToken( - 'DATE_PIPE_DEFAULT_TIMEZONE' - ); - /** - * DI token that allows to provide default configuration for the `DatePipe` instances in an - * application. The value is an object which can include the following fields: - * - `dateFormat`: configures the default date format. If not provided, the `DatePipe` - * will use the 'mediumDate' as a value. - * - `timezone`: configures the default timezone. If not provided, the `DatePipe` will - * use the end-user's local system timezone. - * - * @see `DatePipeConfig` - * - * @usageNotes - * - * Various date pipe default values can be overwritten by providing this token with - * the value that has this interface. - * - * For example: - * - * Override the default date format by providing a value using the token: - * ```typescript - * providers: [ - * {provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {dateFormat: 'shortDate'}} - * ] - * ``` - * - * Override the default timezone by providing a value using the token: - * ```typescript - * providers: [ - * {provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {timezone: '-1200'}} - * ] - * ``` - */ - const DATE_PIPE_DEFAULT_OPTIONS = new i0.InjectionToken( - 'DATE_PIPE_DEFAULT_OPTIONS' - ); - // clang-format off - /** - * @ngModule CommonModule - * @description - * - * Formats a date value according to locale rules. - * - * `DatePipe` is executed only when it detects a pure change to the input value. - * A pure change is either a change to a primitive input value - * (such as `String`, `Number`, `Boolean`, or `Symbol`), - * or a changed object reference (such as `Date`, `Array`, `Function`, or `Object`). - * - * Note that mutating a `Date` object does not cause the pipe to be rendered again. - * To ensure that the pipe is executed, you must create a new `Date` object. - * - * Only the `en-US` locale data comes with Angular. To localize dates - * in another language, you must import the corresponding locale data. - * See the [I18n guide](guide/i18n-common-format-data-locale) for more information. - * - * The time zone of the formatted value can be specified either by passing it in as the second - * parameter of the pipe, or by setting the default through the `DATE_PIPE_DEFAULT_TIMEZONE` - * injection token. The value that is passed in as the second parameter takes precedence over - * the one defined using the injection token. - * - * @see `formatDate()` - * - * - * @usageNotes - * - * The result of this pipe is not reevaluated when the input is mutated. To avoid the need to - * reformat the date on every change-detection cycle, treat the date as an immutable object - * and change the reference when the pipe needs to run again. - * - * ### Pre-defined format options - * - * | Option | Equivalent to | Examples (given in `en-US` locale) | - * |---------------|-------------------------------------|-------------------------------------------------| - * | `'short'` | `'M/d/yy, h:mm a'` | `6/15/15, 9:03 AM` | - * | `'medium'` | `'MMM d, y, h:mm:ss a'` | `Jun 15, 2015, 9:03:01 AM` | - * | `'long'` | `'MMMM d, y, h:mm:ss a z'` | `June 15, 2015 at 9:03:01 AM GMT+1` | - * | `'full'` | `'EEEE, MMMM d, y, h:mm:ss a zzzz'` | `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00` | - * | `'shortDate'` | `'M/d/yy'` | `6/15/15` | - * | `'mediumDate'`| `'MMM d, y'` | `Jun 15, 2015` | - * | `'longDate'` | `'MMMM d, y'` | `June 15, 2015` | - * | `'fullDate'` | `'EEEE, MMMM d, y'` | `Monday, June 15, 2015` | - * | `'shortTime'` | `'h:mm a'` | `9:03 AM` | - * | `'mediumTime'`| `'h:mm:ss a'` | `9:03:01 AM` | - * | `'longTime'` | `'h:mm:ss a z'` | `9:03:01 AM GMT+1` | - * | `'fullTime'` | `'h:mm:ss a zzzz'` | `9:03:01 AM GMT+01:00` | - * - * ### Custom format options - * - * You can construct a format string using symbols to specify the components - * of a date-time value, as described in the following table. - * Format details depend on the locale. - * Fields marked with (*) are only available in the extra data set for the given locale. - * - * | Field type | Format | Description | Example Value | - * |-------------------- |-------------|---------------------------------------------------------------|------------------------------------------------------------| - * | Era | G, GG & GGG | Abbreviated | AD | - * | | GGGG | Wide | Anno Domini | - * | | GGGGG | Narrow | A | - * | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | - * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | - * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | - * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | - * | Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | - * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | - * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | - * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | - * | Month | M | Numeric: 1 digit | 9, 12 | - * | | MM | Numeric: 2 digits + zero padded | 09, 12 | - * | | MMM | Abbreviated | Sep | - * | | MMMM | Wide | September | - * | | MMMMM | Narrow | S | - * | Month standalone | L | Numeric: 1 digit | 9, 12 | - * | | LL | Numeric: 2 digits + zero padded | 09, 12 | - * | | LLL | Abbreviated | Sep | - * | | LLLL | Wide | September | - * | | LLLLL | Narrow | S | - * | Week of year | w | Numeric: minimum digits | 1... 53 | - * | | ww | Numeric: 2 digits + zero padded | 01... 53 | - * | Week of month | W | Numeric: 1 digit | 1... 5 | - * | Day of month | d | Numeric: minimum digits | 1 | - * | | dd | Numeric: 2 digits + zero padded | 01 | - * | Week day | E, EE & EEE | Abbreviated | Tue | - * | | EEEE | Wide | Tuesday | - * | | EEEEE | Narrow | T | - * | | EEEEEE | Short | Tu | - * | Week day standalone | c, cc | Numeric: 1 digit | 2 | - * | | ccc | Abbreviated | Tue | - * | | cccc | Wide | Tuesday | - * | | ccccc | Narrow | T | - * | | cccccc | Short | Tu | - * | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | - * | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | - * | | aaaaa | Narrow | a/p | - * | Period* | B, BB & BBB | Abbreviated | mid. | - * | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | - * | | BBBBB | Narrow | md | - * | Period standalone* | b, bb & bbb | Abbreviated | mid. | - * | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | - * | | bbbbb | Narrow | md | - * | Hour 1-12 | h | Numeric: minimum digits | 1, 12 | - * | | hh | Numeric: 2 digits + zero padded | 01, 12 | - * | Hour 0-23 | H | Numeric: minimum digits | 0, 23 | - * | | HH | Numeric: 2 digits + zero padded | 00, 23 | - * | Minute | m | Numeric: minimum digits | 8, 59 | - * | | mm | Numeric: 2 digits + zero padded | 08, 59 | - * | Second | s | Numeric: minimum digits | 0... 59 | - * | | ss | Numeric: 2 digits + zero padded | 00... 59 | - * | Fractional seconds | S | Numeric: 1 digit | 0... 9 | - * | | SS | Numeric: 2 digits + zero padded | 00... 99 | - * | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | - * | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | - * | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | - * | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | - * | | ZZZZ | Long localized GMT format | GMT-8:00 | - * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | - * | | O, OO & OOO | Short localized GMT format | GMT-8 | - * | | OOOO | Long localized GMT format | GMT-08:00 | - * - * - * ### Format examples - * - * These examples transform a date into various formats, - * assuming that `dateObj` is a JavaScript `Date` object for - * year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11, - * given in the local time for the `en-US` locale. - * - * ``` - * {{ dateObj | date }} // output is 'Jun 15, 2015' - * {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM' - * {{ dateObj | date:'shortTime' }} // output is '9:43 PM' - * {{ dateObj | date:'mm:ss' }} // output is '43:11' - * ``` - * - * ### Usage example - * - * The following component uses a date pipe to display the current date in different formats. - * - * ``` - * @Component({ - * selector: 'date-pipe', - * template: `
    - *

    Today is {{today | date}}

    - *

    Or if you prefer, {{today | date:'fullDate'}}

    - *

    The time is {{today | date:'h:mm a z'}}

    - *
    ` - * }) - * // Get the current date and time as a date-time value. - * export class DatePipeComponent { - * today: number = Date.now(); - * } - * ``` - * - * @publicApi - */ - // clang-format on - class DatePipe { - constructor(locale, defaultTimezone, defaultOptions) { - this.locale = locale; - this.defaultTimezone = defaultTimezone; - this.defaultOptions = defaultOptions; - } - transform(value, format, timezone, locale) { - var _a, _b, _c, _d, _e; - if (value == null || value === '' || value !== value) return null; - try { - const _format = - (_b = - format !== null && format !== void 0 - ? format - : (_a = this.defaultOptions) === null || _a === void 0 - ? void 0 - : _a.dateFormat) !== null && _b !== void 0 - ? _b - : DEFAULT_DATE_FORMAT; - const _timezone = - (_e = - (_d = - timezone !== null && timezone !== void 0 - ? timezone - : (_c = this.defaultOptions) === null || _c === void 0 - ? void 0 - : _c.timezone) !== null && _d !== void 0 - ? _d - : this.defaultTimezone) !== null && _e !== void 0 - ? _e - : undefined; - return formatDate(value, _format, locale || this.locale, _timezone); - } catch (error) { - throw invalidPipeArgumentError(DatePipe, error.message); - } - } - } - DatePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: DatePipe, - deps: [ - { token: i0.LOCALE_ID }, - { token: DATE_PIPE_DEFAULT_TIMEZONE, optional: true }, - { token: DATE_PIPE_DEFAULT_OPTIONS, optional: true }, - ], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - DatePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: DatePipe, - isStandalone: true, - name: 'date', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: DatePipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'date', - pure: true, - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [i0.LOCALE_ID], - }, - ], - }, - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [DATE_PIPE_DEFAULT_TIMEZONE], - }, - { - type: i0.Optional, - }, - ], - }, - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [DATE_PIPE_DEFAULT_OPTIONS], - }, - { - type: i0.Optional, - }, - ], - }, - ]; - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - const _INTERPOLATION_REGEXP = /#/g; - /** - * @ngModule CommonModule - * @description - * - * Maps a value to a string that pluralizes the value according to locale rules. - * - * @usageNotes - * - * ### Example - * - * {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'} - * - * @publicApi - */ - class I18nPluralPipe { - constructor(_localization) { - this._localization = _localization; - } - /** - * @param value the number to be formatted - * @param pluralMap an object that mimics the ICU format, see - * https://unicode-org.github.io/icu/userguide/format_parse/messages/. - * @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by - * default). - */ - transform(value, pluralMap, locale) { - if (value == null) return ''; - if (typeof pluralMap !== 'object' || pluralMap === null) { - throw invalidPipeArgumentError(I18nPluralPipe, pluralMap); - } - const key = getPluralCategory( - value, - Object.keys(pluralMap), - this._localization, - locale - ); - return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString()); - } - } - I18nPluralPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: I18nPluralPipe, - deps: [{ token: NgLocalization }], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - I18nPluralPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: I18nPluralPipe, - isStandalone: true, - name: 'i18nPlural', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: I18nPluralPipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'i18nPlural', - pure: true, - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [{ type: NgLocalization }]; - }, - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @ngModule CommonModule - * @description - * - * Generic selector that displays the string that matches the current value. - * - * If none of the keys of the `mapping` match the `value`, then the content - * of the `other` key is returned when present, otherwise an empty string is returned. - * - * @usageNotes - * - * ### Example - * - * {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'} - * - * @publicApi - */ - class I18nSelectPipe { - /** - * @param value a string to be internationalized. - * @param mapping an object that indicates the text that should be displayed - * for different values of the provided `value`. - */ - transform(value, mapping) { - if (value == null) return ''; - if (typeof mapping !== 'object' || typeof value !== 'string') { - throw invalidPipeArgumentError(I18nSelectPipe, mapping); - } - if (mapping.hasOwnProperty(value)) { - return mapping[value]; - } - if (mapping.hasOwnProperty('other')) { - return mapping['other']; - } - return ''; - } - } - I18nSelectPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: I18nSelectPipe, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - I18nSelectPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: I18nSelectPipe, - isStandalone: true, - name: 'i18nSelect', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: I18nSelectPipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'i18nSelect', - pure: true, - standalone: true, - }, - ], - }, - ], - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @ngModule CommonModule - * @description - * - * Converts a value into its JSON-format representation. Useful for debugging. - * - * @usageNotes - * - * The following component uses a JSON pipe to convert an object - * to JSON format, and displays the string in both formats for comparison. - * - * {@example common/pipes/ts/json_pipe.ts region='JsonPipe'} - * - * @publicApi - */ - class JsonPipe { - /** - * @param value A value of any type to convert into a JSON-format string. - */ - transform(value) { - return JSON.stringify(value, null, 2); - } - } - JsonPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: JsonPipe, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - JsonPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: JsonPipe, - isStandalone: true, - name: 'json', - pure: false, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: JsonPipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'json', - pure: false, - standalone: true, - }, - ], - }, - ], - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - function makeKeyValuePair(key, value) { - return { key: key, value: value }; - } - /** - * @ngModule CommonModule - * @description - * - * Transforms Object or Map into an array of key value pairs. - * - * The output array will be ordered by keys. - * By default the comparator will be by Unicode point value. - * You can optionally pass a compareFn if your keys are complex types. - * - * @usageNotes - * ### Examples - * - * This examples show how an Object or a Map can be iterated by ngFor with the use of this - * keyvalue pipe. - * - * {@example common/pipes/ts/keyvalue_pipe.ts region='KeyValuePipe'} - * - * @publicApi - */ - class KeyValuePipe { - constructor(differs) { - this.differs = differs; - this.keyValues = []; - this.compareFn = defaultComparator; - } - transform(input, compareFn = defaultComparator) { - if (!input || (!(input instanceof Map) && typeof input !== 'object')) { - return null; - } - if (!this.differ) { - // make a differ for whatever type we've been passed in - this.differ = this.differs.find(input).create(); - } - const differChanges = this.differ.diff(input); - const compareFnChanged = compareFn !== this.compareFn; - if (differChanges) { - this.keyValues = []; - differChanges.forEachItem((r) => { - this.keyValues.push(makeKeyValuePair(r.key, r.currentValue)); + /***** Assert functions *****/ + /** + * Verifies that there is no `src` set on a host element. + */ + function assertNoConflictingSrc(dir) { + if (dir.src) { + throw new i0["ɵRuntimeError"](2950 /* RuntimeErrorCode.UNEXPECTED_SRC_ATTR */, `${imgDirectiveDetails(dir.ngSrc)} both \`src\` and \`ngSrc\` have been set. ` + + `Supplying both of these attributes breaks lazy loading. ` + + `The NgOptimizedImage directive sets \`src\` itself based on the value of \`ngSrc\`. ` + + `To fix this, please remove the \`src\` attribute.`); + } + } + /** + * Verifies that there is no `srcset` set on a host element. + */ + function assertNoConflictingSrcset(dir) { + if (dir.srcset) { + throw new i0["ɵRuntimeError"](2951 /* RuntimeErrorCode.UNEXPECTED_SRCSET_ATTR */, `${imgDirectiveDetails(dir.ngSrc)} both \`srcset\` and \`ngSrcset\` have been set. ` + + `Supplying both of these attributes breaks lazy loading. ` + + `The NgOptimizedImage directive sets \`srcset\` itself based on the value of ` + + `\`ngSrcset\`. To fix this, please remove the \`srcset\` attribute.`); + } + } + /** + * Verifies that the `ngSrc` is not a Base64-encoded image. + */ + function assertNotBase64Image(dir) { + let ngSrc = dir.ngSrc.trim(); + if (ngSrc.startsWith('data:')) { + if (ngSrc.length > BASE64_IMG_MAX_LENGTH_IN_ERROR) { + ngSrc = ngSrc.substring(0, BASE64_IMG_MAX_LENGTH_IN_ERROR) + '...'; + } + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc, false)} \`ngSrc\` is a Base64-encoded string ` + + `(${ngSrc}). NgOptimizedImage does not support Base64-encoded strings. ` + + `To fix this, disable the NgOptimizedImage directive for this element ` + + `by removing \`ngSrc\` and using a standard \`src\` attribute instead.`); + } + } + /** + * Verifies that the 'sizes' only includes responsive values. + */ + function assertNoComplexSizes(dir) { + let sizes = dir.sizes; + if (sizes === null || sizes === void 0 ? void 0 : sizes.match(/((\)|,)\s|^)\d+px/)) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc, false)} \`sizes\` was set to a string including ` + + `pixel values. For automatic \`srcset\` generation, \`sizes\` must only include responsive ` + + `values, such as \`sizes="50vw"\` or \`sizes="(min-width: 768px) 50vw, 100vw"\`. ` + + `To fix this, modify the \`sizes\` attribute, or provide your own \`ngSrcset\` value directly.`); + } + } + /** + * Verifies that the `ngSrc` is not a Blob URL. + */ + function assertNotBlobUrl(dir) { + const ngSrc = dir.ngSrc.trim(); + if (ngSrc.startsWith('blob:')) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`ngSrc\` was set to a blob URL (${ngSrc}). ` + + `Blob URLs are not supported by the NgOptimizedImage directive. ` + + `To fix this, disable the NgOptimizedImage directive for this element ` + + `by removing \`ngSrc\` and using a regular \`src\` attribute instead.`); + } + } + /** + * Verifies that the input is set to a non-empty string. + */ + function assertNonEmptyInput(dir, name, value) { + const isString = typeof value === 'string'; + const isEmptyString = isString && value.trim() === ''; + if (!isString || isEmptyString) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`${name}\` has an invalid value ` + + `(\`${value}\`). To fix this, change the value to a non-empty string.`); + } + } + /** + * Verifies that the `ngSrcset` is in a valid format, e.g. "100w, 200w" or "1x, 2x". + */ + function assertValidNgSrcset(dir, value) { + if (value == null) + return; + assertNonEmptyInput(dir, 'ngSrcset', value); + const stringVal = value; + const isValidWidthDescriptor = VALID_WIDTH_DESCRIPTOR_SRCSET.test(stringVal); + const isValidDensityDescriptor = VALID_DENSITY_DESCRIPTOR_SRCSET.test(stringVal); + if (isValidDensityDescriptor) { + assertUnderDensityCap(dir, stringVal); + } + const isValidSrcset = isValidWidthDescriptor || isValidDensityDescriptor; + if (!isValidSrcset) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`ngSrcset\` has an invalid value (\`${value}\`). ` + + `To fix this, supply \`ngSrcset\` using a comma-separated list of one or more width ` + + `descriptors (e.g. "100w, 200w") or density descriptors (e.g. "1x, 2x").`); + } + } + function assertUnderDensityCap(dir, value) { + const underDensityCap = value.split(',').every(num => num === '' || parseFloat(num) <= ABSOLUTE_SRCSET_DENSITY_CAP); + if (!underDensityCap) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`ngSrcset\` contains an unsupported image density:` + + `\`${value}\`. NgOptimizedImage generally recommends a max image density of ` + + `${RECOMMENDED_SRCSET_DENSITY_CAP}x but supports image densities up to ` + + `${ABSOLUTE_SRCSET_DENSITY_CAP}x. The human eye cannot distinguish between image densities ` + + `greater than ${RECOMMENDED_SRCSET_DENSITY_CAP}x - which makes them unnecessary for ` + + `most use cases. Images that will be pinch-zoomed are typically the primary use case for ` + + `${ABSOLUTE_SRCSET_DENSITY_CAP}x images. Please remove the high density descriptor and try again.`); + } + } + /** + * Creates a `RuntimeError` instance to represent a situation when an input is set after + * the directive has initialized. + */ + function postInitInputChangeError(dir, inputName) { + let reason; + if (inputName === 'width' || inputName === 'height') { + reason = `Changing \`${inputName}\` may result in different attribute value ` + + `applied to the underlying image element and cause layout shifts on a page.`; + } + else { + reason = `Changing the \`${inputName}\` would have no effect on the underlying ` + + `image element, because the resource loading has already occurred.`; + } + return new i0["ɵRuntimeError"](2953 /* RuntimeErrorCode.UNEXPECTED_INPUT_CHANGE */, `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` was updated after initialization. ` + + `The NgOptimizedImage directive will not react to this input change. ${reason} ` + + `To fix this, either switch \`${inputName}\` to a static value ` + + `or wrap the image element in an *ngIf that is gated on the necessary value.`); + } + /** + * Verify that none of the listed inputs has changed. + */ + function assertNoPostInitInputChange(dir, changes, inputs) { + inputs.forEach(input => { + const isUpdated = changes.hasOwnProperty(input); + if (isUpdated && !changes[input].isFirstChange()) { + if (input === 'ngSrc') { + // When the `ngSrc` input changes, we detect that only in the + // `ngOnChanges` hook, thus the `ngSrc` is already set. We use + // `ngSrc` in the error message, so we use a previous value, but + // not the updated one in it. + dir = { ngSrc: changes[input].previousValue }; + } + throw postInitInputChangeError(dir, input); + } }); - } - if (differChanges || compareFnChanged) { - this.keyValues.sort(compareFn); - this.compareFn = compareFn; - } - return this.keyValues; - } - } - KeyValuePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: KeyValuePipe, - deps: [{ token: i0__namespace.KeyValueDiffers }], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - KeyValuePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: KeyValuePipe, - isStandalone: true, - name: 'keyvalue', - pure: false, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: KeyValuePipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'keyvalue', - pure: false, - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [{ type: i0__namespace.KeyValueDiffers }]; - }, - }); - function defaultComparator(keyValueA, keyValueB) { - const a = keyValueA.key; - const b = keyValueB.key; - // if same exit with 0; - if (a === b) return 0; - // make sure that undefined are at the end of the sort. - if (a === undefined) return 1; - if (b === undefined) return -1; - // make sure that nulls are at the end of the sort. - if (a === null) return 1; - if (b === null) return -1; - if (typeof a == 'string' && typeof b == 'string') { - return a < b ? -1 : 1; - } - if (typeof a == 'number' && typeof b == 'number') { - return a - b; - } - if (typeof a == 'boolean' && typeof b == 'boolean') { - return a < b ? -1 : 1; - } - // `a` and `b` are of different types. Compare their string values. - const aString = String(a); - const bString = String(b); - return aString == bString ? 0 : aString < bString ? -1 : 1; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @ngModule CommonModule - * @description - * - * Formats a value according to digit options and locale rules. - * Locale determines group sizing and separator, - * decimal point character, and other locale-specific configurations. - * - * @see `formatNumber()` - * - * @usageNotes - * - * ### digitsInfo - * - * The value's decimal representation is specified by the `digitsInfo` - * parameter, written in the following format:
    - * - * ``` - * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits} - * ``` - * - * - `minIntegerDigits`: - * The minimum number of integer digits before the decimal point. - * Default is 1. - * - * - `minFractionDigits`: - * The minimum number of digits after the decimal point. - * Default is 0. - * - * - `maxFractionDigits`: - * The maximum number of digits after the decimal point. - * Default is 3. - * - * If the formatted value is truncated it will be rounded using the "to-nearest" method: - * - * ``` - * {{3.6 | number: '1.0-0'}} - * - * - * {{-3.6 | number:'1.0-0'}} - * - * ``` - * - * ### locale - * - * `locale` will format a value according to locale rules. - * Locale determines group sizing and separator, - * decimal point character, and other locale-specific configurations. - * - * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. - * - * See [Setting your app locale](guide/i18n-common-locale-id). - * - * ### Example - * - * The following code shows how the pipe transforms values - * according to various format specifications, - * where the caller's default locale is `en-US`. - * - * - * - * @publicApi - */ - class DecimalPipe { - constructor(_locale) { - this._locale = _locale; - } - /** - * @param value The value to be formatted. - * @param digitsInfo Sets digit and decimal representation. - * [See more](#digitsinfo). - * @param locale Specifies what locale format rules to use. - * [See more](#locale). - */ - transform(value, digitsInfo, locale) { - if (!isValue(value)) return null; - locale = locale || this._locale; - try { - const num = strToNumber(value); - return formatNumber(num, locale, digitsInfo); - } catch (error) { - throw invalidPipeArgumentError(DecimalPipe, error.message); - } - } - } - DecimalPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: DecimalPipe, - deps: [{ token: i0.LOCALE_ID }], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - DecimalPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: DecimalPipe, - isStandalone: true, - name: 'number', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: DecimalPipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'number', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [i0.LOCALE_ID], - }, - ], - }, - ]; - }, - }); - /** - * @ngModule CommonModule - * @description - * - * Transforms a number to a percentage - * string, formatted according to locale rules that determine group sizing and - * separator, decimal-point character, and other locale-specific - * configurations. - * - * @see `formatPercent()` - * - * @usageNotes - * The following code shows how the pipe transforms numbers - * into text strings, according to various format specifications, - * where the caller's default locale is `en-US`. - * - * - * - * @publicApi - */ - class PercentPipe { - constructor(_locale) { - this._locale = _locale; - } - /** - * - * @param value The number to be formatted as a percentage. - * @param digitsInfo Decimal representation options, specified by a string - * in the following format:
    - * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. - * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. - * Default is `1`. - * - `minFractionDigits`: The minimum number of digits after the decimal point. - * Default is `0`. - * - `maxFractionDigits`: The maximum number of digits after the decimal point. - * Default is `0`. - * @param locale A locale code for the locale format rules to use. - * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. - * See [Setting your app locale](guide/i18n-common-locale-id). + } + /** + * Verifies that a specified input is a number greater than 0. */ - transform(value, digitsInfo, locale) { - if (!isValue(value)) return null; - locale = locale || this._locale; - try { - const num = strToNumber(value); - return formatPercent(num, locale, digitsInfo); - } catch (error) { - throw invalidPipeArgumentError(PercentPipe, error.message); - } - } - } - PercentPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PercentPipe, - deps: [{ token: i0.LOCALE_ID }], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - PercentPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PercentPipe, - isStandalone: true, - name: 'percent', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PercentPipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'percent', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [i0.LOCALE_ID], - }, - ], - }, - ]; - }, - }); - /** - * @ngModule CommonModule - * @description - * - * Transforms a number to a currency string, formatted according to locale rules - * that determine group sizing and separator, decimal-point character, - * and other locale-specific configurations. - * - * {@a currency-code-deprecation} - *
    - * - * **Deprecation notice:** - * - * The default currency code is currently always `USD` but this is deprecated from v9. - * - * **In v11 the default currency code will be taken from the current locale identified by - * the `LOCALE_ID` token. See the [i18n guide](guide/i18n-common-locale-id) for - * more information.** - * - * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in - * your application `NgModule`: - * - * ```ts - * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'} - * ``` - * - *
    - * - * @see `getCurrencySymbol()` - * @see `formatCurrency()` - * - * @usageNotes - * The following code shows how the pipe transforms numbers - * into text strings, according to various format specifications, - * where the caller's default locale is `en-US`. - * - * - * - * @publicApi - */ - class CurrencyPipe { - constructor(_locale, _defaultCurrencyCode = 'USD') { - this._locale = _locale; - this._defaultCurrencyCode = _defaultCurrencyCode; - } - /** - * - * @param value The number to be formatted as currency. - * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, - * such as `USD` for the US dollar and `EUR` for the euro. The default currency code can be - * configured using the `DEFAULT_CURRENCY_CODE` injection token. - * @param display The format for the currency indicator. One of the following: - * - `code`: Show the code (such as `USD`). - * - `symbol`(default): Show the symbol (such as `$`). - * - `symbol-narrow`: Use the narrow symbol for locales that have two symbols for their - * currency. - * For example, the Canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`. If the - * locale has no narrow symbol, uses the standard symbol for the locale. - * - String: Use the given string value instead of a code or a symbol. - * For example, an empty string will suppress the currency & symbol. - * - Boolean (marked deprecated in v5): `true` for symbol and false for `code`. - * - * @param digitsInfo Decimal representation options, specified by a string - * in the following format:
    - * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. - * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. - * Default is `1`. - * - `minFractionDigits`: The minimum number of digits after the decimal point. - * Default is `2`. - * - `maxFractionDigits`: The maximum number of digits after the decimal point. - * Default is `2`. - * If not provided, the number will be formatted with the proper amount of digits, - * depending on what the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) specifies. - * For example, the Canadian dollar has 2 digits, whereas the Chilean peso has none. - * @param locale A locale code for the locale format rules to use. - * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. - * See [Setting your app locale](guide/i18n-common-locale-id). + function assertGreaterThanZero(dir, inputValue, inputName) { + const validNumber = typeof inputValue === 'number' && inputValue > 0; + const validString = typeof inputValue === 'string' && /^\d+$/.test(inputValue.trim()) && parseInt(inputValue) > 0; + if (!validNumber && !validString) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` has an invalid value ` + + `(\`${inputValue}\`). To fix this, provide \`${inputName}\` ` + + `as a number greater than 0.`); + } + } + /** + * Verifies that the rendered image is not visually distorted. Effectively this is checking: + * - Whether the "width" and "height" attributes reflect the actual dimensions of the image. + * - Whether image styling is "correct" (see below for a longer explanation). */ - transform( - value, - currencyCode = this._defaultCurrencyCode, - display = 'symbol', - digitsInfo, - locale - ) { - if (!isValue(value)) return null; - locale = locale || this._locale; - if (typeof display === 'boolean') { - if ( - (typeof ngDevMode === 'undefined' || ngDevMode) && - console && - console.warn - ) { - console.warn( - `Warning: the currency pipe has been changed in Angular v5. The symbolDisplay option (third parameter) is now a string instead of a boolean. The accepted values are "code", "symbol" or "symbol-narrow".` - ); - } - display = display ? 'symbol' : 'code'; - } - let currency = currencyCode || this._defaultCurrencyCode; - if (display !== 'code') { - if (display === 'symbol' || display === 'symbol-narrow') { - currency = getCurrencySymbol( - currency, - display === 'symbol' ? 'wide' : 'narrow', - locale - ); - } else { - currency = display; - } - } - try { - const num = strToNumber(value); - return formatCurrency(num, locale, currency, currencyCode, digitsInfo); - } catch (error) { - throw invalidPipeArgumentError(CurrencyPipe, error.message); - } - } - } - CurrencyPipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: CurrencyPipe, - deps: [{ token: i0.LOCALE_ID }, { token: i0.DEFAULT_CURRENCY_CODE }], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - CurrencyPipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: CurrencyPipe, - isStandalone: true, - name: 'currency', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: CurrencyPipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'currency', - standalone: true, - }, - ], - }, - ], - ctorParameters: function () { - return [ - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [i0.LOCALE_ID], - }, - ], - }, - { - type: undefined, - decorators: [ - { - type: i0.Inject, - args: [i0.DEFAULT_CURRENCY_CODE], - }, - ], - }, - ]; - }, - }); - function isValue(value) { - return !(value == null || value === '' || value !== value); - } - /** - * Transforms a string into a number (if needed). - */ - function strToNumber(value) { - // Convert strings to numbers - if ( - typeof value === 'string' && - !isNaN(Number(value) - parseFloat(value)) - ) { - return Number(value); - } - if (typeof value !== 'number') { - throw new Error(`${value} is not a number`); - } - return value; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @ngModule CommonModule - * @description - * - * Creates a new `Array` or `String` containing a subset (slice) of the elements. - * - * @usageNotes - * - * All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()` - * and `String.prototype.slice()`. - * - * When operating on an `Array`, the returned `Array` is always a copy even when all - * the elements are being returned. - * - * When operating on a blank value, the pipe returns the blank value. - * - * ### List Example - * - * This `ngFor` example: - * - * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'} - * - * produces the following: - * - * ```html - *
  • b
  • - *
  • c
  • - * ``` - * - * ### String Examples - * - * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'} - * - * @publicApi - */ - class SlicePipe { - transform(value, start, end) { - if (value == null) return null; - if (!this.supports(value)) { - throw invalidPipeArgumentError(SlicePipe, value); - } - return value.slice(start, end); - } - supports(obj) { - return typeof obj === 'string' || Array.isArray(obj); - } - } - SlicePipe.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: SlicePipe, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Pipe, - }); - SlicePipe.ɵpipe = i0__namespace.ɵɵngDeclarePipe({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: SlicePipe, - isStandalone: true, - name: 'slice', - pure: false, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: SlicePipe, - decorators: [ - { - type: i0.Pipe, - args: [ - { - name: 'slice', - pure: false, - standalone: true, - }, - ], - }, - ], - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * A collection of Angular pipes that are likely to be used in each and every application. - */ - const COMMON_PIPES = [ - AsyncPipe, - UpperCasePipe, - LowerCasePipe, - JsonPipe, - SlicePipe, - DecimalPipe, - PercentPipe, - TitleCasePipe, - CurrencyPipe, - DatePipe, - I18nPluralPipe, - I18nSelectPipe, - KeyValuePipe, - ]; - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - // Note: This does not contain the location providers, - // as they need some platform specific implementations to work. - /** - * Exports all the basic Angular directives and pipes, - * such as `NgIf`, `NgForOf`, `DecimalPipe`, and so on. - * Re-exported by `BrowserModule`, which is included automatically in the root - * `AppModule` when you create a new app with the CLI `new` command. - * - * @publicApi - */ - class CommonModule {} - CommonModule.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: CommonModule, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.NgModule, - }); - CommonModule.ɵmod = i0__namespace.ɵɵngDeclareNgModule({ - minVersion: '14.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: CommonModule, - imports: [ - NgClass, - NgComponentOutlet, - NgForOf, - NgIf, - NgTemplateOutlet, - NgStyle, - NgSwitch, - NgSwitchCase, - NgSwitchDefault, - NgPlural, - NgPluralCase, - AsyncPipe, - UpperCasePipe, - LowerCasePipe, - JsonPipe, - SlicePipe, - DecimalPipe, - PercentPipe, - TitleCasePipe, - CurrencyPipe, - DatePipe, - I18nPluralPipe, - I18nSelectPipe, - KeyValuePipe, - ], - exports: [ - NgClass, - NgComponentOutlet, - NgForOf, - NgIf, - NgTemplateOutlet, - NgStyle, - NgSwitch, - NgSwitchCase, - NgSwitchDefault, - NgPlural, - NgPluralCase, - AsyncPipe, - UpperCasePipe, - LowerCasePipe, - JsonPipe, - SlicePipe, - DecimalPipe, - PercentPipe, - TitleCasePipe, - CurrencyPipe, - DatePipe, - I18nPluralPipe, - I18nSelectPipe, - KeyValuePipe, - ], - }); - CommonModule.ɵinj = i0__namespace.ɵɵngDeclareInjector({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: CommonModule, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: CommonModule, - decorators: [ - { - type: i0.NgModule, - args: [ - { - imports: [COMMON_DIRECTIVES, COMMON_PIPES], - exports: [COMMON_DIRECTIVES, COMMON_PIPES], - }, - ], - }, - ], - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - const PLATFORM_BROWSER_ID = 'browser'; - const PLATFORM_SERVER_ID = 'server'; - const PLATFORM_WORKER_APP_ID = 'browserWorkerApp'; - const PLATFORM_WORKER_UI_ID = 'browserWorkerUi'; - /** - * Returns whether a platform id represents a browser platform. - * @publicApi - */ - function isPlatformBrowser(platformId) { - return platformId === PLATFORM_BROWSER_ID; - } - /** - * Returns whether a platform id represents a server platform. - * @publicApi - */ - function isPlatformServer(platformId) { - return platformId === PLATFORM_SERVER_ID; - } - /** - * Returns whether a platform id represents a web worker app platform. - * @publicApi - */ - function isPlatformWorkerApp(platformId) { - return platformId === PLATFORM_WORKER_APP_ID; - } - /** - * Returns whether a platform id represents a web worker UI platform. - * @publicApi - */ - function isPlatformWorkerUi(platformId) { - return platformId === PLATFORM_WORKER_UI_ID; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @publicApi - */ - const VERSION = new i0.Version('15.0.1'); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Defines a scroll position manager. Implemented by `BrowserViewportScroller`. - * - * @publicApi - */ - class ViewportScroller {} - // De-sugared tree-shakable injection - // See #23917 - /** @nocollapse */ - ViewportScroller.ɵprov = i0['ɵɵdefineInjectable']({ - token: ViewportScroller, - providedIn: 'root', - factory: () => - new BrowserViewportScroller(i0['ɵɵinject'](DOCUMENT), window), - }); - /** - * Manages the scroll position for a browser window. - */ - class BrowserViewportScroller { - constructor(document, window) { - this.document = document; - this.window = window; - this.offset = () => [0, 0]; - } - /** - * Configures the top offset used when scrolling to an anchor. - * @param offset A position in screen coordinates (a tuple with x and y values) - * or a function that returns the top offset position. - * - */ - setOffset(offset) { - if (Array.isArray(offset)) { - this.offset = () => offset; - } else { - this.offset = offset; - } - } - /** - * Retrieves the current scroll position. - * @returns The position in screen coordinates. - */ - getScrollPosition() { - if (this.supportsScrolling()) { - return [this.window.pageXOffset, this.window.pageYOffset]; - } else { - return [0, 0]; - } - } - /** - * Sets the scroll position. - * @param position The new position in screen coordinates. - */ - scrollToPosition(position) { - if (this.supportsScrolling()) { - this.window.scrollTo(position[0], position[1]); - } - } - /** - * Scrolls to an element and attempts to focus the element. - * - * Note that the function name here is misleading in that the target string may be an ID for a - * non-anchor element. - * - * @param target The ID of an element or name of the anchor. - * - * @see https://html.spec.whatwg.org/#the-indicated-part-of-the-document - * @see https://html.spec.whatwg.org/#scroll-to-fragid - */ - scrollToAnchor(target) { - if (!this.supportsScrolling()) { - return; - } - const elSelected = findAnchorFromDocument(this.document, target); - if (elSelected) { - this.scrollToElement(elSelected); - // After scrolling to the element, the spec dictates that we follow the focus steps for the - // target. Rather than following the robust steps, simply attempt focus. - // - // @see https://html.spec.whatwg.org/#get-the-focusable-area - // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus - // @see https://html.spec.whatwg.org/#focusable-area - elSelected.focus(); - } - } - /** - * Disables automatic scroll restoration provided by the browser. - */ - setHistoryScrollRestoration(scrollRestoration) { - if (this.supportScrollRestoration()) { - const history = this.window.history; - if (history && history.scrollRestoration) { - history.scrollRestoration = scrollRestoration; - } - } - } - /** - * Scrolls to an element using the native offset and the specified offset set on this scroller. - * - * The offset can be used when we know that there is a floating header and scrolling naively to an - * element (ex: `scrollIntoView`) leaves the element hidden behind the floating header. - */ - scrollToElement(el) { - const rect = el.getBoundingClientRect(); - const left = rect.left + this.window.pageXOffset; - const top = rect.top + this.window.pageYOffset; - const offset = this.offset(); - this.window.scrollTo(left - offset[0], top - offset[1]); - } - /** - * We only support scroll restoration when we can get a hold of window. - * This means that we do not support this behavior when running in a web worker. - * - * Lifting this restriction right now would require more changes in the dom adapter. - * Since webworkers aren't widely used, we will lift it once RouterScroller is - * battle-tested. - */ - supportScrollRestoration() { - try { - if (!this.supportsScrolling()) { - return false; - } - // The `scrollRestoration` property could be on the `history` instance or its prototype. - const scrollRestorationDescriptor = - getScrollRestorationProperty(this.window.history) || - getScrollRestorationProperty( - Object.getPrototypeOf(this.window.history) - ); - // We can write to the `scrollRestoration` property if it is a writable data field or it has a - // setter function. - return ( - !!scrollRestorationDescriptor && - !!( - scrollRestorationDescriptor.writable || - scrollRestorationDescriptor.set - ) - ); - } catch (_a) { - return false; - } - } - supportsScrolling() { - try { - return ( - !!this.window && - !!this.window.scrollTo && - 'pageXOffset' in this.window - ); - } catch (_a) { - return false; - } - } - } - function getScrollRestorationProperty(obj) { - return Object.getOwnPropertyDescriptor(obj, 'scrollRestoration'); - } - function findAnchorFromDocument(document, target) { - const documentResult = - document.getElementById(target) || document.getElementsByName(target)[0]; - if (documentResult) { - return documentResult; - } - // `getElementById` and `getElementsByName` won't pierce through the shadow DOM so we - // have to traverse the DOM manually and do the lookup through the shadow roots. - if ( - typeof document.createTreeWalker === 'function' && - document.body && - (document.body.createShadowRoot || document.body.attachShadow) - ) { - const treeWalker = document.createTreeWalker( - document.body, - NodeFilter.SHOW_ELEMENT - ); - let currentNode = treeWalker.currentNode; - while (currentNode) { - const shadowRoot = currentNode.shadowRoot; - if (shadowRoot) { - // Note that `ShadowRoot` doesn't support `getElementsByName` - // so we have to fall back to `querySelector`. - const result = - shadowRoot.getElementById(target) || - shadowRoot.querySelector(`[name="${target}"]`); - if (result) { - return result; - } - } - currentNode = treeWalker.nextNode(); - } - } - return null; - } - /** - * Provides an empty implementation of the viewport scroller. - */ - class NullViewportScroller { - /** - * Empty implementation - */ - setOffset(offset) {} - /** - * Empty implementation - */ - getScrollPosition() { - return [0, 0]; - } - /** - * Empty implementation - */ - scrollToPosition(position) {} - /** - * Empty implementation - */ - scrollToAnchor(anchor) {} - /** - * Empty implementation - */ - setHistoryScrollRestoration(scrollRestoration) {} - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * A wrapper around the `XMLHttpRequest` constructor. - * - * @publicApi - */ - class XhrFactory {} - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - // Converts a string that represents a URL into a URL class instance. - function getUrl(src, win) { - // Don't use a base URL is the URL is absolute. - return isAbsoluteUrl(src) ? new URL(src) : new URL(src, win.location.href); - } - // Checks whether a URL is absolute (i.e. starts with `http://` or `https://`). - function isAbsoluteUrl(src) { - return /^https?:\/\//.test(src); - } - // Given a URL, extract the hostname part. - // If a URL is a relative one - the URL is returned as is. - function extractHostname(url) { - return isAbsoluteUrl(url) ? new URL(url).hostname : url; - } - function isValidPath(path) { - const isString = typeof path === 'string'; - if (!isString || path.trim() === '') { - return false; - } - // Calling new URL() will throw if the path string is malformed - try { - const url = new URL(path); - return true; - } catch (_a) { - return false; - } - } - function normalizePath(path) { - return path.endsWith('/') ? path.slice(0, -1) : path; - } - function normalizeSrc(src) { - return src.startsWith('/') ? src.slice(1) : src; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Noop image loader that does no transformation to the original src and just returns it as is. - * This loader is used as a default one if more specific logic is not provided in an app config. - * - * @see `ImageLoader` - * @see `NgOptimizedImage` - */ - const noopImageLoader = (config) => config.src; - /** - * Injection token that configures the image loader function. - * - * @see `ImageLoader` - * @see `NgOptimizedImage` - * @publicApi - */ - const IMAGE_LOADER = new i0.InjectionToken('ImageLoader', { - providedIn: 'root', - factory: () => noopImageLoader, - }); - /** - * Internal helper function that makes it easier to introduce custom image loaders for the - * `NgOptimizedImage` directive. It is enough to specify a URL builder function to obtain full DI - * configuration for a given loader: a DI token corresponding to the actual loader function, plus DI - * tokens managing preconnect check functionality. - * @param buildUrlFn a function returning a full URL based on loader's configuration - * @param exampleUrls example of full URLs for a given loader (used in error messages) - * @returns a set of DI providers corresponding to the configured image loader - */ - function createImageLoader(buildUrlFn, exampleUrls) { - return function provideImageLoader(path) { - if (!isValidPath(path)) { - throwInvalidPathError(path, exampleUrls || []); - } - // The trailing / is stripped (if provided) to make URL construction (concatenation) easier in - // the individual loader functions. - path = normalizePath(path); - const loaderFn = (config) => { - if (isAbsoluteUrl(config.src)) { - // Image loader functions expect an image file name (e.g. `my-image.png`) - // or a relative path + a file name (e.g. `/a/b/c/my-image.png`) as an input, - // so the final absolute URL can be constructed. - // When an absolute URL is provided instead - the loader can not - // build a final URL, thus the error is thrown to indicate that. - throwUnexpectedAbsoluteUrlError(path, config.src); - } - return buildUrlFn( - path, - Object.assign(Object.assign({}, config), { - src: normalizeSrc(config.src), - }) - ); - }; - const providers = [{ provide: IMAGE_LOADER, useValue: loaderFn }]; - return providers; - }; - } - function throwInvalidPathError(path, exampleUrls) { - throw new i0[ - 'ɵRuntimeError' - ](2959 /* RuntimeErrorCode.INVALID_LOADER_ARGUMENTS */, ngDevMode && `Image loader has detected an invalid path (\`${path}\`). ` + `To fix this, supply a path using one of the following formats: ${exampleUrls.join(' or ')}`); - } - function throwUnexpectedAbsoluteUrlError(path, url) { - throw new i0[ - 'ɵRuntimeError' - ](2959 /* RuntimeErrorCode.INVALID_LOADER_ARGUMENTS */, ngDevMode && `Image loader has detected a \`\` tag with an invalid \`ngSrc\` attribute: ${url}. ` + `This image loader expects \`ngSrc\` to be a relative URL - ` + `however the provided value is an absolute URL. ` + `To fix this, provide \`ngSrc\` as a path relative to the base URL ` + `configured for this loader (\`${path}\`).`); - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Function that generates an ImageLoader for [Cloudflare Image - * Resizing](https://developers.cloudflare.com/images/image-resizing/) and turns it into an Angular - * provider. Note: Cloudflare has multiple image products - this provider is specifically for - * Cloudflare Image Resizing; it will not work with Cloudflare Images or Cloudflare Polish. - * - * @param path Your domain name, e.g. https://mysite.com - * @returns Provider that provides an ImageLoader function - * - * @publicApi - */ - const provideCloudflareLoader = createImageLoader( - createCloudflareUrl, - ngDevMode - ? ['https:///cdn-cgi/image//'] - : undefined - ); - function createCloudflareUrl(path, config) { - let params = `format=auto`; - if (config.width) { - params += `,width=${config.width}`; - } - // Cloudflare image URLs format: - // https://developers.cloudflare.com/images/image-resizing/url-format/ - return `${path}/cdn-cgi/image/${params}/${config.src}`; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Name and URL tester for Cloudinary. - */ - const cloudinaryLoaderInfo = { - name: 'Cloudinary', - testUrl: isCloudinaryUrl, - }; - const CLOUDINARY_LOADER_REGEX = /https?\:\/\/[^\/]+\.cloudinary\.com\/.+/; - /** - * Tests whether a URL is from Cloudinary CDN. - */ - function isCloudinaryUrl(url) { - return CLOUDINARY_LOADER_REGEX.test(url); - } - /** - * Function that generates an ImageLoader for Cloudinary and turns it into an Angular provider. - * - * @param path Base URL of your Cloudinary images - * This URL should match one of the following formats: - * https://res.cloudinary.com/mysite - * https://mysite.cloudinary.com - * https://subdomain.mysite.com - * @returns Set of providers to configure the Cloudinary loader. - * - * @publicApi - */ - const provideCloudinaryLoader = createImageLoader( - createCloudinaryUrl, - ngDevMode - ? [ - 'https://res.cloudinary.com/mysite', - 'https://mysite.cloudinary.com', - 'https://subdomain.mysite.com', - ] - : undefined - ); - function createCloudinaryUrl(path, config) { - // Cloudinary image URLformat: - // https://cloudinary.com/documentation/image_transformations#transformation_url_structure - // Example of a Cloudinary image URL: - // https://res.cloudinary.com/mysite/image/upload/c_scale,f_auto,q_auto,w_600/marketing/tile-topics-m.png - let params = `f_auto,q_auto`; // sets image format and quality to "auto" - if (config.width) { - params += `,w_${config.width}`; - } - return `${path}/image/upload/${params}/${config.src}`; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Name and URL tester for ImageKit. - */ - const imageKitLoaderInfo = { - name: 'ImageKit', - testUrl: isImageKitUrl, - }; - const IMAGE_KIT_LOADER_REGEX = /https?\:\/\/[^\/]+\.imagekit\.io\/.+/; - /** - * Tests whether a URL is from ImageKit CDN. - */ - function isImageKitUrl(url) { - return IMAGE_KIT_LOADER_REGEX.test(url); - } - /** - * Function that generates an ImageLoader for ImageKit and turns it into an Angular provider. - * - * @param path Base URL of your ImageKit images - * This URL should match one of the following formats: - * https://ik.imagekit.io/myaccount - * https://subdomain.mysite.com - * @returns Set of providers to configure the ImageKit loader. - * - * @publicApi - */ - const provideImageKitLoader = createImageLoader( - createImagekitUrl, - ngDevMode - ? ['https://ik.imagekit.io/mysite', 'https://subdomain.mysite.com'] - : undefined - ); - function createImagekitUrl(path, config) { - // Example of an ImageKit image URL: - // https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg - let params = `tr:q-auto`; // applies the "auto quality" transformation - if (config.width) { - params += `,w-${config.width}`; - } - return `${path}/${params}/${config.src}`; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Name and URL tester for Imgix. - */ - const imgixLoaderInfo = { - name: 'Imgix', - testUrl: isImgixUrl, - }; - const IMGIX_LOADER_REGEX = /https?\:\/\/[^\/]+\.imgix\.net\/.+/; - /** - * Tests whether a URL is from Imgix CDN. - */ - function isImgixUrl(url) { - return IMGIX_LOADER_REGEX.test(url); - } - /** - * Function that generates an ImageLoader for Imgix and turns it into an Angular provider. - * - * @param path path to the desired Imgix origin, - * e.g. https://somepath.imgix.net or https://images.mysite.com - * @returns Set of providers to configure the Imgix loader. - * - * @publicApi - */ - const provideImgixLoader = createImageLoader( - createImgixUrl, - ngDevMode ? ['https://somepath.imgix.net/'] : undefined - ); - function createImgixUrl(path, config) { - const url = new URL(`${path}/${config.src}`); - // This setting ensures the smallest allowable format is set. - url.searchParams.set('auto', 'format'); - if (config.width) { - url.searchParams.set('w', config.width.toString()); - } - return url.href; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - // Assembles directive details string, useful for error messages. - function imgDirectiveDetails(ngSrc, includeNgSrc = true) { - const ngSrcInfo = includeNgSrc - ? `(activated on an element with the \`ngSrc="${ngSrc}"\`) ` - : ''; - return `The NgOptimizedImage directive ${ngSrcInfo}has detected that`; - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Asserts that the application is in development mode. Throws an error if the application is in - * production mode. This assert can be used to make sure that there is no dev-mode code invoked in - * the prod mode accidentally. - */ - function assertDevMode(checkName) { - if (!ngDevMode) { - throw new i0['ɵRuntimeError']( - 2958 /* RuntimeErrorCode.UNEXPECTED_DEV_MODE_CHECK_IN_PROD_MODE */, - `Unexpected invocation of the ${checkName} in the prod mode. ` + - `Please make sure that the prod mode is enabled for production builds.` - ); - } - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * Observer that detects whether an image with `NgOptimizedImage` - * is treated as a Largest Contentful Paint (LCP) element. If so, - * asserts that the image has the `priority` attribute. - * - * Note: this is a dev-mode only class and it does not appear in prod bundles, - * thus there is no `ngDevMode` use in the code. - * - * Based on https://web.dev/lcp/#measure-lcp-in-javascript. - */ - class LCPImageObserver { - constructor() { - // Map of full image URLs -> original `ngSrc` values. - this.images = new Map(); - // Keep track of images for which `console.warn` was produced. - this.alreadyWarned = new Set(); - this.window = null; - this.observer = null; - assertDevMode('LCP checker'); - const win = i0.inject(DOCUMENT).defaultView; - if ( - typeof win !== 'undefined' && - typeof PerformanceObserver !== 'undefined' - ) { - this.window = win; - this.observer = this.initPerformanceObserver(); - } - } - /** - * Inits PerformanceObserver and subscribes to LCP events. - * Based on https://web.dev/lcp/#measure-lcp-in-javascript - */ - initPerformanceObserver() { - const observer = new PerformanceObserver((entryList) => { - var _a, _b; - const entries = entryList.getEntries(); - if (entries.length === 0) return; - // We use the latest entry produced by the `PerformanceObserver` as the best - // signal on which element is actually an LCP one. As an example, the first image to load on - // a page, by virtue of being the only thing on the page so far, is often a LCP candidate - // and gets reported by PerformanceObserver, but isn't necessarily the LCP element. - const lcpElement = entries[entries.length - 1]; - // Cast to `any` due to missing `element` on the `LargestContentfulPaint` type of entry. - // See https://developer.mozilla.org/en-US/docs/Web/API/LargestContentfulPaint - const imgSrc = - (_b = - (_a = lcpElement.element) === null || _a === void 0 - ? void 0 - : _a.src) !== null && _b !== void 0 - ? _b - : ''; - // Exclude `data:` and `blob:` URLs, since they are not supported by the directive. - if (imgSrc.startsWith('data:') || imgSrc.startsWith('blob:')) return; - const imgNgSrc = this.images.get(imgSrc); - if (imgNgSrc && !this.alreadyWarned.has(imgSrc)) { - this.alreadyWarned.add(imgSrc); - logMissingPriorityWarning(imgSrc); - } - }); - observer.observe({ type: 'largest-contentful-paint', buffered: true }); - return observer; - } - registerImage(rewrittenSrc, originalNgSrc) { - if (!this.observer) return; - this.images.set(getUrl(rewrittenSrc, this.window).href, originalNgSrc); - } - unregisterImage(rewrittenSrc) { - if (!this.observer) return; - this.images.delete(getUrl(rewrittenSrc, this.window).href); - } - ngOnDestroy() { - if (!this.observer) return; - this.observer.disconnect(); - this.images.clear(); - this.alreadyWarned.clear(); - } - } - LCPImageObserver.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LCPImageObserver, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - LCPImageObserver.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LCPImageObserver, - providedIn: 'root', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: LCPImageObserver, - decorators: [ - { - type: i0.Injectable, - args: [{ providedIn: 'root' }], - }, - ], - ctorParameters: function () { - return []; - }, - }); - function logMissingPriorityWarning(ngSrc) { - const directiveDetails = imgDirectiveDetails(ngSrc); - console.warn( - i0['ɵformatRuntimeError']( - 2955 /* RuntimeErrorCode.LCP_IMG_MISSING_PRIORITY */, - `${directiveDetails} this image is the Largest Contentful Paint (LCP) ` + - `element but was not marked "priority". This image should be marked ` + - `"priority" in order to prioritize its loading. ` + - `To fix this, add the "priority" attribute.` - ) - ); - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - // Set of origins that are always excluded from the preconnect checks. - const INTERNAL_PRECONNECT_CHECK_BLOCKLIST = new Set([ - 'localhost', - '127.0.0.1', - '0.0.0.0', - ]); - /** - * Injection token to configure which origins should be excluded - * from the preconnect checks. It can either be a single string or an array of strings - * to represent a group of origins, for example: - * - * ```typescript - * {provide: PRECONNECT_CHECK_BLOCKLIST, useValue: 'https://your-domain.com'} - * ``` - * - * or: - * - * ```typescript - * {provide: PRECONNECT_CHECK_BLOCKLIST, - * useValue: ['https://your-domain-1.com', 'https://your-domain-2.com']} - * ``` - * - * @publicApi - */ - const PRECONNECT_CHECK_BLOCKLIST = new i0.InjectionToken( - 'PRECONNECT_CHECK_BLOCKLIST' - ); - /** - * Contains the logic to detect whether an image, marked with the "priority" attribute - * has a corresponding `` tag in the `document.head`. - * - * Note: this is a dev-mode only class, which should not appear in prod bundles, - * thus there is no `ngDevMode` use in the code. - */ - class PreconnectLinkChecker { - constructor() { - this.document = i0.inject(DOCUMENT); - /** - * Set of tags found on this page. - * The `null` value indicates that there was no DOM query operation performed. - */ - this.preconnectLinks = null; - /* - * Keep track of all already seen origin URLs to avoid repeating the same check. - */ - this.alreadySeen = new Set(); - this.window = null; - this.blocklist = new Set(INTERNAL_PRECONNECT_CHECK_BLOCKLIST); - assertDevMode('preconnect link checker'); - const win = this.document.defaultView; - if (typeof win !== 'undefined') { - this.window = win; - } - const blocklist = i0.inject(PRECONNECT_CHECK_BLOCKLIST, { - optional: true, - }); - if (blocklist) { - this.populateBlocklist(blocklist); - } - } - populateBlocklist(origins) { - if (Array.isArray(origins)) { - deepForEach(origins, (origin) => { - this.blocklist.add(extractHostname(origin)); + function assertNoImageDistortion(dir, img, renderer) { + const removeListenerFn = renderer.listen(img, 'load', () => { + removeListenerFn(); + // TODO: `clientWidth`, `clientHeight`, `naturalWidth` and `naturalHeight` + // are typed as number, but we run `parseFloat` (which accepts strings only). + // Verify whether `parseFloat` is needed in the cases below. + const renderedWidth = parseFloat(img.clientWidth); + const renderedHeight = parseFloat(img.clientHeight); + const renderedAspectRatio = renderedWidth / renderedHeight; + const nonZeroRenderedDimensions = renderedWidth !== 0 && renderedHeight !== 0; + const intrinsicWidth = parseFloat(img.naturalWidth); + const intrinsicHeight = parseFloat(img.naturalHeight); + const intrinsicAspectRatio = intrinsicWidth / intrinsicHeight; + const suppliedWidth = dir.width; + const suppliedHeight = dir.height; + const suppliedAspectRatio = suppliedWidth / suppliedHeight; + // Tolerance is used to account for the impact of subpixel rendering. + // Due to subpixel rendering, the rendered, intrinsic, and supplied + // aspect ratios of a correctly configured image may not exactly match. + // For example, a `width=4030 height=3020` image might have a rendered + // size of "1062w, 796.48h". (An aspect ratio of 1.334... vs. 1.333...) + const inaccurateDimensions = Math.abs(suppliedAspectRatio - intrinsicAspectRatio) > ASPECT_RATIO_TOLERANCE; + const stylingDistortion = nonZeroRenderedDimensions && + Math.abs(intrinsicAspectRatio - renderedAspectRatio) > ASPECT_RATIO_TOLERANCE; + if (inaccurateDimensions) { + console.warn(i0["ɵformatRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the aspect ratio of the image does not match ` + + `the aspect ratio indicated by the width and height attributes. ` + + `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` + + `(aspect-ratio: ${intrinsicAspectRatio}). \nSupplied width and height attributes: ` + + `${suppliedWidth}w x ${suppliedHeight}h (aspect-ratio: ${suppliedAspectRatio}). ` + + `\nTo fix this, update the width and height attributes.`)); + } + else if (stylingDistortion) { + console.warn(i0["ɵformatRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the aspect ratio of the rendered image ` + + `does not match the image's intrinsic aspect ratio. ` + + `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` + + `(aspect-ratio: ${intrinsicAspectRatio}). \nRendered image size: ` + + `${renderedWidth}w x ${renderedHeight}h (aspect-ratio: ` + + `${renderedAspectRatio}). \nThis issue can occur if "width" and "height" ` + + `attributes are added to an image without updating the corresponding ` + + `image styling. To fix this, adjust image styling. In most cases, ` + + `adding "height: auto" or "width: auto" to the image styling will fix ` + + `this issue.`)); + } + else if (!dir.ngSrcset && nonZeroRenderedDimensions) { + // If `ngSrcset` hasn't been set, sanity check the intrinsic size. + const recommendedWidth = RECOMMENDED_SRCSET_DENSITY_CAP * renderedWidth; + const recommendedHeight = RECOMMENDED_SRCSET_DENSITY_CAP * renderedHeight; + const oversizedWidth = (intrinsicWidth - recommendedWidth) >= OVERSIZED_IMAGE_TOLERANCE; + const oversizedHeight = (intrinsicHeight - recommendedHeight) >= OVERSIZED_IMAGE_TOLERANCE; + if (oversizedWidth || oversizedHeight) { + console.warn(i0["ɵformatRuntimeError"](2960 /* RuntimeErrorCode.OVERSIZED_IMAGE */, `${imgDirectiveDetails(dir.ngSrc)} the intrinsic image is significantly ` + + `larger than necessary. ` + + `\nRendered image size: ${renderedWidth}w x ${renderedHeight}h. ` + + `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h. ` + + `\nRecommended intrinsic image size: ${recommendedWidth}w x ${recommendedHeight}h. ` + + `\nNote: Recommended intrinsic image size is calculated assuming a maximum DPR of ` + + `${RECOMMENDED_SRCSET_DENSITY_CAP}. To improve loading time, resize the image ` + + `or consider using the "ngSrcset" and "sizes" attributes.`)); + } + } }); - } else { - this.blocklist.add(extractHostname(origins)); - } - } - /** - * Checks that a preconnect resource hint exists in the head fo rthe - * given src. - * - * @param rewrittenSrc src formatted with loader - * @param originalNgSrc ngSrc value - */ - assertPreconnect(rewrittenSrc, originalNgSrc) { - if (!this.window) return; - const imgUrl = getUrl(rewrittenSrc, this.window); - if ( - this.blocklist.has(imgUrl.hostname) || - this.alreadySeen.has(imgUrl.origin) - ) - return; - // Register this origin as seen, so we don't check it again later. - this.alreadySeen.add(imgUrl.origin); - if (!this.preconnectLinks) { - // Note: we query for preconnect links only *once* and cache the results - // for the entire lifespan of an application, since it's unlikely that the - // list would change frequently. This allows to make sure there are no - // performance implications of making extra DOM lookups for each image. - this.preconnectLinks = this.queryPreconnectLinks(); - } - if (!this.preconnectLinks.has(imgUrl.origin)) { - console.warn( - i0['ɵformatRuntimeError']( - 2956 /* RuntimeErrorCode.PRIORITY_IMG_MISSING_PRECONNECT_TAG */, - `${imgDirectiveDetails( - originalNgSrc - )} there is no preconnect tag present for this ` + - `image. Preconnecting to the origin(s) that serve priority images ensures that these ` + - `images are delivered as soon as possible. To fix this, please add the following ` + - `element into the of the document:\n` + - ` ` - ) - ); - } - } - queryPreconnectLinks() { - const preconnectUrls = new Set(); - const selector = 'link[rel=preconnect]'; - const links = Array.from(this.document.querySelectorAll(selector)); - for (let link of links) { - const url = getUrl(link.href, this.window); - preconnectUrls.add(url.origin); - } - return preconnectUrls; - } - ngOnDestroy() { - var _a; - (_a = this.preconnectLinks) === null || _a === void 0 - ? void 0 - : _a.clear(); - this.alreadySeen.clear(); - } - } - PreconnectLinkChecker.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PreconnectLinkChecker, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - PreconnectLinkChecker.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PreconnectLinkChecker, - providedIn: 'root', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PreconnectLinkChecker, - decorators: [ - { - type: i0.Injectable, - args: [{ providedIn: 'root' }], - }, - ], - ctorParameters: function () { - return []; - }, - }); - /** - * Invokes a callback for each element in the array. Also invokes a callback - * recursively for each nested array. - */ - function deepForEach(input, fn) { - for (let value of input) { - Array.isArray(value) ? deepForEach(value, fn) : fn(value); - } - } - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * In SSR scenarios, a preload `` element is generated for priority images. - * Having a large number of preload tags may negatively affect the performance, - * so we warn developers (by throwing an error) if the number of preloaded images - * is above a certain threshold. This const specifies this threshold. - */ - const DEFAULT_PRELOADED_IMAGES_LIMIT = 5; - /** - * Helps to keep track of priority images that already have a corresponding - * preload tag (to avoid generating multiple preload tags with the same URL). - * - * This Set tracks the original src passed into the `ngSrc` input not the src after it has been - * run through the specified `IMAGE_LOADER`. - */ - const PRELOADED_IMAGES = new i0.InjectionToken( - 'NG_OPTIMIZED_PRELOADED_IMAGES', - { providedIn: 'root', factory: () => new Set() } - ); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * @description Contains the logic needed to track and add preload link tags to the `` tag. It - * will also track what images have already had preload link tags added so as to not duplicate link - * tags. - * - * In dev mode this service will validate that the number of preloaded images does not exceed the - * configured default preloaded images limit: {@link DEFAULT_PRELOADED_IMAGES_LIMIT}. - */ - class PreloadLinkCreator { - constructor() { - this.preloadedImages = i0.inject(PRELOADED_IMAGES); - this.document = i0.inject(DOCUMENT); - } - /** - * @description Add a preload `` to the `` of the `index.html` that is served from the - * server while using Angular Universal and SSR to kick off image loads for high priority images. - * - * The `sizes` (passed in from the user) and `srcset` (parsed and formatted from `ngSrcset`) - * properties used to set the corresponding attributes, `imagesizes` and `imagesrcset` - * respectively, on the preload `` tag so that the correctly sized image is preloaded from - * the CDN. - * - * {@link https://web.dev/preload-responsive-images/#imagesrcset-and-imagesizes} - * - * @param renderer The `Renderer2` passed in from the directive - * @param src The original src of the image that is set on the `ngSrc` input. - * @param srcset The parsed and formatted srcset created from the `ngSrcset` input - * @param sizes The value of the `sizes` attribute passed in to the `` tag - */ - createPreloadLinkTag(renderer, src, srcset, sizes) { - if (ngDevMode) { - if (this.preloadedImages.size >= DEFAULT_PRELOADED_IMAGES_LIMIT) { - throw new i0['ɵRuntimeError']( - 2961 /* RuntimeErrorCode.TOO_MANY_PRELOADED_IMAGES */, - ngDevMode && - `The \`NgOptimizedImage\` directive has detected that more than ` + - `${DEFAULT_PRELOADED_IMAGES_LIMIT} images were marked as priority. ` + - `This might negatively affect an overall performance of the page. ` + - `To fix this, remove the "priority" attribute from images with less priority.` - ); - } - } - if (this.preloadedImages.has(src)) { - return; - } - this.preloadedImages.add(src); - const preload = renderer.createElement('link'); - renderer.setAttribute(preload, 'as', 'image'); - renderer.setAttribute(preload, 'href', src); - renderer.setAttribute(preload, 'rel', 'preload'); - renderer.setAttribute(preload, 'fetchpriority', 'high'); - if (sizes) { - renderer.setAttribute(preload, 'imageSizes', sizes); - } - if (srcset) { - renderer.setAttribute(preload, 'imageSrcset', srcset); - } - renderer.appendChild(this.document.head, preload); - } - } - PreloadLinkCreator.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PreloadLinkCreator, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Injectable, - }); - PreloadLinkCreator.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PreloadLinkCreator, - providedIn: 'root', - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: PreloadLinkCreator, - decorators: [ - { - type: i0.Injectable, - args: [{ providedIn: 'root' }], - }, - ], - }); - - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - /** - * When a Base64-encoded image is passed as an input to the `NgOptimizedImage` directive, - * an error is thrown. The image content (as a string) might be very long, thus making - * it hard to read an error message if the entire string is included. This const defines - * the number of characters that should be included into the error message. The rest - * of the content is truncated. - */ - const BASE64_IMG_MAX_LENGTH_IN_ERROR = 50; - /** - * RegExpr to determine whether a src in a srcset is using width descriptors. - * Should match something like: "100w, 200w". - */ - const VALID_WIDTH_DESCRIPTOR_SRCSET = /^((\s*\d+w\s*(,|$)){1,})$/; - /** - * RegExpr to determine whether a src in a srcset is using density descriptors. - * Should match something like: "1x, 2x, 50x". Also supports decimals like "1.5x, 1.50x". - */ - const VALID_DENSITY_DESCRIPTOR_SRCSET = /^((\s*\d+(\.\d+)?x\s*(,|$)){1,})$/; - /** - * Srcset values with a density descriptor higher than this value will actively - * throw an error. Such densities are not permitted as they cause image sizes - * to be unreasonably large and slow down LCP. - */ - const ABSOLUTE_SRCSET_DENSITY_CAP = 3; - /** - * Used only in error message text to communicate best practices, as we will - * only throw based on the slightly more conservative ABSOLUTE_SRCSET_DENSITY_CAP. - */ - const RECOMMENDED_SRCSET_DENSITY_CAP = 2; - /** - * Used in generating automatic density-based srcsets - */ - const DENSITY_SRCSET_MULTIPLIERS = [1, 2]; - /** - * Used to determine which breakpoints to use on full-width images - */ - const VIEWPORT_BREAKPOINT_CUTOFF = 640; - /** - * Used to determine whether two aspect ratios are similar in value. - */ - const ASPECT_RATIO_TOLERANCE = 0.1; - /** - * Used to determine whether the image has been requested at an overly - * large size compared to the actual rendered image size (after taking - * into account a typical device pixel ratio). In pixels. - */ - const OVERSIZED_IMAGE_TOLERANCE = 1000; - /** Info about built-in loaders we can test for. */ - const BUILT_IN_LOADERS = [ - imgixLoaderInfo, - imageKitLoaderInfo, - cloudinaryLoaderInfo, - ]; - const defaultConfig = { - breakpoints: [ - 16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, - 3840, - ], - }; - /** - * Injection token that configures the image optimized image functionality. - * - * @see `NgOptimizedImage` - * @publicApi - * @developerPreview - */ - const IMAGE_CONFIG = new i0.InjectionToken('ImageConfig', { - providedIn: 'root', - factory: () => defaultConfig, - }); - /** - * Directive that improves image loading performance by enforcing best practices. - * - * `NgOptimizedImage` ensures that the loading of the Largest Contentful Paint (LCP) image is - * prioritized by: - * - Automatically setting the `fetchpriority` attribute on the `` tag - * - Lazy loading non-priority images by default - * - Asserting that there is a corresponding preconnect link tag in the document head - * - * In addition, the directive: - * - Generates appropriate asset URLs if a corresponding `ImageLoader` function is provided - * - Automatically generates a srcset - * - Requires that `width` and `height` are set - * - Warns if `width` or `height` have been set incorrectly - * - Warns if the image will be visually distorted when rendered - * - * @usageNotes - * The `NgOptimizedImage` directive is marked as [standalone](guide/standalone-components) and can - * be imported directly. - * - * Follow the steps below to enable and use the directive: - * 1. Import it into the necessary NgModule or a standalone Component. - * 2. Optionally provide an `ImageLoader` if you use an image hosting service. - * 3. Update the necessary `` tags in templates and replace `src` attributes with `ngSrc`. - * Using a `ngSrc` allows the directive to control when the `src` gets set, which triggers an image - * download. - * - * Step 1: import the `NgOptimizedImage` directive. - * - * ```typescript - * import { NgOptimizedImage } from '@angular/common'; - * - * // Include it into the necessary NgModule - * @NgModule({ - * imports: [NgOptimizedImage], - * }) - * class AppModule {} - * - * // ... or a standalone Component - * @Component({ - * standalone: true - * imports: [NgOptimizedImage], - * }) - * class MyStandaloneComponent {} - * ``` - * - * Step 2: configure a loader. - * - * To use the **default loader**: no additional code changes are necessary. The URL returned by the - * generic loader will always match the value of "src". In other words, this loader applies no - * transformations to the resource URL and the value of the `ngSrc` attribute will be used as is. - * - * To use an existing loader for a **third-party image service**: add the provider factory for your - * chosen service to the `providers` array. In the example below, the Imgix loader is used: - * - * ```typescript - * import {provideImgixLoader} from '@angular/common'; - * - * // Call the function and add the result to the `providers` array: - * providers: [ - * provideImgixLoader("https://my.base.url/"), - * ], - * ``` - * - * The `NgOptimizedImage` directive provides the following functions: - * - `provideCloudflareLoader` - * - `provideCloudinaryLoader` - * - `provideImageKitLoader` - * - `provideImgixLoader` - * - * If you use a different image provider, you can create a custom loader function as described - * below. - * - * To use a **custom loader**: provide your loader function as a value for the `IMAGE_LOADER` DI - * token. - * - * ```typescript - * import {IMAGE_LOADER, ImageLoaderConfig} from '@angular/common'; - * - * // Configure the loader using the `IMAGE_LOADER` token. - * providers: [ - * { - * provide: IMAGE_LOADER, - * useValue: (config: ImageLoaderConfig) => { - * return `https://example.com/${config.src}-${config.width}.jpg}`; - * } - * }, - * ], - * ``` - * - * Step 3: update `` tags in templates to use `ngSrc` instead of `src`. - * - * ``` - * - * ``` - * - * @publicApi - */ - class NgOptimizedImage { - constructor() { - this.imageLoader = i0.inject(IMAGE_LOADER); - this.config = processConfig(i0.inject(IMAGE_CONFIG)); - this.renderer = i0.inject(i0.Renderer2); - this.imgElement = i0.inject(i0.ElementRef).nativeElement; - this.injector = i0.inject(i0.Injector); - this.isServer = isPlatformServer(i0.inject(i0.PLATFORM_ID)); - this.preloadLinkChecker = i0.inject(PreloadLinkCreator); - // a LCP image observer - should be injected only in the dev mode - this.lcpObserver = ngDevMode ? this.injector.get(LCPImageObserver) : null; - /** - * Calculate the rewritten `src` once and store it. - * This is needed to avoid repetitive calculations and make sure the directive cleanup in the - * `ngOnDestroy` does not rely on the `IMAGE_LOADER` logic (which in turn can rely on some other - * instance that might be already destroyed). - */ - this._renderedSrc = null; - this._priority = false; - this._disableOptimizedSrcset = false; - this._fill = false; - } - /** - * For responsive images: the intrinsic width of the image in pixels. - * For fixed size images: the desired rendered width of the image in pixels. - */ - set width(value) { - ngDevMode && assertGreaterThanZero(this, value, 'width'); - this._width = inputToInteger(value); - } - get width() { - return this._width; - } - /** - * For responsive images: the intrinsic height of the image in pixels. - * For fixed size images: the desired rendered height of the image in pixels.* The intrinsic - * height of the image in pixels. - */ - set height(value) { - ngDevMode && assertGreaterThanZero(this, value, 'height'); - this._height = inputToInteger(value); - } - get height() { - return this._height; - } - /** - * Indicates whether this image should have a high priority. - */ - set priority(value) { - this._priority = inputToBoolean(value); - } - get priority() { - return this._priority; - } - /** - * Disables automatic srcset generation for this image. - */ - set disableOptimizedSrcset(value) { - this._disableOptimizedSrcset = inputToBoolean(value); - } - get disableOptimizedSrcset() { - return this._disableOptimizedSrcset; - } - /** - * Sets the image to "fill mode", which eliminates the height/width requirement and adds - * styles such that the image fills its containing element. - * - * @developerPreview + } + /** + * Verifies that a specified input is set. */ - set fill(value) { - this._fill = inputToBoolean(value); - } - get fill() { - return this._fill; - } - /** @nodoc */ - ngOnInit() { - if (ngDevMode) { - assertNonEmptyInput(this, 'ngSrc', this.ngSrc); - assertValidNgSrcset(this, this.ngSrcset); - assertNoConflictingSrc(this); - if (this.ngSrcset) { - assertNoConflictingSrcset(this); - } - assertNotBase64Image(this); - assertNotBlobUrl(this); - if (this.fill) { - assertEmptyWidthAndHeight(this); - assertNonZeroRenderedHeight(this, this.imgElement, this.renderer); - } else { - assertNonEmptyWidthAndHeight(this); - // Only check for distorted images when not in fill mode, where - // images may be intentionally stretched, cropped or letterboxed. - assertNoImageDistortion(this, this.imgElement, this.renderer); - } - assertValidLoadingInput(this); - if (!this.ngSrcset) { - assertNoComplexSizes(this); - } - assertNotMissingBuiltInLoader(this.ngSrc, this.imageLoader); - if (this.priority) { - const checker = this.injector.get(PreconnectLinkChecker); - checker.assertPreconnect(this.getRewrittenSrc(), this.ngSrc); - } else { - // Monitor whether an image is an LCP element only in case - // the `priority` attribute is missing. Otherwise, an image - // has the necessary settings and no extra checks are required. - if (this.lcpObserver !== null) { - const ngZone = this.injector.get(i0.NgZone); - ngZone.runOutsideAngular(() => { - this.lcpObserver.registerImage( - this.getRewrittenSrc(), - this.ngSrc - ); - }); - } - } - } - this.setHostAttributes(); - } - setHostAttributes() { - // Must set width/height explicitly in case they are bound (in which case they will - // only be reflected and not found by the browser) - if (this.fill) { - if (!this.sizes) { - this.sizes = '100vw'; - } - } else { - this.setHostAttribute('width', this.width.toString()); - this.setHostAttribute('height', this.height.toString()); - } - this.setHostAttribute('loading', this.getLoadingBehavior()); - this.setHostAttribute('fetchpriority', this.getFetchPriority()); - // The `src` and `srcset` attributes should be set last since other attributes - // could affect the image's loading behavior. - const rewrittenSrc = this.getRewrittenSrc(); - this.setHostAttribute('src', rewrittenSrc); - let rewrittenSrcset = undefined; - if (this.sizes) { - this.setHostAttribute('sizes', this.sizes); - } - if (this.ngSrcset) { - rewrittenSrcset = this.getRewrittenSrcset(); - } else if ( - !this._disableOptimizedSrcset && - !this.srcset && - this.imageLoader !== noopImageLoader - ) { - rewrittenSrcset = this.getAutomaticSrcset(); - } - if (rewrittenSrcset) { - this.setHostAttribute('srcset', rewrittenSrcset); - } - if (this.isServer && this.priority) { - this.preloadLinkChecker.createPreloadLinkTag( - this.renderer, - rewrittenSrc, - rewrittenSrcset, - this.sizes - ); - } - } - /** @nodoc */ - ngOnChanges(changes) { - if (ngDevMode) { - assertNoPostInitInputChange(this, changes, [ - 'ngSrc', - 'ngSrcset', - 'width', - 'height', - 'priority', - 'fill', - 'loading', - 'sizes', - 'disableOptimizedSrcset', - ]); - } - } - getLoadingBehavior() { - if (!this.priority && this.loading !== undefined) { - return this.loading; - } - return this.priority ? 'eager' : 'lazy'; - } - getFetchPriority() { - return this.priority ? 'high' : 'auto'; - } - getRewrittenSrc() { - // ImageLoaderConfig supports setting a width property. However, we're not setting width here - // because if the developer uses rendered width instead of intrinsic width in the HTML width - // attribute, the image requested may be too small for 2x+ screens. - if (!this._renderedSrc) { - const imgConfig = { src: this.ngSrc }; - // Cache calculated image src to reuse it later in the code. - this._renderedSrc = this.imageLoader(imgConfig); - } - return this._renderedSrc; - } - getRewrittenSrcset() { - const widthSrcSet = VALID_WIDTH_DESCRIPTOR_SRCSET.test(this.ngSrcset); - const finalSrcs = this.ngSrcset - .split(',') - .filter((src) => src !== '') - .map((srcStr) => { - srcStr = srcStr.trim(); - const width = widthSrcSet - ? parseFloat(srcStr) - : parseFloat(srcStr) * this.width; - return `${this.imageLoader({ src: this.ngSrc, width })} ${srcStr}`; + function assertNonEmptyWidthAndHeight(dir) { + let missingAttributes = []; + if (dir.width === undefined) + missingAttributes.push('width'); + if (dir.height === undefined) + missingAttributes.push('height'); + if (missingAttributes.length > 0) { + throw new i0["ɵRuntimeError"](2954 /* RuntimeErrorCode.REQUIRED_INPUT_MISSING */, `${imgDirectiveDetails(dir.ngSrc)} these required attributes ` + + `are missing: ${missingAttributes.map(attr => `"${attr}"`).join(', ')}. ` + + `Including "width" and "height" attributes will prevent image-related layout shifts. ` + + `To fix this, include "width" and "height" attributes on the image tag or turn on ` + + `"fill" mode with the \`fill\` attribute.`); + } + } + /** + * Verifies that width and height are not set. Used in fill mode, where those attributes don't make + * sense. + */ + function assertEmptyWidthAndHeight(dir) { + if (dir.width || dir.height) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the attributes \`height\` and/or \`width\` are present ` + + `along with the \`fill\` attribute. Because \`fill\` mode causes an image to fill its containing ` + + `element, the size attributes have no effect and should be removed.`); + } + } + /** + * Verifies that the rendered image has a nonzero height. If the image is in fill mode, provides + * guidance that this can be caused by the containing element's CSS position property. + */ + function assertNonZeroRenderedHeight(dir, img, renderer) { + const removeListenerFn = renderer.listen(img, 'load', () => { + removeListenerFn(); + const renderedHeight = parseFloat(img.clientHeight); + if (dir.fill && renderedHeight === 0) { + console.warn(i0["ɵformatRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the height of the fill-mode image is zero. ` + + `This is likely because the containing element does not have the CSS 'position' ` + + `property set to one of the following: "relative", "fixed", or "absolute". ` + + `To fix this problem, make sure the container element has the CSS 'position' ` + + `property defined and the height of the element is not zero.`)); + } }); - return finalSrcs.join(', '); - } - getAutomaticSrcset() { - if (this.sizes) { - return this.getResponsiveSrcset(); - } else { - return this.getFixedSrcset(); - } - } - getResponsiveSrcset() { - var _a; - const { breakpoints } = this.config; - let filteredBreakpoints = breakpoints; - if ( - ((_a = this.sizes) === null || _a === void 0 ? void 0 : _a.trim()) === - '100vw' - ) { - // Since this is a full-screen-width image, our srcset only needs to include - // breakpoints with full viewport widths. - filteredBreakpoints = breakpoints.filter( - (bp) => bp >= VIEWPORT_BREAKPOINT_CUTOFF - ); - } - const finalSrcs = filteredBreakpoints.map( - (bp) => `${this.imageLoader({ src: this.ngSrc, width: bp })} ${bp}w` - ); - return finalSrcs.join(', '); - } - getFixedSrcset() { - const finalSrcs = DENSITY_SRCSET_MULTIPLIERS.map( - (multiplier) => - `${this.imageLoader({ - src: this.ngSrc, - width: this.width * multiplier, - })} ${multiplier}x` - ); - return finalSrcs.join(', '); - } - /** @nodoc */ - ngOnDestroy() { - if (ngDevMode) { - if ( - !this.priority && - this._renderedSrc !== null && - this.lcpObserver !== null - ) { - this.lcpObserver.unregisterImage(this._renderedSrc); - } - } - } - setHostAttribute(name, value) { - this.renderer.setAttribute(this.imgElement, name, value); - } - } - NgOptimizedImage.ɵfac = i0__namespace.ɵɵngDeclareFactory({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgOptimizedImage, - deps: [], - target: i0__namespace.ɵɵFactoryTarget.Directive, - }); - NgOptimizedImage.ɵdir = i0__namespace.ɵɵngDeclareDirective({ - minVersion: '14.0.0', - version: '15.0.1', - type: NgOptimizedImage, - isStandalone: true, - selector: 'img[ngSrc]', - inputs: { - ngSrc: 'ngSrc', - ngSrcset: 'ngSrcset', - sizes: 'sizes', - width: 'width', - height: 'height', - loading: 'loading', - priority: 'priority', - disableOptimizedSrcset: 'disableOptimizedSrcset', - fill: 'fill', - src: 'src', - srcset: 'srcset', - }, - host: { - properties: { - 'style.position': 'fill ? "absolute" : null', - 'style.width': 'fill ? "100%" : null', - 'style.height': 'fill ? "100%" : null', - 'style.inset': 'fill ? "0px" : null', - }, - }, - usesOnChanges: true, - ngImport: i0__namespace, - }); - i0__namespace.ɵɵngDeclareClassMetadata({ - minVersion: '12.0.0', - version: '15.0.1', - ngImport: i0__namespace, - type: NgOptimizedImage, - decorators: [ - { - type: i0.Directive, - args: [ - { - standalone: true, - selector: 'img[ngSrc]', - host: { - '[style.position]': 'fill ? "absolute" : null', - '[style.width]': 'fill ? "100%" : null', - '[style.height]': 'fill ? "100%" : null', - '[style.inset]': 'fill ? "0px" : null', - }, - }, - ], - }, - ], - propDecorators: { - ngSrc: [ - { - type: i0.Input, - }, - ], - ngSrcset: [ - { - type: i0.Input, - }, - ], - sizes: [ - { - type: i0.Input, - }, - ], - width: [ - { - type: i0.Input, - }, - ], - height: [ - { - type: i0.Input, - }, - ], - loading: [ - { - type: i0.Input, - }, - ], - priority: [ - { - type: i0.Input, - }, - ], - disableOptimizedSrcset: [ - { - type: i0.Input, - }, - ], - fill: [ - { - type: i0.Input, - }, - ], - src: [ - { - type: i0.Input, - }, - ], - srcset: [ - { - type: i0.Input, - }, - ], - }, - }); - /***** Helpers *****/ - /** - * Convert input value to integer. - */ - function inputToInteger(value) { - return typeof value === 'string' ? parseInt(value, 10) : value; - } - /** - * Convert input value to boolean. - */ - function inputToBoolean(value) { - return value != null && `${value}` !== 'false'; - } - /** - * Sorts provided config breakpoints and uses defaults. - */ - function processConfig(config) { - let sortedBreakpoints = {}; - if (config.breakpoints) { - sortedBreakpoints.breakpoints = config.breakpoints.sort((a, b) => a - b); - } - return Object.assign({}, defaultConfig, config, sortedBreakpoints); - } - /***** Assert functions *****/ - /** - * Verifies that there is no `src` set on a host element. - */ - function assertNoConflictingSrc(dir) { - if (dir.src) { - throw new i0['ɵRuntimeError']( - 2950 /* RuntimeErrorCode.UNEXPECTED_SRC_ATTR */, - `${imgDirectiveDetails( - dir.ngSrc - )} both \`src\` and \`ngSrc\` have been set. ` + - `Supplying both of these attributes breaks lazy loading. ` + - `The NgOptimizedImage directive sets \`src\` itself based on the value of \`ngSrc\`. ` + - `To fix this, please remove the \`src\` attribute.` - ); - } - } - /** - * Verifies that there is no `srcset` set on a host element. - */ - function assertNoConflictingSrcset(dir) { - if (dir.srcset) { - throw new i0['ɵRuntimeError']( - 2951 /* RuntimeErrorCode.UNEXPECTED_SRCSET_ATTR */, - `${imgDirectiveDetails( - dir.ngSrc - )} both \`srcset\` and \`ngSrcset\` have been set. ` + - `Supplying both of these attributes breaks lazy loading. ` + - `The NgOptimizedImage directive sets \`srcset\` itself based on the value of ` + - `\`ngSrcset\`. To fix this, please remove the \`srcset\` attribute.` - ); - } - } - /** - * Verifies that the `ngSrc` is not a Base64-encoded image. - */ - function assertNotBase64Image(dir) { - let ngSrc = dir.ngSrc.trim(); - if (ngSrc.startsWith('data:')) { - if (ngSrc.length > BASE64_IMG_MAX_LENGTH_IN_ERROR) { - ngSrc = ngSrc.substring(0, BASE64_IMG_MAX_LENGTH_IN_ERROR) + '...'; - } - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc, - false - )} \`ngSrc\` is a Base64-encoded string ` + - `(${ngSrc}). NgOptimizedImage does not support Base64-encoded strings. ` + - `To fix this, disable the NgOptimizedImage directive for this element ` + - `by removing \`ngSrc\` and using a standard \`src\` attribute instead.` - ); - } - } - /** - * Verifies that the 'sizes' only includes responsive values. - */ - function assertNoComplexSizes(dir) { - let sizes = dir.sizes; - if ( - sizes === null || sizes === void 0 - ? void 0 - : sizes.match(/((\)|,)\s|^)\d+px/) - ) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc, - false - )} \`sizes\` was set to a string including ` + - `pixel values. For automatic \`srcset\` generation, \`sizes\` must only include responsive ` + - `values, such as \`sizes="50vw"\` or \`sizes="(min-width: 768px) 50vw, 100vw"\`. ` + - `To fix this, modify the \`sizes\` attribute, or provide your own \`ngSrcset\` value directly.` - ); - } - } - /** - * Verifies that the `ngSrc` is not a Blob URL. - */ - function assertNotBlobUrl(dir) { - const ngSrc = dir.ngSrc.trim(); - if (ngSrc.startsWith('blob:')) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc - )} \`ngSrc\` was set to a blob URL (${ngSrc}). ` + - `Blob URLs are not supported by the NgOptimizedImage directive. ` + - `To fix this, disable the NgOptimizedImage directive for this element ` + - `by removing \`ngSrc\` and using a regular \`src\` attribute instead.` - ); - } - } - /** - * Verifies that the input is set to a non-empty string. - */ - function assertNonEmptyInput(dir, name, value) { - const isString = typeof value === 'string'; - const isEmptyString = isString && value.trim() === ''; - if (!isString || isEmptyString) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails(dir.ngSrc)} \`${name}\` has an invalid value ` + - `(\`${value}\`). To fix this, change the value to a non-empty string.` - ); - } - } - /** - * Verifies that the `ngSrcset` is in a valid format, e.g. "100w, 200w" or "1x, 2x". - */ - function assertValidNgSrcset(dir, value) { - if (value == null) return; - assertNonEmptyInput(dir, 'ngSrcset', value); - const stringVal = value; - const isValidWidthDescriptor = - VALID_WIDTH_DESCRIPTOR_SRCSET.test(stringVal); - const isValidDensityDescriptor = - VALID_DENSITY_DESCRIPTOR_SRCSET.test(stringVal); - if (isValidDensityDescriptor) { - assertUnderDensityCap(dir, stringVal); - } - const isValidSrcset = isValidWidthDescriptor || isValidDensityDescriptor; - if (!isValidSrcset) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc - )} \`ngSrcset\` has an invalid value (\`${value}\`). ` + - `To fix this, supply \`ngSrcset\` using a comma-separated list of one or more width ` + - `descriptors (e.g. "100w, 200w") or density descriptors (e.g. "1x, 2x").` - ); - } - } - function assertUnderDensityCap(dir, value) { - const underDensityCap = value - .split(',') - .every( - (num) => num === '' || parseFloat(num) <= ABSOLUTE_SRCSET_DENSITY_CAP - ); - if (!underDensityCap) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc - )} the \`ngSrcset\` contains an unsupported image density:` + - `\`${value}\`. NgOptimizedImage generally recommends a max image density of ` + - `${RECOMMENDED_SRCSET_DENSITY_CAP}x but supports image densities up to ` + - `${ABSOLUTE_SRCSET_DENSITY_CAP}x. The human eye cannot distinguish between image densities ` + - `greater than ${RECOMMENDED_SRCSET_DENSITY_CAP}x - which makes them unnecessary for ` + - `most use cases. Images that will be pinch-zoomed are typically the primary use case for ` + - `${ABSOLUTE_SRCSET_DENSITY_CAP}x images. Please remove the high density descriptor and try again.` - ); - } - } - /** - * Creates a `RuntimeError` instance to represent a situation when an input is set after - * the directive has initialized. - */ - function postInitInputChangeError(dir, inputName) { - let reason; - if (inputName === 'width' || inputName === 'height') { - reason = - `Changing \`${inputName}\` may result in different attribute value ` + - `applied to the underlying image element and cause layout shifts on a page.`; - } else { - reason = - `Changing the \`${inputName}\` would have no effect on the underlying ` + - `image element, because the resource loading has already occurred.`; - } - return new i0[ - 'ɵRuntimeError' - ](2953 /* RuntimeErrorCode.UNEXPECTED_INPUT_CHANGE */, `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` was updated after initialization. ` + `The NgOptimizedImage directive will not react to this input change. ${reason} ` + `To fix this, either switch \`${inputName}\` to a static value ` + `or wrap the image element in an *ngIf that is gated on the necessary value.`); - } - /** - * Verify that none of the listed inputs has changed. - */ - function assertNoPostInitInputChange(dir, changes, inputs) { - inputs.forEach((input) => { - const isUpdated = changes.hasOwnProperty(input); - if (isUpdated && !changes[input].isFirstChange()) { - if (input === 'ngSrc') { - // When the `ngSrc` input changes, we detect that only in the - // `ngOnChanges` hook, thus the `ngSrc` is already set. We use - // `ngSrc` in the error message, so we use a previous value, but - // not the updated one in it. - dir = { ngSrc: changes[input].previousValue }; - } - throw postInitInputChangeError(dir, input); - } - }); - } - /** - * Verifies that a specified input is a number greater than 0. - */ - function assertGreaterThanZero(dir, inputValue, inputName) { - const validNumber = typeof inputValue === 'number' && inputValue > 0; - const validString = - typeof inputValue === 'string' && - /^\d+$/.test(inputValue.trim()) && - parseInt(inputValue) > 0; - if (!validNumber && !validString) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc - )} \`${inputName}\` has an invalid value ` + - `(\`${inputValue}\`). To fix this, provide \`${inputName}\` ` + - `as a number greater than 0.` - ); - } - } - /** - * Verifies that the rendered image is not visually distorted. Effectively this is checking: - * - Whether the "width" and "height" attributes reflect the actual dimensions of the image. - * - Whether image styling is "correct" (see below for a longer explanation). - */ - function assertNoImageDistortion(dir, img, renderer) { - const removeListenerFn = renderer.listen(img, 'load', () => { - removeListenerFn(); - // TODO: `clientWidth`, `clientHeight`, `naturalWidth` and `naturalHeight` - // are typed as number, but we run `parseFloat` (which accepts strings only). - // Verify whether `parseFloat` is needed in the cases below. - const renderedWidth = parseFloat(img.clientWidth); - const renderedHeight = parseFloat(img.clientHeight); - const renderedAspectRatio = renderedWidth / renderedHeight; - const nonZeroRenderedDimensions = - renderedWidth !== 0 && renderedHeight !== 0; - const intrinsicWidth = parseFloat(img.naturalWidth); - const intrinsicHeight = parseFloat(img.naturalHeight); - const intrinsicAspectRatio = intrinsicWidth / intrinsicHeight; - const suppliedWidth = dir.width; - const suppliedHeight = dir.height; - const suppliedAspectRatio = suppliedWidth / suppliedHeight; - // Tolerance is used to account for the impact of subpixel rendering. - // Due to subpixel rendering, the rendered, intrinsic, and supplied - // aspect ratios of a correctly configured image may not exactly match. - // For example, a `width=4030 height=3020` image might have a rendered - // size of "1062w, 796.48h". (An aspect ratio of 1.334... vs. 1.333...) - const inaccurateDimensions = - Math.abs(suppliedAspectRatio - intrinsicAspectRatio) > - ASPECT_RATIO_TOLERANCE; - const stylingDistortion = - nonZeroRenderedDimensions && - Math.abs(intrinsicAspectRatio - renderedAspectRatio) > - ASPECT_RATIO_TOLERANCE; - if (inaccurateDimensions) { - console.warn( - i0['ɵformatRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc - )} the aspect ratio of the image does not match ` + - `the aspect ratio indicated by the width and height attributes. ` + - `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` + - `(aspect-ratio: ${intrinsicAspectRatio}). \nSupplied width and height attributes: ` + - `${suppliedWidth}w x ${suppliedHeight}h (aspect-ratio: ${suppliedAspectRatio}). ` + - `\nTo fix this, update the width and height attributes.` - ) - ); - } else if (stylingDistortion) { - console.warn( - i0['ɵformatRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc - )} the aspect ratio of the rendered image ` + - `does not match the image's intrinsic aspect ratio. ` + - `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` + - `(aspect-ratio: ${intrinsicAspectRatio}). \nRendered image size: ` + - `${renderedWidth}w x ${renderedHeight}h (aspect-ratio: ` + - `${renderedAspectRatio}). \nThis issue can occur if "width" and "height" ` + - `attributes are added to an image without updating the corresponding ` + - `image styling. To fix this, adjust image styling. In most cases, ` + - `adding "height: auto" or "width: auto" to the image styling will fix ` + - `this issue.` - ) - ); - } else if (!dir.ngSrcset && nonZeroRenderedDimensions) { - // If `ngSrcset` hasn't been set, sanity check the intrinsic size. - const recommendedWidth = RECOMMENDED_SRCSET_DENSITY_CAP * renderedWidth; - const recommendedHeight = - RECOMMENDED_SRCSET_DENSITY_CAP * renderedHeight; - const oversizedWidth = - intrinsicWidth - recommendedWidth >= OVERSIZED_IMAGE_TOLERANCE; - const oversizedHeight = - intrinsicHeight - recommendedHeight >= OVERSIZED_IMAGE_TOLERANCE; - if (oversizedWidth || oversizedHeight) { - console.warn( - i0['ɵformatRuntimeError']( - 2960 /* RuntimeErrorCode.OVERSIZED_IMAGE */, - `${imgDirectiveDetails( - dir.ngSrc - )} the intrinsic image is significantly ` + - `larger than necessary. ` + - `\nRendered image size: ${renderedWidth}w x ${renderedHeight}h. ` + - `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h. ` + - `\nRecommended intrinsic image size: ${recommendedWidth}w x ${recommendedHeight}h. ` + - `\nNote: Recommended intrinsic image size is calculated assuming a maximum DPR of ` + - `${RECOMMENDED_SRCSET_DENSITY_CAP}. To improve loading time, resize the image ` + - `or consider using the "ngSrcset" and "sizes" attributes.` - ) - ); - } - } - }); - } - /** - * Verifies that a specified input is set. - */ - function assertNonEmptyWidthAndHeight(dir) { - let missingAttributes = []; - if (dir.width === undefined) missingAttributes.push('width'); - if (dir.height === undefined) missingAttributes.push('height'); - if (missingAttributes.length > 0) { - throw new i0['ɵRuntimeError']( - 2954 /* RuntimeErrorCode.REQUIRED_INPUT_MISSING */, - `${imgDirectiveDetails(dir.ngSrc)} these required attributes ` + - `are missing: ${missingAttributes - .map((attr) => `"${attr}"`) - .join(', ')}. ` + - `Including "width" and "height" attributes will prevent image-related layout shifts. ` + - `To fix this, include "width" and "height" attributes on the image tag or turn on ` + - `"fill" mode with the \`fill\` attribute.` - ); - } - } - /** - * Verifies that width and height are not set. Used in fill mode, where those attributes don't make - * sense. - */ - function assertEmptyWidthAndHeight(dir) { - if (dir.width || dir.height) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc - )} the attributes \`height\` and/or \`width\` are present ` + - `along with the \`fill\` attribute. Because \`fill\` mode causes an image to fill its containing ` + - `element, the size attributes have no effect and should be removed.` - ); - } - } - /** - * Verifies that the rendered image has a nonzero height. If the image is in fill mode, provides - * guidance that this can be caused by the containing element's CSS position property. - */ - function assertNonZeroRenderedHeight(dir, img, renderer) { - const removeListenerFn = renderer.listen(img, 'load', () => { - removeListenerFn(); - const renderedHeight = parseFloat(img.clientHeight); - if (dir.fill && renderedHeight === 0) { - console.warn( - i0['ɵformatRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails( - dir.ngSrc - )} the height of the fill-mode image is zero. ` + - `This is likely because the containing element does not have the CSS 'position' ` + - `property set to one of the following: "relative", "fixed", or "absolute". ` + - `To fix this problem, make sure the container element has the CSS 'position' ` + - `property defined and the height of the element is not zero.` - ) - ); - } - }); - } - /** - * Verifies that the `loading` attribute is set to a valid input & - * is not used on priority images. - */ - function assertValidLoadingInput(dir) { - if (dir.loading && dir.priority) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` + - `was used on an image that was marked "priority". ` + - `Setting \`loading\` on priority images is not allowed ` + - `because these images will always be eagerly loaded. ` + - `To fix this, remove the “loading” attribute from the priority image.` - ); - } - const validInputs = ['auto', 'eager', 'lazy']; - if (typeof dir.loading === 'string' && !validInputs.includes(dir.loading)) { - throw new i0['ɵRuntimeError']( - 2952 /* RuntimeErrorCode.INVALID_INPUT */, - `${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` + - `has an invalid value (\`${dir.loading}\`). ` + - `To fix this, provide a valid value ("lazy", "eager", or "auto").` - ); - } - } - /** - * Warns if NOT using a loader (falling back to the generic loader) and - * the image appears to be hosted on one of the image CDNs for which - * we do have a built-in image loader. Suggests switching to the - * built-in loader. - * - * @param ngSrc Value of the ngSrc attribute - * @param imageLoader ImageLoader provided - */ - function assertNotMissingBuiltInLoader(ngSrc, imageLoader) { - if (imageLoader === noopImageLoader) { - let builtInLoaderName = ''; - for (const loader of BUILT_IN_LOADERS) { - if (loader.testUrl(ngSrc)) { - builtInLoaderName = loader.name; - break; - } - } - if (builtInLoaderName) { - console.warn( - i0['ɵformatRuntimeError']( - 2962 /* RuntimeErrorCode.MISSING_BUILTIN_LOADER */, - `NgOptimizedImage: It looks like your images may be hosted on the ` + - `${builtInLoaderName} CDN, but your app is not using Angular's ` + - `built-in loader for that CDN. We recommend switching to use ` + - `the built-in by calling \`provide${builtInLoaderName}Loader()\` ` + - `in your \`providers\` and passing it your instance's base URL. ` + - `If you don't want to use the built-in loader, define a custom ` + - `loader function using IMAGE_LOADER to silence this warning.` - ) - ); - } - } - } + } + /** + * Verifies that the `loading` attribute is set to a valid input & + * is not used on priority images. + */ + function assertValidLoadingInput(dir) { + if (dir.loading && dir.priority) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` + + `was used on an image that was marked "priority". ` + + `Setting \`loading\` on priority images is not allowed ` + + `because these images will always be eagerly loaded. ` + + `To fix this, remove the “loading” attribute from the priority image.`); + } + const validInputs = ['auto', 'eager', 'lazy']; + if (typeof dir.loading === 'string' && !validInputs.includes(dir.loading)) { + throw new i0["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` + + `has an invalid value (\`${dir.loading}\`). ` + + `To fix this, provide a valid value ("lazy", "eager", or "auto").`); + } + } + /** + * Warns if NOT using a loader (falling back to the generic loader) and + * the image appears to be hosted on one of the image CDNs for which + * we do have a built-in image loader. Suggests switching to the + * built-in loader. + * + * @param ngSrc Value of the ngSrc attribute + * @param imageLoader ImageLoader provided + */ + function assertNotMissingBuiltInLoader(ngSrc, imageLoader) { + if (imageLoader === noopImageLoader) { + let builtInLoaderName = ''; + for (const loader of BUILT_IN_LOADERS) { + if (loader.testUrl(ngSrc)) { + builtInLoaderName = loader.name; + break; + } + } + if (builtInLoaderName) { + console.warn(i0["ɵformatRuntimeError"](2962 /* RuntimeErrorCode.MISSING_BUILTIN_LOADER */, `NgOptimizedImage: It looks like your images may be hosted on the ` + + `${builtInLoaderName} CDN, but your app is not using Angular's ` + + `built-in loader for that CDN. We recommend switching to use ` + + `the built-in by calling \`provide${builtInLoaderName}Loader()\` ` + + `in your \`providers\` and passing it your instance's base URL. ` + + `If you don't want to use the built-in loader, define a custom ` + + `loader function using IMAGE_LOADER to silence this warning.`)); + } + } + } + + exports.APP_BASE_HREF = APP_BASE_HREF; + exports.AsyncPipe = AsyncPipe; + exports.CommonModule = CommonModule; + exports.CurrencyPipe = CurrencyPipe; + exports.DATE_PIPE_DEFAULT_OPTIONS = DATE_PIPE_DEFAULT_OPTIONS; + exports.DATE_PIPE_DEFAULT_TIMEZONE = DATE_PIPE_DEFAULT_TIMEZONE; + exports.DOCUMENT = DOCUMENT; + exports.DatePipe = DatePipe; + exports.DecimalPipe = DecimalPipe; + exports.HashLocationStrategy = HashLocationStrategy; + exports.I18nPluralPipe = I18nPluralPipe; + exports.I18nSelectPipe = I18nSelectPipe; + exports.IMAGE_CONFIG = IMAGE_CONFIG; + exports.IMAGE_LOADER = IMAGE_LOADER; + exports.JsonPipe = JsonPipe; + exports.KeyValuePipe = KeyValuePipe; + exports.LOCATION_INITIALIZED = LOCATION_INITIALIZED; + exports.Location = Location; + exports.LocationStrategy = LocationStrategy; + exports.LowerCasePipe = LowerCasePipe; + exports.NgClass = NgClass; + exports.NgComponentOutlet = NgComponentOutlet; + exports.NgFor = NgForOf; + exports.NgForOf = NgForOf; + exports.NgForOfContext = NgForOfContext; + exports.NgIf = NgIf; + exports.NgIfContext = NgIfContext; + exports.NgLocaleLocalization = NgLocaleLocalization; + exports.NgLocalization = NgLocalization; + exports.NgOptimizedImage = NgOptimizedImage; + exports.NgPlural = NgPlural; + exports.NgPluralCase = NgPluralCase; + exports.NgStyle = NgStyle; + exports.NgSwitch = NgSwitch; + exports.NgSwitchCase = NgSwitchCase; + exports.NgSwitchDefault = NgSwitchDefault; + exports.NgTemplateOutlet = NgTemplateOutlet; + exports.PRECONNECT_CHECK_BLOCKLIST = PRECONNECT_CHECK_BLOCKLIST; + exports.PathLocationStrategy = PathLocationStrategy; + exports.PercentPipe = PercentPipe; + exports.PlatformLocation = PlatformLocation; + exports.SlicePipe = SlicePipe; + exports.TitleCasePipe = TitleCasePipe; + exports.UpperCasePipe = UpperCasePipe; + exports.VERSION = VERSION; + exports.ViewportScroller = ViewportScroller; + exports.XhrFactory = XhrFactory; + exports.formatCurrency = formatCurrency; + exports.formatDate = formatDate; + exports.formatNumber = formatNumber; + exports.formatPercent = formatPercent; + exports.getCurrencySymbol = getCurrencySymbol; + exports.getLocaleCurrencyCode = getLocaleCurrencyCode; + exports.getLocaleCurrencyName = getLocaleCurrencyName; + exports.getLocaleCurrencySymbol = getLocaleCurrencySymbol; + exports.getLocaleDateFormat = getLocaleDateFormat; + exports.getLocaleDateTimeFormat = getLocaleDateTimeFormat; + exports.getLocaleDayNames = getLocaleDayNames; + exports.getLocaleDayPeriods = getLocaleDayPeriods; + exports.getLocaleDirection = getLocaleDirection; + exports.getLocaleEraNames = getLocaleEraNames; + exports.getLocaleExtraDayPeriodRules = getLocaleExtraDayPeriodRules; + exports.getLocaleExtraDayPeriods = getLocaleExtraDayPeriods; + exports.getLocaleFirstDayOfWeek = getLocaleFirstDayOfWeek; + exports.getLocaleId = getLocaleId; + exports.getLocaleMonthNames = getLocaleMonthNames; + exports.getLocaleNumberFormat = getLocaleNumberFormat; + exports.getLocaleNumberSymbol = getLocaleNumberSymbol; + exports.getLocalePluralCase = getLocalePluralCase; + exports.getLocaleTimeFormat = getLocaleTimeFormat; + exports.getLocaleWeekEndRange = getLocaleWeekEndRange; + exports.getNumberOfCurrencyDigits = getNumberOfCurrencyDigits; + exports.isPlatformBrowser = isPlatformBrowser; + exports.isPlatformServer = isPlatformServer; + exports.isPlatformWorkerApp = isPlatformWorkerApp; + exports.isPlatformWorkerUi = isPlatformWorkerUi; + exports.provideCloudflareLoader = provideCloudflareLoader; + exports.provideCloudinaryLoader = provideCloudinaryLoader; + exports.provideImageKitLoader = provideImageKitLoader; + exports.provideImgixLoader = provideImgixLoader; + exports.registerLocaleData = registerLocaleData; + exports["ɵBrowserPlatformLocation"] = BrowserPlatformLocation; + exports["ɵDomAdapter"] = DomAdapter; + exports["ɵNullViewportScroller"] = NullViewportScroller; + exports["ɵPLATFORM_BROWSER_ID"] = PLATFORM_BROWSER_ID; + exports["ɵPLATFORM_SERVER_ID"] = PLATFORM_SERVER_ID; + exports["ɵPLATFORM_WORKER_APP_ID"] = PLATFORM_WORKER_APP_ID; + exports["ɵPLATFORM_WORKER_UI_ID"] = PLATFORM_WORKER_UI_ID; + exports["ɵgetDOM"] = getDOM; + exports["ɵparseCookieValue"] = parseCookieValue; + exports["ɵsetRootDomAdapter"] = setRootDomAdapter; - exports.APP_BASE_HREF = APP_BASE_HREF; - exports.AsyncPipe = AsyncPipe; - exports.CommonModule = CommonModule; - exports.CurrencyPipe = CurrencyPipe; - exports.DATE_PIPE_DEFAULT_OPTIONS = DATE_PIPE_DEFAULT_OPTIONS; - exports.DATE_PIPE_DEFAULT_TIMEZONE = DATE_PIPE_DEFAULT_TIMEZONE; - exports.DOCUMENT = DOCUMENT; - exports.DatePipe = DatePipe; - exports.DecimalPipe = DecimalPipe; - exports.HashLocationStrategy = HashLocationStrategy; - exports.I18nPluralPipe = I18nPluralPipe; - exports.I18nSelectPipe = I18nSelectPipe; - exports.IMAGE_CONFIG = IMAGE_CONFIG; - exports.IMAGE_LOADER = IMAGE_LOADER; - exports.JsonPipe = JsonPipe; - exports.KeyValuePipe = KeyValuePipe; - exports.LOCATION_INITIALIZED = LOCATION_INITIALIZED; - exports.Location = Location; - exports.LocationStrategy = LocationStrategy; - exports.LowerCasePipe = LowerCasePipe; - exports.NgClass = NgClass; - exports.NgComponentOutlet = NgComponentOutlet; - exports.NgFor = NgForOf; - exports.NgForOf = NgForOf; - exports.NgForOfContext = NgForOfContext; - exports.NgIf = NgIf; - exports.NgIfContext = NgIfContext; - exports.NgLocaleLocalization = NgLocaleLocalization; - exports.NgLocalization = NgLocalization; - exports.NgOptimizedImage = NgOptimizedImage; - exports.NgPlural = NgPlural; - exports.NgPluralCase = NgPluralCase; - exports.NgStyle = NgStyle; - exports.NgSwitch = NgSwitch; - exports.NgSwitchCase = NgSwitchCase; - exports.NgSwitchDefault = NgSwitchDefault; - exports.NgTemplateOutlet = NgTemplateOutlet; - exports.PRECONNECT_CHECK_BLOCKLIST = PRECONNECT_CHECK_BLOCKLIST; - exports.PathLocationStrategy = PathLocationStrategy; - exports.PercentPipe = PercentPipe; - exports.PlatformLocation = PlatformLocation; - exports.SlicePipe = SlicePipe; - exports.TitleCasePipe = TitleCasePipe; - exports.UpperCasePipe = UpperCasePipe; - exports.VERSION = VERSION; - exports.ViewportScroller = ViewportScroller; - exports.XhrFactory = XhrFactory; - exports.formatCurrency = formatCurrency; - exports.formatDate = formatDate; - exports.formatNumber = formatNumber; - exports.formatPercent = formatPercent; - exports.getCurrencySymbol = getCurrencySymbol; - exports.getLocaleCurrencyCode = getLocaleCurrencyCode; - exports.getLocaleCurrencyName = getLocaleCurrencyName; - exports.getLocaleCurrencySymbol = getLocaleCurrencySymbol; - exports.getLocaleDateFormat = getLocaleDateFormat; - exports.getLocaleDateTimeFormat = getLocaleDateTimeFormat; - exports.getLocaleDayNames = getLocaleDayNames; - exports.getLocaleDayPeriods = getLocaleDayPeriods; - exports.getLocaleDirection = getLocaleDirection; - exports.getLocaleEraNames = getLocaleEraNames; - exports.getLocaleExtraDayPeriodRules = getLocaleExtraDayPeriodRules; - exports.getLocaleExtraDayPeriods = getLocaleExtraDayPeriods; - exports.getLocaleFirstDayOfWeek = getLocaleFirstDayOfWeek; - exports.getLocaleId = getLocaleId; - exports.getLocaleMonthNames = getLocaleMonthNames; - exports.getLocaleNumberFormat = getLocaleNumberFormat; - exports.getLocaleNumberSymbol = getLocaleNumberSymbol; - exports.getLocalePluralCase = getLocalePluralCase; - exports.getLocaleTimeFormat = getLocaleTimeFormat; - exports.getLocaleWeekEndRange = getLocaleWeekEndRange; - exports.getNumberOfCurrencyDigits = getNumberOfCurrencyDigits; - exports.isPlatformBrowser = isPlatformBrowser; - exports.isPlatformServer = isPlatformServer; - exports.isPlatformWorkerApp = isPlatformWorkerApp; - exports.isPlatformWorkerUi = isPlatformWorkerUi; - exports.provideCloudflareLoader = provideCloudflareLoader; - exports.provideCloudinaryLoader = provideCloudinaryLoader; - exports.provideImageKitLoader = provideImageKitLoader; - exports.provideImgixLoader = provideImgixLoader; - exports.registerLocaleData = registerLocaleData; - exports['ɵBrowserPlatformLocation'] = BrowserPlatformLocation; - exports['ɵDomAdapter'] = DomAdapter; - exports['ɵNullViewportScroller'] = NullViewportScroller; - exports['ɵPLATFORM_BROWSER_ID'] = PLATFORM_BROWSER_ID; - exports['ɵPLATFORM_SERVER_ID'] = PLATFORM_SERVER_ID; - exports['ɵPLATFORM_WORKER_APP_ID'] = PLATFORM_WORKER_APP_ID; - exports['ɵPLATFORM_WORKER_UI_ID'] = PLATFORM_WORKER_UI_ID; - exports['ɵgetDOM'] = getDOM; - exports['ɵparseCookieValue'] = parseCookieValue; - exports['ɵsetRootDomAdapter'] = setRootDomAdapter; -}); +})); diff --git a/libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-compiler.js b/libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-compiler.js index cf363e36a..409aa238c 100644 --- a/libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-compiler.js +++ b/libs/code-demos/assets/runner/ng2/build-umd-bundles/bundles/angular-compiler.js @@ -1,13 +1,8 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' - ? factory(exports) - : typeof define === 'function' && define.amd - ? define(['exports'], factory) - : ((global = - typeof globalThis !== 'undefined' ? globalThis : global || self), - factory((global.ngCompiler = {}))); -})(this, function (exports) { - 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ngCompiler = {})); +})(this, (function (exports) { 'use strict'; /** * @license Angular v15.1.0-next.0 @@ -24,43 +19,37 @@ */ exports.TagContentType = void 0; (function (TagContentType) { - TagContentType[(TagContentType['RAW_TEXT'] = 0)] = 'RAW_TEXT'; - TagContentType[(TagContentType['ESCAPABLE_RAW_TEXT'] = 1)] = - 'ESCAPABLE_RAW_TEXT'; - TagContentType[(TagContentType['PARSABLE_DATA'] = 2)] = 'PARSABLE_DATA'; + TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT"; + TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT"; + TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA"; })(exports.TagContentType || (exports.TagContentType = {})); function splitNsName(elementName) { - if (elementName[0] != ':') { - return [null, elementName]; - } - const colonIndex = elementName.indexOf(':', 1); - if (colonIndex === -1) { - throw new Error( - `Unsupported format "${elementName}" expecting ":namespace:name"` - ); - } - return [ - elementName.slice(1, colonIndex), - elementName.slice(colonIndex + 1), - ]; + if (elementName[0] != ':') { + return [null, elementName]; + } + const colonIndex = elementName.indexOf(':', 1); + if (colonIndex === -1) { + throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`); + } + return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)]; } // `` tags work the same regardless the namespace function isNgContainer(tagName) { - return splitNsName(tagName)[1] === 'ng-container'; + return splitNsName(tagName)[1] === 'ng-container'; } // `` tags work the same regardless the namespace function isNgContent(tagName) { - return splitNsName(tagName)[1] === 'ng-content'; + return splitNsName(tagName)[1] === 'ng-content'; } // `` tags work the same regardless the namespace function isNgTemplate(tagName) { - return splitNsName(tagName)[1] === 'ng-template'; + return splitNsName(tagName)[1] === 'ng-template'; } function getNsPrefix(fullName) { - return fullName === null ? null : splitNsName(fullName)[0]; + return fullName === null ? null : splitNsName(fullName)[0]; } function mergeNsAndName(prefix, localName) { - return prefix ? `:${prefix}:${localName}` : localName; + return prefix ? `:${prefix}:${localName}` : localName; } /** @@ -71,195 +60,106 @@ * found in the LICENSE file at https://angular.io/license */ class HtmlTagDefinition { - constructor({ - closedByChildren, - implicitNamespacePrefix, - contentType = exports.TagContentType.PARSABLE_DATA, - closedByParent = false, - isVoid = false, - ignoreFirstLf = false, - preventNamespaceInheritance = false, - } = {}) { - this.closedByChildren = {}; - this.closedByParent = false; - this.canSelfClose = false; - if (closedByChildren && closedByChildren.length > 0) { - closedByChildren.forEach( - (tagName) => (this.closedByChildren[tagName] = true) - ); - } - this.isVoid = isVoid; - this.closedByParent = closedByParent || isVoid; - this.implicitNamespacePrefix = implicitNamespacePrefix || null; - this.contentType = contentType; - this.ignoreFirstLf = ignoreFirstLf; - this.preventNamespaceInheritance = preventNamespaceInheritance; - } - isClosedByChild(name) { - return this.isVoid || name.toLowerCase() in this.closedByChildren; - } - getContentType(prefix) { - if (typeof this.contentType === 'object') { - const overrideType = - prefix === undefined ? undefined : this.contentType[prefix]; - return overrideType !== null && overrideType !== void 0 - ? overrideType - : this.contentType.default; - } - return this.contentType; - } + constructor({ closedByChildren, implicitNamespacePrefix, contentType = exports.TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, ignoreFirstLf = false, preventNamespaceInheritance = false } = {}) { + this.closedByChildren = {}; + this.closedByParent = false; + this.canSelfClose = false; + if (closedByChildren && closedByChildren.length > 0) { + closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true); + } + this.isVoid = isVoid; + this.closedByParent = closedByParent || isVoid; + this.implicitNamespacePrefix = implicitNamespacePrefix || null; + this.contentType = contentType; + this.ignoreFirstLf = ignoreFirstLf; + this.preventNamespaceInheritance = preventNamespaceInheritance; + } + isClosedByChild(name) { + return this.isVoid || name.toLowerCase() in this.closedByChildren; + } + getContentType(prefix) { + if (typeof this.contentType === 'object') { + const overrideType = prefix === undefined ? undefined : this.contentType[prefix]; + return overrideType !== null && overrideType !== void 0 ? overrideType : this.contentType.default; + } + return this.contentType; + } } let _DEFAULT_TAG_DEFINITION; // see https://www.w3.org/TR/html51/syntax.html#optional-tags // This implementation does not fully conform to the HTML5 spec. let TAG_DEFINITIONS; function getHtmlTagDefinition(tagName) { - var _a, _b; - if (!TAG_DEFINITIONS) { - _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition(); - TAG_DEFINITIONS = { - base: new HtmlTagDefinition({ isVoid: true }), - meta: new HtmlTagDefinition({ isVoid: true }), - area: new HtmlTagDefinition({ isVoid: true }), - embed: new HtmlTagDefinition({ isVoid: true }), - link: new HtmlTagDefinition({ isVoid: true }), - img: new HtmlTagDefinition({ isVoid: true }), - input: new HtmlTagDefinition({ isVoid: true }), - param: new HtmlTagDefinition({ isVoid: true }), - hr: new HtmlTagDefinition({ isVoid: true }), - br: new HtmlTagDefinition({ isVoid: true }), - source: new HtmlTagDefinition({ isVoid: true }), - track: new HtmlTagDefinition({ isVoid: true }), - wbr: new HtmlTagDefinition({ isVoid: true }), - p: new HtmlTagDefinition({ - closedByChildren: [ - 'address', - 'article', - 'aside', - 'blockquote', - 'div', - 'dl', - 'fieldset', - 'footer', - 'form', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'header', - 'hgroup', - 'hr', - 'main', - 'nav', - 'ol', - 'p', - 'pre', - 'section', - 'table', - 'ul', - ], - closedByParent: true, - }), - thead: new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'] }), - tbody: new HtmlTagDefinition({ - closedByChildren: ['tbody', 'tfoot'], - closedByParent: true, - }), - tfoot: new HtmlTagDefinition({ - closedByChildren: ['tbody'], - closedByParent: true, - }), - tr: new HtmlTagDefinition({ - closedByChildren: ['tr'], - closedByParent: true, - }), - td: new HtmlTagDefinition({ - closedByChildren: ['td', 'th'], - closedByParent: true, - }), - th: new HtmlTagDefinition({ - closedByChildren: ['td', 'th'], - closedByParent: true, - }), - col: new HtmlTagDefinition({ isVoid: true }), - svg: new HtmlTagDefinition({ implicitNamespacePrefix: 'svg' }), - foreignObject: new HtmlTagDefinition({ - // Usually the implicit namespace here would be redundant since it will be inherited from - // the parent `svg`, but we have to do it for `foreignObject`, because the way the parser - // works is that the parent node of an end tag is its own start tag which means that - // the `preventNamespaceInheritance` on `foreignObject` would have it default to the - // implicit namespace which is `html`, unless specified otherwise. - implicitNamespacePrefix: 'svg', - // We want to prevent children of foreignObject from inheriting its namespace, because - // the point of the element is to allow nodes from other namespaces to be inserted. - preventNamespaceInheritance: true, - }), - math: new HtmlTagDefinition({ implicitNamespacePrefix: 'math' }), - li: new HtmlTagDefinition({ - closedByChildren: ['li'], - closedByParent: true, - }), - dt: new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'] }), - dd: new HtmlTagDefinition({ - closedByChildren: ['dt', 'dd'], - closedByParent: true, - }), - rb: new HtmlTagDefinition({ - closedByChildren: ['rb', 'rt', 'rtc', 'rp'], - closedByParent: true, - }), - rt: new HtmlTagDefinition({ - closedByChildren: ['rb', 'rt', 'rtc', 'rp'], - closedByParent: true, - }), - rtc: new HtmlTagDefinition({ - closedByChildren: ['rb', 'rtc', 'rp'], - closedByParent: true, - }), - rp: new HtmlTagDefinition({ - closedByChildren: ['rb', 'rt', 'rtc', 'rp'], - closedByParent: true, - }), - optgroup: new HtmlTagDefinition({ - closedByChildren: ['optgroup'], - closedByParent: true, - }), - option: new HtmlTagDefinition({ - closedByChildren: ['option', 'optgroup'], - closedByParent: true, - }), - pre: new HtmlTagDefinition({ ignoreFirstLf: true }), - listing: new HtmlTagDefinition({ ignoreFirstLf: true }), - style: new HtmlTagDefinition({ - contentType: exports.TagContentType.RAW_TEXT, - }), - script: new HtmlTagDefinition({ - contentType: exports.TagContentType.RAW_TEXT, - }), - title: new HtmlTagDefinition({ - // The browser supports two separate `title` tags which have to use - // a different content type: `HTMLTitleElement` and `SVGTitleElement` - contentType: { - default: exports.TagContentType.ESCAPABLE_RAW_TEXT, - svg: exports.TagContentType.PARSABLE_DATA, - }, - }), - textarea: new HtmlTagDefinition({ - contentType: exports.TagContentType.ESCAPABLE_RAW_TEXT, - ignoreFirstLf: true, - }), - }; - } - // We have to make both a case-sensitive and a case-insensitive lookup, because - // HTML tag names are case insensitive, whereas some SVG tags are case sensitive. - return (_b = - (_a = TAG_DEFINITIONS[tagName]) !== null && _a !== void 0 - ? _a - : TAG_DEFINITIONS[tagName.toLowerCase()]) !== null && _b !== void 0 - ? _b - : _DEFAULT_TAG_DEFINITION; + var _a, _b; + if (!TAG_DEFINITIONS) { + _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition(); + TAG_DEFINITIONS = { + 'base': new HtmlTagDefinition({ isVoid: true }), + 'meta': new HtmlTagDefinition({ isVoid: true }), + 'area': new HtmlTagDefinition({ isVoid: true }), + 'embed': new HtmlTagDefinition({ isVoid: true }), + 'link': new HtmlTagDefinition({ isVoid: true }), + 'img': new HtmlTagDefinition({ isVoid: true }), + 'input': new HtmlTagDefinition({ isVoid: true }), + 'param': new HtmlTagDefinition({ isVoid: true }), + 'hr': new HtmlTagDefinition({ isVoid: true }), + 'br': new HtmlTagDefinition({ isVoid: true }), + 'source': new HtmlTagDefinition({ isVoid: true }), + 'track': new HtmlTagDefinition({ isVoid: true }), + 'wbr': new HtmlTagDefinition({ isVoid: true }), + 'p': new HtmlTagDefinition({ + closedByChildren: [ + 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset', + 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', + 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul' + ], + closedByParent: true + }), + 'thead': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'] }), + 'tbody': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'], closedByParent: true }), + 'tfoot': new HtmlTagDefinition({ closedByChildren: ['tbody'], closedByParent: true }), + 'tr': new HtmlTagDefinition({ closedByChildren: ['tr'], closedByParent: true }), + 'td': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }), + 'th': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }), + 'col': new HtmlTagDefinition({ isVoid: true }), + 'svg': new HtmlTagDefinition({ implicitNamespacePrefix: 'svg' }), + 'foreignObject': new HtmlTagDefinition({ + // Usually the implicit namespace here would be redundant since it will be inherited from + // the parent `svg`, but we have to do it for `foreignObject`, because the way the parser + // works is that the parent node of an end tag is its own start tag which means that + // the `preventNamespaceInheritance` on `foreignObject` would have it default to the + // implicit namespace which is `html`, unless specified otherwise. + implicitNamespacePrefix: 'svg', + // We want to prevent children of foreignObject from inheriting its namespace, because + // the point of the element is to allow nodes from other namespaces to be inserted. + preventNamespaceInheritance: true, + }), + 'math': new HtmlTagDefinition({ implicitNamespacePrefix: 'math' }), + 'li': new HtmlTagDefinition({ closedByChildren: ['li'], closedByParent: true }), + 'dt': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'] }), + 'dd': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'], closedByParent: true }), + 'rb': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }), + 'rt': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }), + 'rtc': new HtmlTagDefinition({ closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true }), + 'rp': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }), + 'optgroup': new HtmlTagDefinition({ closedByChildren: ['optgroup'], closedByParent: true }), + 'option': new HtmlTagDefinition({ closedByChildren: ['option', 'optgroup'], closedByParent: true }), + 'pre': new HtmlTagDefinition({ ignoreFirstLf: true }), + 'listing': new HtmlTagDefinition({ ignoreFirstLf: true }), + 'style': new HtmlTagDefinition({ contentType: exports.TagContentType.RAW_TEXT }), + 'script': new HtmlTagDefinition({ contentType: exports.TagContentType.RAW_TEXT }), + 'title': new HtmlTagDefinition({ + // The browser supports two separate `title` tags which have to use + // a different content type: `HTMLTitleElement` and `SVGTitleElement` + contentType: { default: exports.TagContentType.ESCAPABLE_RAW_TEXT, svg: exports.TagContentType.PARSABLE_DATA } + }), + 'textarea': new HtmlTagDefinition({ contentType: exports.TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true }), + }; + } + // We have to make both a case-sensitive and a case-insensitive lookup, because + // HTML tag names are case insensitive, whereas some SVG tags are case sensitive. + return (_b = (_a = TAG_DEFINITIONS[tagName]) !== null && _a !== void 0 ? _a : TAG_DEFINITIONS[tagName.toLowerCase()]) !== null && _b !== void 0 ? _b : _DEFAULT_TAG_DEFINITION; } /** @@ -269,475 +169,406 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ - const _SELECTOR_REGEXP = new RegExp( - '(\\:not\\()|' + // 1: ":not(" + const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' + // 1: ":not(" '(([\\.\\#]?)[-\\w]+)|' + // 2: "tag"; 3: "."/"#"; // "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range // 4: attribute; 5: attribute_string; 6: attribute_value - '(?:\\[([-.\\w*\\\\$]+)(?:=(["\']?)([^\\]"\']*)\\5)?\\])|' + // "[name]", "[name=value]", + '(?:\\[([-.\\w*\\\\$]+)(?:=([\"\']?)([^\\]\"\']*)\\5)?\\])|' + // "[name]", "[name=value]", // "[name="value"]", // "[name='value']" '(\\))|' + // 7: ")" '(\\s*,\\s*)', // 8: "," - 'g' - ); + 'g'); /** * A css selector contains an element name, * css classes and attribute/value pairs with the purpose * of selecting subsets out of them. */ class CssSelector { - constructor() { - this.element = null; - this.classNames = []; + constructor() { + this.element = null; + this.classNames = []; + /** + * The selectors are encoded in pairs where: + * - even locations are attribute names + * - odd locations are attribute values. + * + * Example: + * Selector: `[key1=value1][key2]` would parse to: + * ``` + * ['key1', 'value1', 'key2', ''] + * ``` + */ + this.attrs = []; + this.notSelectors = []; + } + static parse(selector) { + const results = []; + const _addResult = (res, cssSel) => { + if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 && + cssSel.attrs.length == 0) { + cssSel.element = '*'; + } + res.push(cssSel); + }; + let cssSelector = new CssSelector(); + let match; + let current = cssSelector; + let inNot = false; + _SELECTOR_REGEXP.lastIndex = 0; + while (match = _SELECTOR_REGEXP.exec(selector)) { + if (match[1 /* SelectorRegexp.NOT */]) { + if (inNot) { + throw new Error('Nesting :not in a selector is not allowed'); + } + inNot = true; + current = new CssSelector(); + cssSelector.notSelectors.push(current); + } + const tag = match[2 /* SelectorRegexp.TAG */]; + if (tag) { + const prefix = match[3 /* SelectorRegexp.PREFIX */]; + if (prefix === '#') { + // #hash + current.addAttribute('id', tag.slice(1)); + } + else if (prefix === '.') { + // Class + current.addClassName(tag.slice(1)); + } + else { + // Element + current.setElement(tag); + } + } + const attribute = match[4 /* SelectorRegexp.ATTRIBUTE */]; + if (attribute) { + current.addAttribute(current.unescapeAttribute(attribute), match[6 /* SelectorRegexp.ATTRIBUTE_VALUE */]); + } + if (match[7 /* SelectorRegexp.NOT_END */]) { + inNot = false; + current = cssSelector; + } + if (match[8 /* SelectorRegexp.SEPARATOR */]) { + if (inNot) { + throw new Error('Multiple selectors in :not are not supported'); + } + _addResult(results, cssSelector); + cssSelector = current = new CssSelector(); + } + } + _addResult(results, cssSelector); + return results; + } /** - * The selectors are encoded in pairs where: - * - even locations are attribute names - * - odd locations are attribute values. + * Unescape `\$` sequences from the CSS attribute selector. * - * Example: - * Selector: `[key1=value1][key2]` would parse to: - * ``` - * ['key1', 'value1', 'key2', ''] - * ``` + * This is needed because `$` can have a special meaning in CSS selectors, + * but we might want to match an attribute that contains `$`. + * [MDN web link for more + * info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). + * @param attr the attribute to unescape. + * @returns the unescaped string. */ - this.attrs = []; - this.notSelectors = []; - } - static parse(selector) { - const results = []; - const _addResult = (res, cssSel) => { - if ( - cssSel.notSelectors.length > 0 && - !cssSel.element && - cssSel.classNames.length == 0 && - cssSel.attrs.length == 0 - ) { - cssSel.element = '*'; - } - res.push(cssSel); - }; - let cssSelector = new CssSelector(); - let match; - let current = cssSelector; - let inNot = false; - _SELECTOR_REGEXP.lastIndex = 0; - while ((match = _SELECTOR_REGEXP.exec(selector))) { - if (match[1 /* SelectorRegexp.NOT */]) { - if (inNot) { - throw new Error('Nesting :not in a selector is not allowed'); - } - inNot = true; - current = new CssSelector(); - cssSelector.notSelectors.push(current); - } - const tag = match[2 /* SelectorRegexp.TAG */]; - if (tag) { - const prefix = match[3 /* SelectorRegexp.PREFIX */]; - if (prefix === '#') { - // #hash - current.addAttribute('id', tag.slice(1)); - } else if (prefix === '.') { - // Class - current.addClassName(tag.slice(1)); - } else { - // Element - current.setElement(tag); + unescapeAttribute(attr) { + let result = ''; + let escaping = false; + for (let i = 0; i < attr.length; i++) { + const char = attr.charAt(i); + if (char === '\\') { + escaping = true; + continue; + } + if (char === '$' && !escaping) { + throw new Error(`Error in attribute selector "${attr}". ` + + `Unescaped "$" is not supported. Please escape with "\\$".`); + } + escaping = false; + result += char; } - } - const attribute = match[4 /* SelectorRegexp.ATTRIBUTE */]; - if (attribute) { - current.addAttribute( - current.unescapeAttribute(attribute), - match[6 /* SelectorRegexp.ATTRIBUTE_VALUE */] - ); - } - if (match[7 /* SelectorRegexp.NOT_END */]) { - inNot = false; - current = cssSelector; - } - if (match[8 /* SelectorRegexp.SEPARATOR */]) { - if (inNot) { - throw new Error('Multiple selectors in :not are not supported'); + return result; + } + /** + * Escape `$` sequences from the CSS attribute selector. + * + * This is needed because `$` can have a special meaning in CSS selectors, + * with this method we are escaping `$` with `\$'. + * [MDN web link for more + * info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). + * @param attr the attribute to escape. + * @returns the escaped string. + */ + escapeAttribute(attr) { + return attr.replace(/\\/g, '\\\\').replace(/\$/g, '\\$'); + } + isElementSelector() { + return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 && + this.notSelectors.length === 0; + } + hasElementSelector() { + return !!this.element; + } + setElement(element = null) { + this.element = element; + } + /** Gets a template string for an element that matches the selector. */ + getMatchingElementTemplate() { + const tagName = this.element || 'div'; + const classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : ''; + let attrs = ''; + for (let i = 0; i < this.attrs.length; i += 2) { + const attrName = this.attrs[i]; + const attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : ''; + attrs += ` ${attrName}${attrValue}`; } - _addResult(results, cssSelector); - cssSelector = current = new CssSelector(); - } + return getHtmlTagDefinition(tagName).isVoid ? `<${tagName}${classAttr}${attrs}/>` : + `<${tagName}${classAttr}${attrs}>`; } - _addResult(results, cssSelector); - return results; - } - /** - * Unescape `\$` sequences from the CSS attribute selector. - * - * This is needed because `$` can have a special meaning in CSS selectors, - * but we might want to match an attribute that contains `$`. - * [MDN web link for more - * info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). - * @param attr the attribute to unescape. - * @returns the unescaped string. - */ - unescapeAttribute(attr) { - let result = ''; - let escaping = false; - for (let i = 0; i < attr.length; i++) { - const char = attr.charAt(i); - if (char === '\\') { - escaping = true; - continue; - } - if (char === '$' && !escaping) { - throw new Error( - `Error in attribute selector "${attr}". ` + - `Unescaped "$" is not supported. Please escape with "\\$".` - ); - } - escaping = false; - result += char; + getAttrs() { + const result = []; + if (this.classNames.length > 0) { + result.push('class', this.classNames.join(' ')); + } + return result.concat(this.attrs); } - return result; - } - /** - * Escape `$` sequences from the CSS attribute selector. - * - * This is needed because `$` can have a special meaning in CSS selectors, - * with this method we are escaping `$` with `\$'. - * [MDN web link for more - * info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). - * @param attr the attribute to escape. - * @returns the escaped string. - */ - escapeAttribute(attr) { - return attr.replace(/\\/g, '\\\\').replace(/\$/g, '\\$'); - } - isElementSelector() { - return ( - this.hasElementSelector() && - this.classNames.length == 0 && - this.attrs.length == 0 && - this.notSelectors.length === 0 - ); - } - hasElementSelector() { - return !!this.element; - } - setElement(element = null) { - this.element = element; - } - /** Gets a template string for an element that matches the selector. */ - getMatchingElementTemplate() { - const tagName = this.element || 'div'; - const classAttr = - this.classNames.length > 0 - ? ` class="${this.classNames.join(' ')}"` - : ''; - let attrs = ''; - for (let i = 0; i < this.attrs.length; i += 2) { - const attrName = this.attrs[i]; - const attrValue = - this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : ''; - attrs += ` ${attrName}${attrValue}`; - } - return getHtmlTagDefinition(tagName).isVoid - ? `<${tagName}${classAttr}${attrs}/>` - : `<${tagName}${classAttr}${attrs}>`; - } - getAttrs() { - const result = []; - if (this.classNames.length > 0) { - result.push('class', this.classNames.join(' ')); - } - return result.concat(this.attrs); - } - addAttribute(name, value = '') { - this.attrs.push(name, (value && value.toLowerCase()) || ''); - } - addClassName(name) { - this.classNames.push(name.toLowerCase()); - } - toString() { - let res = this.element || ''; - if (this.classNames) { - this.classNames.forEach((klass) => (res += `.${klass}`)); - } - if (this.attrs) { - for (let i = 0; i < this.attrs.length; i += 2) { - const name = this.escapeAttribute(this.attrs[i]); - const value = this.attrs[i + 1]; - res += `[${name}${value ? '=' + value : ''}]`; - } + addAttribute(name, value = '') { + this.attrs.push(name, value && value.toLowerCase() || ''); + } + addClassName(name) { + this.classNames.push(name.toLowerCase()); + } + toString() { + let res = this.element || ''; + if (this.classNames) { + this.classNames.forEach(klass => res += `.${klass}`); + } + if (this.attrs) { + for (let i = 0; i < this.attrs.length; i += 2) { + const name = this.escapeAttribute(this.attrs[i]); + const value = this.attrs[i + 1]; + res += `[${name}${value ? '=' + value : ''}]`; + } + } + this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`); + return res; } - this.notSelectors.forEach( - (notSelector) => (res += `:not(${notSelector})`) - ); - return res; - } } /** * Reads a list of CssSelectors and allows to calculate which ones * are contained in a given CssSelector. */ class SelectorMatcher { - constructor() { - this._elementMap = new Map(); - this._elementPartialMap = new Map(); - this._classMap = new Map(); - this._classPartialMap = new Map(); - this._attrValueMap = new Map(); - this._attrValuePartialMap = new Map(); - this._listContexts = []; - } - static createNotMatcher(notSelectors) { - const notMatcher = new SelectorMatcher(); - notMatcher.addSelectables(notSelectors, null); - return notMatcher; - } - addSelectables(cssSelectors, callbackCtxt) { - let listContext = null; - if (cssSelectors.length > 1) { - listContext = new SelectorListContext(cssSelectors); - this._listContexts.push(listContext); - } - for (let i = 0; i < cssSelectors.length; i++) { - this._addSelectable(cssSelectors[i], callbackCtxt, listContext); - } - } - /** - * Add an object that can be found later on by calling `match`. - * @param cssSelector A css selector - * @param callbackCtxt An opaque object that will be given to the callback of the `match` function - */ - _addSelectable(cssSelector, callbackCtxt, listContext) { - let matcher = this; - const element = cssSelector.element; - const classNames = cssSelector.classNames; - const attrs = cssSelector.attrs; - const selectable = new SelectorContext( - cssSelector, - callbackCtxt, - listContext - ); - if (element) { - const isTerminal = attrs.length === 0 && classNames.length === 0; - if (isTerminal) { - this._addTerminal(matcher._elementMap, element, selectable); - } else { - matcher = this._addPartial(matcher._elementPartialMap, element); - } + constructor() { + this._elementMap = new Map(); + this._elementPartialMap = new Map(); + this._classMap = new Map(); + this._classPartialMap = new Map(); + this._attrValueMap = new Map(); + this._attrValuePartialMap = new Map(); + this._listContexts = []; + } + static createNotMatcher(notSelectors) { + const notMatcher = new SelectorMatcher(); + notMatcher.addSelectables(notSelectors, null); + return notMatcher; + } + addSelectables(cssSelectors, callbackCtxt) { + let listContext = null; + if (cssSelectors.length > 1) { + listContext = new SelectorListContext(cssSelectors); + this._listContexts.push(listContext); + } + for (let i = 0; i < cssSelectors.length; i++) { + this._addSelectable(cssSelectors[i], callbackCtxt, listContext); + } } - if (classNames) { - for (let i = 0; i < classNames.length; i++) { - const isTerminal = attrs.length === 0 && i === classNames.length - 1; - const className = classNames[i]; - if (isTerminal) { - this._addTerminal(matcher._classMap, className, selectable); - } else { - matcher = this._addPartial(matcher._classPartialMap, className); + /** + * Add an object that can be found later on by calling `match`. + * @param cssSelector A css selector + * @param callbackCtxt An opaque object that will be given to the callback of the `match` function + */ + _addSelectable(cssSelector, callbackCtxt, listContext) { + let matcher = this; + const element = cssSelector.element; + const classNames = cssSelector.classNames; + const attrs = cssSelector.attrs; + const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext); + if (element) { + const isTerminal = attrs.length === 0 && classNames.length === 0; + if (isTerminal) { + this._addTerminal(matcher._elementMap, element, selectable); + } + else { + matcher = this._addPartial(matcher._elementPartialMap, element); + } + } + if (classNames) { + for (let i = 0; i < classNames.length; i++) { + const isTerminal = attrs.length === 0 && i === classNames.length - 1; + const className = classNames[i]; + if (isTerminal) { + this._addTerminal(matcher._classMap, className, selectable); + } + else { + matcher = this._addPartial(matcher._classPartialMap, className); + } + } + } + if (attrs) { + for (let i = 0; i < attrs.length; i += 2) { + const isTerminal = i === attrs.length - 2; + const name = attrs[i]; + const value = attrs[i + 1]; + if (isTerminal) { + const terminalMap = matcher._attrValueMap; + let terminalValuesMap = terminalMap.get(name); + if (!terminalValuesMap) { + terminalValuesMap = new Map(); + terminalMap.set(name, terminalValuesMap); + } + this._addTerminal(terminalValuesMap, value, selectable); + } + else { + const partialMap = matcher._attrValuePartialMap; + let partialValuesMap = partialMap.get(name); + if (!partialValuesMap) { + partialValuesMap = new Map(); + partialMap.set(name, partialValuesMap); + } + matcher = this._addPartial(partialValuesMap, value); + } + } } - } } - if (attrs) { - for (let i = 0; i < attrs.length; i += 2) { - const isTerminal = i === attrs.length - 2; - const name = attrs[i]; - const value = attrs[i + 1]; - if (isTerminal) { - const terminalMap = matcher._attrValueMap; - let terminalValuesMap = terminalMap.get(name); - if (!terminalValuesMap) { - terminalValuesMap = new Map(); - terminalMap.set(name, terminalValuesMap); - } - this._addTerminal(terminalValuesMap, value, selectable); - } else { - const partialMap = matcher._attrValuePartialMap; - let partialValuesMap = partialMap.get(name); - if (!partialValuesMap) { - partialValuesMap = new Map(); - partialMap.set(name, partialValuesMap); - } - matcher = this._addPartial(partialValuesMap, value); + _addTerminal(map, name, selectable) { + let terminalList = map.get(name); + if (!terminalList) { + terminalList = []; + map.set(name, terminalList); } - } + terminalList.push(selectable); } - } - _addTerminal(map, name, selectable) { - let terminalList = map.get(name); - if (!terminalList) { - terminalList = []; - map.set(name, terminalList); - } - terminalList.push(selectable); - } - _addPartial(map, name) { - let matcher = map.get(name); - if (!matcher) { - matcher = new SelectorMatcher(); - map.set(name, matcher); - } - return matcher; - } - /** - * Find the objects that have been added via `addSelectable` - * whose css selector is contained in the given css selector. - * @param cssSelector A css selector - * @param matchedCallback This callback will be called with the object handed into `addSelectable` - * @return boolean true if a match was found - */ - match(cssSelector, matchedCallback) { - let result = false; - const element = cssSelector.element; - const classNames = cssSelector.classNames; - const attrs = cssSelector.attrs; - for (let i = 0; i < this._listContexts.length; i++) { - this._listContexts[i].alreadyMatched = false; - } - result = - this._matchTerminal( - this._elementMap, - element, - cssSelector, - matchedCallback - ) || result; - result = - this._matchPartial( - this._elementPartialMap, - element, - cssSelector, - matchedCallback - ) || result; - if (classNames) { - for (let i = 0; i < classNames.length; i++) { - const className = classNames[i]; - result = - this._matchTerminal( - this._classMap, - className, - cssSelector, - matchedCallback - ) || result; - result = - this._matchPartial( - this._classPartialMap, - className, - cssSelector, - matchedCallback - ) || result; - } + _addPartial(map, name) { + let matcher = map.get(name); + if (!matcher) { + matcher = new SelectorMatcher(); + map.set(name, matcher); + } + return matcher; } - if (attrs) { - for (let i = 0; i < attrs.length; i += 2) { - const name = attrs[i]; - const value = attrs[i + 1]; - const terminalValuesMap = this._attrValueMap.get(name); - if (value) { - result = - this._matchTerminal( - terminalValuesMap, - '', - cssSelector, - matchedCallback - ) || result; - } - result = - this._matchTerminal( - terminalValuesMap, - value, - cssSelector, - matchedCallback - ) || result; - const partialValuesMap = this._attrValuePartialMap.get(name); - if (value) { - result = - this._matchPartial( - partialValuesMap, - '', - cssSelector, - matchedCallback - ) || result; - } - result = - this._matchPartial( - partialValuesMap, - value, - cssSelector, - matchedCallback - ) || result; - } + /** + * Find the objects that have been added via `addSelectable` + * whose css selector is contained in the given css selector. + * @param cssSelector A css selector + * @param matchedCallback This callback will be called with the object handed into `addSelectable` + * @return boolean true if a match was found + */ + match(cssSelector, matchedCallback) { + let result = false; + const element = cssSelector.element; + const classNames = cssSelector.classNames; + const attrs = cssSelector.attrs; + for (let i = 0; i < this._listContexts.length; i++) { + this._listContexts[i].alreadyMatched = false; + } + result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result; + result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || + result; + if (classNames) { + for (let i = 0; i < classNames.length; i++) { + const className = classNames[i]; + result = + this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result; + result = + this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) || + result; + } + } + if (attrs) { + for (let i = 0; i < attrs.length; i += 2) { + const name = attrs[i]; + const value = attrs[i + 1]; + const terminalValuesMap = this._attrValueMap.get(name); + if (value) { + result = + this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result; + } + result = + this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result; + const partialValuesMap = this._attrValuePartialMap.get(name); + if (value) { + result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result; + } + result = + this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result; + } + } + return result; } - return result; - } - /** @internal */ - _matchTerminal(map, name, cssSelector, matchedCallback) { - if (!map || typeof name !== 'string') { - return false; - } - let selectables = map.get(name) || []; - const starSelectables = map.get('*'); - if (starSelectables) { - selectables = selectables.concat(starSelectables); - } - if (selectables.length === 0) { - return false; - } - let selectable; - let result = false; - for (let i = 0; i < selectables.length; i++) { - selectable = selectables[i]; - result = selectable.finalize(cssSelector, matchedCallback) || result; + /** @internal */ + _matchTerminal(map, name, cssSelector, matchedCallback) { + if (!map || typeof name !== 'string') { + return false; + } + let selectables = map.get(name) || []; + const starSelectables = map.get('*'); + if (starSelectables) { + selectables = selectables.concat(starSelectables); + } + if (selectables.length === 0) { + return false; + } + let selectable; + let result = false; + for (let i = 0; i < selectables.length; i++) { + selectable = selectables[i]; + result = selectable.finalize(cssSelector, matchedCallback) || result; + } + return result; + } + /** @internal */ + _matchPartial(map, name, cssSelector, matchedCallback) { + if (!map || typeof name !== 'string') { + return false; + } + const nestedSelector = map.get(name); + if (!nestedSelector) { + return false; + } + // TODO(perf): get rid of recursion and measure again + // TODO(perf): don't pass the whole selector into the recursion, + // but only the not processed parts + return nestedSelector.match(cssSelector, matchedCallback); } - return result; - } - /** @internal */ - _matchPartial(map, name, cssSelector, matchedCallback) { - if (!map || typeof name !== 'string') { - return false; - } - const nestedSelector = map.get(name); - if (!nestedSelector) { - return false; - } - // TODO(perf): get rid of recursion and measure again - // TODO(perf): don't pass the whole selector into the recursion, - // but only the not processed parts - return nestedSelector.match(cssSelector, matchedCallback); - } } class SelectorListContext { - constructor(selectors) { - this.selectors = selectors; - this.alreadyMatched = false; - } + constructor(selectors) { + this.selectors = selectors; + this.alreadyMatched = false; + } } // Store context to pass back selector and context when a selector is matched class SelectorContext { - constructor(selector, cbContext, listContext) { - this.selector = selector; - this.cbContext = cbContext; - this.listContext = listContext; - this.notSelectors = selector.notSelectors; - } - finalize(cssSelector, callback) { - let result = true; - if ( - this.notSelectors.length > 0 && - (!this.listContext || !this.listContext.alreadyMatched) - ) { - const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors); - result = !notMatcher.match(cssSelector, null); - } - if ( - result && - callback && - (!this.listContext || !this.listContext.alreadyMatched) - ) { - if (this.listContext) { - this.listContext.alreadyMatched = true; - } - callback(this.selector, this.cbContext); + constructor(selector, cbContext, listContext) { + this.selector = selector; + this.cbContext = cbContext; + this.listContext = listContext; + this.notSelectors = selector.notSelectors; + } + finalize(cssSelector, callback) { + let result = true; + if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) { + const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors); + result = !notMatcher.match(cssSelector, null); + } + if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) { + if (this.listContext) { + this.listContext.alreadyMatched = true; + } + callback(this.selector, this.cbContext); + } + return result; } - return result; - } } /** @@ -752,113 +583,85 @@ const emitDistinctChangesOnlyDefaultValue = true; exports.ViewEncapsulation = void 0; (function (ViewEncapsulation) { - ViewEncapsulation[(ViewEncapsulation['Emulated'] = 0)] = 'Emulated'; - // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. - ViewEncapsulation[(ViewEncapsulation['None'] = 2)] = 'None'; - ViewEncapsulation[(ViewEncapsulation['ShadowDom'] = 3)] = 'ShadowDom'; + ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated"; + // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. + ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None"; + ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom"; })(exports.ViewEncapsulation || (exports.ViewEncapsulation = {})); exports.ChangeDetectionStrategy = void 0; (function (ChangeDetectionStrategy) { - ChangeDetectionStrategy[(ChangeDetectionStrategy['OnPush'] = 0)] = 'OnPush'; - ChangeDetectionStrategy[(ChangeDetectionStrategy['Default'] = 1)] = - 'Default'; + ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush"; + ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default"; })(exports.ChangeDetectionStrategy || (exports.ChangeDetectionStrategy = {})); const CUSTOM_ELEMENTS_SCHEMA = { - name: 'custom-elements', + name: 'custom-elements' }; const NO_ERRORS_SCHEMA = { - name: 'no-errors-schema', + name: 'no-errors-schema' }; const Type$1 = Function; var SecurityContext; (function (SecurityContext) { - SecurityContext[(SecurityContext['NONE'] = 0)] = 'NONE'; - SecurityContext[(SecurityContext['HTML'] = 1)] = 'HTML'; - SecurityContext[(SecurityContext['STYLE'] = 2)] = 'STYLE'; - SecurityContext[(SecurityContext['SCRIPT'] = 3)] = 'SCRIPT'; - SecurityContext[(SecurityContext['URL'] = 4)] = 'URL'; - SecurityContext[(SecurityContext['RESOURCE_URL'] = 5)] = 'RESOURCE_URL'; + SecurityContext[SecurityContext["NONE"] = 0] = "NONE"; + SecurityContext[SecurityContext["HTML"] = 1] = "HTML"; + SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE"; + SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT"; + SecurityContext[SecurityContext["URL"] = 4] = "URL"; + SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL"; })(SecurityContext || (SecurityContext = {})); var MissingTranslationStrategy; (function (MissingTranslationStrategy) { - MissingTranslationStrategy[(MissingTranslationStrategy['Error'] = 0)] = - 'Error'; - MissingTranslationStrategy[(MissingTranslationStrategy['Warning'] = 1)] = - 'Warning'; - MissingTranslationStrategy[(MissingTranslationStrategy['Ignore'] = 2)] = - 'Ignore'; + MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error"; + MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning"; + MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore"; })(MissingTranslationStrategy || (MissingTranslationStrategy = {})); function parserSelectorToSimpleSelector(selector) { - const classes = - selector.classNames && selector.classNames.length - ? [8 /* SelectorFlags.CLASS */, ...selector.classNames] - : []; - const elementName = - selector.element && selector.element !== '*' ? selector.element : ''; - return [elementName, ...selector.attrs, ...classes]; + const classes = selector.classNames && selector.classNames.length ? + [8 /* SelectorFlags.CLASS */, ...selector.classNames] : + []; + const elementName = selector.element && selector.element !== '*' ? selector.element : ''; + return [elementName, ...selector.attrs, ...classes]; } function parserSelectorToNegativeSelector(selector) { - const classes = - selector.classNames && selector.classNames.length - ? [8 /* SelectorFlags.CLASS */, ...selector.classNames] - : []; - if (selector.element) { - return [ - 1 /* SelectorFlags.NOT */ | 4 /* SelectorFlags.ELEMENT */, - selector.element, - ...selector.attrs, - ...classes, - ]; - } else if (selector.attrs.length) { - return [ - 1 /* SelectorFlags.NOT */ | 2 /* SelectorFlags.ATTRIBUTE */, - ...selector.attrs, - ...classes, - ]; - } else { - return selector.classNames && selector.classNames.length - ? [ - 1 /* SelectorFlags.NOT */ | 8 /* SelectorFlags.CLASS */, - ...selector.classNames, - ] - : []; - } + const classes = selector.classNames && selector.classNames.length ? + [8 /* SelectorFlags.CLASS */, ...selector.classNames] : + []; + if (selector.element) { + return [ + 1 /* SelectorFlags.NOT */ | 4 /* SelectorFlags.ELEMENT */, selector.element, ...selector.attrs, ...classes + ]; + } + else if (selector.attrs.length) { + return [1 /* SelectorFlags.NOT */ | 2 /* SelectorFlags.ATTRIBUTE */, ...selector.attrs, ...classes]; + } + else { + return selector.classNames && selector.classNames.length ? + [1 /* SelectorFlags.NOT */ | 8 /* SelectorFlags.CLASS */, ...selector.classNames] : + []; + } } function parserSelectorToR3Selector(selector) { - const positive = parserSelectorToSimpleSelector(selector); - const negative = - selector.notSelectors && selector.notSelectors.length - ? selector.notSelectors.map((notSelector) => - parserSelectorToNegativeSelector(notSelector) - ) - : []; - return positive.concat(...negative); + const positive = parserSelectorToSimpleSelector(selector); + const negative = selector.notSelectors && selector.notSelectors.length ? + selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) : + []; + return positive.concat(...negative); } function parseSelectorToR3Selector(selector) { - return selector - ? CssSelector.parse(selector).map(parserSelectorToR3Selector) - : []; + return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : []; } - var core = /*#__PURE__*/ Object.freeze({ - __proto__: null, - emitDistinctChangesOnlyDefaultValue: emitDistinctChangesOnlyDefaultValue, - get ViewEncapsulation() { - return exports.ViewEncapsulation; - }, - get ChangeDetectionStrategy() { - return exports.ChangeDetectionStrategy; - }, - CUSTOM_ELEMENTS_SCHEMA: CUSTOM_ELEMENTS_SCHEMA, - NO_ERRORS_SCHEMA: NO_ERRORS_SCHEMA, - Type: Type$1, - get SecurityContext() { - return SecurityContext; - }, - get MissingTranslationStrategy() { - return MissingTranslationStrategy; - }, - parseSelectorToR3Selector: parseSelectorToR3Selector, + var core = /*#__PURE__*/Object.freeze({ + __proto__: null, + emitDistinctChangesOnlyDefaultValue: emitDistinctChangesOnlyDefaultValue, + get ViewEncapsulation () { return exports.ViewEncapsulation; }, + get ChangeDetectionStrategy () { return exports.ChangeDetectionStrategy; }, + CUSTOM_ELEMENTS_SCHEMA: CUSTOM_ELEMENTS_SCHEMA, + NO_ERRORS_SCHEMA: NO_ERRORS_SCHEMA, + Type: Type$1, + get SecurityContext () { return SecurityContext; }, + get MissingTranslationStrategy () { return MissingTranslationStrategy; }, + parseSelectorToR3Selector: parseSelectorToR3Selector }); /** @@ -870,123 +673,104 @@ */ const DASH_CASE_REGEXP = /-+([a-z0-9])/g; function dashCaseToCamelCase(input) { - return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase()); + return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase()); } function splitAtColon(input, defaultValues) { - return _splitAt(input, ':', defaultValues); + return _splitAt(input, ':', defaultValues); } function splitAtPeriod(input, defaultValues) { - return _splitAt(input, '.', defaultValues); + return _splitAt(input, '.', defaultValues); } function _splitAt(input, character, defaultValues) { - const characterIndex = input.indexOf(character); - if (characterIndex == -1) return defaultValues; - return [ - input.slice(0, characterIndex).trim(), - input.slice(characterIndex + 1).trim(), - ]; + const characterIndex = input.indexOf(character); + if (characterIndex == -1) + return defaultValues; + return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()]; } function noUndefined(val) { - return val === undefined ? null : val; + return val === undefined ? null : val; } function error(msg) { - throw new Error(`Internal Error: ${msg}`); + throw new Error(`Internal Error: ${msg}`); } function utf8Encode(str) { - let encoded = []; - for (let index = 0; index < str.length; index++) { - let codePoint = str.charCodeAt(index); - // decode surrogate - // see https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae - if ( - codePoint >= 0xd800 && - codePoint <= 0xdbff && - str.length > index + 1 - ) { - const low = str.charCodeAt(index + 1); - if (low >= 0xdc00 && low <= 0xdfff) { - index++; - codePoint = ((codePoint - 0xd800) << 10) + low - 0xdc00 + 0x10000; - } + let encoded = []; + for (let index = 0; index < str.length; index++) { + let codePoint = str.charCodeAt(index); + // decode surrogate + // see https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > (index + 1)) { + const low = str.charCodeAt(index + 1); + if (low >= 0xdc00 && low <= 0xdfff) { + index++; + codePoint = ((codePoint - 0xd800) << 10) + low - 0xdc00 + 0x10000; + } + } + if (codePoint <= 0x7f) { + encoded.push(codePoint); + } + else if (codePoint <= 0x7ff) { + encoded.push(((codePoint >> 6) & 0x1F) | 0xc0, (codePoint & 0x3f) | 0x80); + } + else if (codePoint <= 0xffff) { + encoded.push((codePoint >> 12) | 0xe0, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80); + } + else if (codePoint <= 0x1fffff) { + encoded.push(((codePoint >> 18) & 0x07) | 0xf0, ((codePoint >> 12) & 0x3f) | 0x80, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80); + } } - if (codePoint <= 0x7f) { - encoded.push(codePoint); - } else if (codePoint <= 0x7ff) { - encoded.push( - ((codePoint >> 6) & 0x1f) | 0xc0, - (codePoint & 0x3f) | 0x80 - ); - } else if (codePoint <= 0xffff) { - encoded.push( - (codePoint >> 12) | 0xe0, - ((codePoint >> 6) & 0x3f) | 0x80, - (codePoint & 0x3f) | 0x80 - ); - } else if (codePoint <= 0x1fffff) { - encoded.push( - ((codePoint >> 18) & 0x07) | 0xf0, - ((codePoint >> 12) & 0x3f) | 0x80, - ((codePoint >> 6) & 0x3f) | 0x80, - (codePoint & 0x3f) | 0x80 - ); - } - } - return encoded; + return encoded; } 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}`; - } - if (!token.toString) { - return 'object'; - } - // WARNING: do not try to `JSON.stringify(token)` here - // see https://github.com/angular/angular/issues/23440 - const res = token.toString(); - if (res == null) { - return '' + res; - } - const newLineIndex = res.indexOf('\n'); - return newLineIndex === -1 ? res : res.substring(0, newLineIndex); + 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}`; + } + if (!token.toString) { + return 'object'; + } + // WARNING: do not try to `JSON.stringify(token)` here + // see https://github.com/angular/angular/issues/23440 + const res = token.toString(); + if (res == null) { + return '' + res; + } + const newLineIndex = res.indexOf('\n'); + return newLineIndex === -1 ? res : res.substring(0, newLineIndex); } class Version { - constructor(full) { - this.full = full; - const splits = full.split('.'); - this.major = splits[0]; - this.minor = splits[1]; - this.patch = splits.slice(2).join('.'); - } + constructor(full) { + this.full = full; + const splits = full.split('.'); + this.major = splits[0]; + this.minor = splits[1]; + this.patch = splits.slice(2).join('.'); + } } // Check `global` first, because in Node tests both `global` and `window` may be defined and our // `_global` variable should point to the NodeJS `global` in that case. Note: Typeof/Instanceof // checks are considered side-effects in Terser. We explicitly mark this as side-effect free: // https://github.com/terser/terser/issues/250. - const _global = /* @__PURE__ */ (() => - (typeof global !== 'undefined' && global) || - (typeof window !== 'undefined' && window) || - (typeof self !== 'undefined' && - typeof WorkerGlobalScope !== 'undefined' && - self instanceof WorkerGlobalScope && - self))(); + const _global = ( /* @__PURE__ */(() => (typeof global !== 'undefined' && global) || (typeof window !== 'undefined' && window) || + (typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' && + self instanceof WorkerGlobalScope && self))()); function newArray(size, value) { - const list = []; - for (let i = 0; i < size; i++) { - list.push(value); - } - return list; + const list = []; + for (let i = 0; i < size; i++) { + list.push(value); + } + return list; } /** * Partitions a given array into 2 arrays, based on a boolean value returned by the condition @@ -997,12 +781,12 @@ * boolean value. */ function partitionArray(arr, conditionFn) { - const truthy = []; - const falsy = []; - for (const item of arr) { - (conditionFn(item) ? truthy : falsy).push(item); - } - return [truthy, falsy]; + const truthy = []; + const falsy = []; + for (const item of arr) { + (conditionFn(item) ? truthy : falsy).push(item); + } + return [truthy, falsy]; } /** @@ -1020,142 +804,143 @@ * to reduce memory pressure of allocation for the digits array. */ class BigInteger { - /** - * Creates a big integer using its individual digits in little endian storage. - */ - constructor(digits) { - this.digits = digits; - } - static zero() { - return new BigInteger([0]); - } - static one() { - return new BigInteger([1]); - } - /** - * Creates a clone of this instance. - */ - clone() { - return new BigInteger(this.digits.slice()); - } - /** - * Returns a new big integer with the sum of `this` and `other` as its value. This does not mutate - * `this` but instead returns a new instance, unlike `addToSelf`. - */ - add(other) { - const result = this.clone(); - result.addToSelf(other); - return result; - } - /** - * Adds `other` to the instance itself, thereby mutating its value. - */ - addToSelf(other) { - const maxNrOfDigits = Math.max(this.digits.length, other.digits.length); - let carry = 0; - for (let i = 0; i < maxNrOfDigits; i++) { - let digitSum = carry; - if (i < this.digits.length) { - digitSum += this.digits[i]; - } - if (i < other.digits.length) { - digitSum += other.digits[i]; - } - if (digitSum >= 10) { - this.digits[i] = digitSum - 10; - carry = 1; - } else { - this.digits[i] = digitSum; - carry = 0; - } + /** + * Creates a big integer using its individual digits in little endian storage. + */ + constructor(digits) { + this.digits = digits; } - // Apply a remaining carry if needed. - if (carry > 0) { - this.digits[maxNrOfDigits] = 1; + static zero() { + return new BigInteger([0]); } - } - /** - * Builds the decimal string representation of the big integer. As this is stored in - * little endian, the digits are concatenated in reverse order. - */ - toString() { - let res = ''; - for (let i = this.digits.length - 1; i >= 0; i--) { - res += this.digits[i]; + static one() { + return new BigInteger([1]); + } + /** + * Creates a clone of this instance. + */ + clone() { + return new BigInteger(this.digits.slice()); + } + /** + * Returns a new big integer with the sum of `this` and `other` as its value. This does not mutate + * `this` but instead returns a new instance, unlike `addToSelf`. + */ + add(other) { + const result = this.clone(); + result.addToSelf(other); + return result; + } + /** + * Adds `other` to the instance itself, thereby mutating its value. + */ + addToSelf(other) { + const maxNrOfDigits = Math.max(this.digits.length, other.digits.length); + let carry = 0; + for (let i = 0; i < maxNrOfDigits; i++) { + let digitSum = carry; + if (i < this.digits.length) { + digitSum += this.digits[i]; + } + if (i < other.digits.length) { + digitSum += other.digits[i]; + } + if (digitSum >= 10) { + this.digits[i] = digitSum - 10; + carry = 1; + } + else { + this.digits[i] = digitSum; + carry = 0; + } + } + // Apply a remaining carry if needed. + if (carry > 0) { + this.digits[maxNrOfDigits] = 1; + } + } + /** + * Builds the decimal string representation of the big integer. As this is stored in + * little endian, the digits are concatenated in reverse order. + */ + toString() { + let res = ''; + for (let i = this.digits.length - 1; i >= 0; i--) { + res += this.digits[i]; + } + return res; } - return res; - } } /** * Represents a big integer which is optimized for multiplication operations, as its power-of-twos * are memoized. See `multiplyBy()` for details on the multiplication algorithm. */ class BigIntForMultiplication { - constructor(value) { - this.powerOfTwos = [value]; - } - /** - * Returns the big integer itself. - */ - getValue() { - return this.powerOfTwos[0]; - } - /** - * Computes the value for `num * b`, where `num` is a JS number and `b` is a big integer. The - * value for `b` is represented by a storage model that is optimized for this computation. - * - * This operation is implemented in N(log2(num)) by continuous halving of the number, where the - * least-significant bit (LSB) is tested in each iteration. If the bit is set, the bit's index is - * used as exponent into the power-of-two multiplication of `b`. - * - * As an example, consider the multiplication num=42, b=1337. In binary 42 is 0b00101010 and the - * algorithm unrolls into the following iterations: - * - * Iteration | num | LSB | b * 2^iter | Add? | product - * -----------|------------|------|------------|------|-------- - * 0 | 0b00101010 | 0 | 1337 | No | 0 - * 1 | 0b00010101 | 1 | 2674 | Yes | 2674 - * 2 | 0b00001010 | 0 | 5348 | No | 2674 - * 3 | 0b00000101 | 1 | 10696 | Yes | 13370 - * 4 | 0b00000010 | 0 | 21392 | No | 13370 - * 5 | 0b00000001 | 1 | 42784 | Yes | 56154 - * 6 | 0b00000000 | 0 | 85568 | No | 56154 - * - * The computed product of 56154 is indeed the correct result. - * - * The `BigIntForMultiplication` representation for a big integer provides memoized access to the - * power-of-two values to reduce the workload in computing those values. - */ - multiplyBy(num) { - const product = BigInteger.zero(); - this.multiplyByAndAddTo(num, product); - return product; - } - /** - * See `multiplyBy()` for details. This function allows for the computed product to be added - * directly to the provided result big integer. - */ - multiplyByAndAddTo(num, result) { - for (let exponent = 0; num !== 0; num = num >>> 1, exponent++) { - if (num & 1) { - const value = this.getMultipliedByPowerOfTwo(exponent); - result.addToSelf(value); - } + constructor(value) { + this.powerOfTwos = [value]; + } + /** + * Returns the big integer itself. + */ + getValue() { + return this.powerOfTwos[0]; + } + /** + * Computes the value for `num * b`, where `num` is a JS number and `b` is a big integer. The + * value for `b` is represented by a storage model that is optimized for this computation. + * + * This operation is implemented in N(log2(num)) by continuous halving of the number, where the + * least-significant bit (LSB) is tested in each iteration. If the bit is set, the bit's index is + * used as exponent into the power-of-two multiplication of `b`. + * + * As an example, consider the multiplication num=42, b=1337. In binary 42 is 0b00101010 and the + * algorithm unrolls into the following iterations: + * + * Iteration | num | LSB | b * 2^iter | Add? | product + * -----------|------------|------|------------|------|-------- + * 0 | 0b00101010 | 0 | 1337 | No | 0 + * 1 | 0b00010101 | 1 | 2674 | Yes | 2674 + * 2 | 0b00001010 | 0 | 5348 | No | 2674 + * 3 | 0b00000101 | 1 | 10696 | Yes | 13370 + * 4 | 0b00000010 | 0 | 21392 | No | 13370 + * 5 | 0b00000001 | 1 | 42784 | Yes | 56154 + * 6 | 0b00000000 | 0 | 85568 | No | 56154 + * + * The computed product of 56154 is indeed the correct result. + * + * The `BigIntForMultiplication` representation for a big integer provides memoized access to the + * power-of-two values to reduce the workload in computing those values. + */ + multiplyBy(num) { + const product = BigInteger.zero(); + this.multiplyByAndAddTo(num, product); + return product; + } + /** + * See `multiplyBy()` for details. This function allows for the computed product to be added + * directly to the provided result big integer. + */ + multiplyByAndAddTo(num, result) { + for (let exponent = 0; num !== 0; num = num >>> 1, exponent++) { + if (num & 1) { + const value = this.getMultipliedByPowerOfTwo(exponent); + result.addToSelf(value); + } + } + } + /** + * Computes and memoizes the big integer value for `this.number * 2^exponent`. + */ + getMultipliedByPowerOfTwo(exponent) { + // Compute the powers up until the requested exponent, where each value is computed from its + // predecessor. This is simple as `this.number * 2^(exponent - 1)` only has to be doubled (i.e. + // added to itself) to reach `this.number * 2^exponent`. + for (let i = this.powerOfTwos.length; i <= exponent; i++) { + const previousPower = this.powerOfTwos[i - 1]; + this.powerOfTwos[i] = previousPower.add(previousPower); + } + return this.powerOfTwos[exponent]; } - } - /** - * Computes and memoizes the big integer value for `this.number * 2^exponent`. - */ - getMultipliedByPowerOfTwo(exponent) { - // Compute the powers up until the requested exponent, where each value is computed from its - // predecessor. This is simple as `this.number * 2^(exponent - 1)` only has to be doubled (i.e. - // added to itself) to reach `this.number * 2^exponent`. - for (let i = this.powerOfTwos.length; i <= exponent; i++) { - const previousPower = this.powerOfTwos[i - 1]; - this.powerOfTwos[i] = previousPower.add(previousPower); - } - return this.powerOfTwos[exponent]; - } } /** * Represents an exponentiation operation for the provided base, of which exponents are computed and @@ -1164,24 +949,24 @@ * representation that is lazily computed upon request. */ class BigIntExponentiation { - constructor(base) { - this.base = base; - this.exponents = [new BigIntForMultiplication(BigInteger.one())]; - } - /** - * Compute the value for `this.base^exponent`, resulting in a big integer that is optimized for - * further multiplication operations. - */ - toThePowerOf(exponent) { - // Compute the results up until the requested exponent, where every value is computed from its - // predecessor. This is because `this.base^(exponent - 1)` only has to be multiplied by `base` - // to reach `this.base^exponent`. - for (let i = this.exponents.length; i <= exponent; i++) { - const value = this.exponents[i - 1].multiplyBy(this.base); - this.exponents[i] = new BigIntForMultiplication(value); - } - return this.exponents[exponent]; - } + constructor(base) { + this.base = base; + this.exponents = [new BigIntForMultiplication(BigInteger.one())]; + } + /** + * Compute the value for `this.base^exponent`, resulting in a big integer that is optimized for + * further multiplication operations. + */ + toThePowerOf(exponent) { + // Compute the results up until the requested exponent, where every value is computed from its + // predecessor. This is because `this.base^(exponent - 1)` only has to be multiplied by `base` + // to reach `this.base^exponent`. + for (let i = this.exponents.length; i <= exponent; i++) { + const value = this.exponents[i - 1].multiplyBy(this.base); + this.exponents[i] = new BigIntForMultiplication(value); + } + return this.exponents[exponent]; + } } /** @@ -1195,29 +980,27 @@ * Return the message id or compute it using the XLIFF1 digest. */ function digest$1(message) { - return message.id || computeDigest(message); + return message.id || computeDigest(message); } /** * Compute the message id using the XLIFF1 digest. */ function computeDigest(message) { - return sha1( - serializeNodes(message.nodes).join('') + `[${message.meaning}]` - ); + return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`); } /** * Return the message id or compute it using the XLIFF2/XMB/$localize digest. */ function decimalDigest(message) { - return message.id || computeDecimalDigest(message); + return message.id || computeDecimalDigest(message); } /** * Compute the message id using the XLIFF2/XMB/$localize digest. */ function computeDecimalDigest(message) { - const visitor = new _SerializerIgnoreIcuExpVisitor(); - const parts = message.nodes.map((a) => a.visit(visitor, null)); - return computeMsgId(parts.join(''), message.meaning); + const visitor = new _SerializerIgnoreIcuExpVisitor(); + const parts = message.nodes.map(a => a.visit(visitor, null)); + return computeMsgId(parts.join(''), message.meaning); } /** * Serialize the i18n ast to something xml-like in order to generate an UID. @@ -1227,39 +1010,31 @@ * @internal */ class _SerializerVisitor { - visitText(text, context) { - return text.value; - } - visitContainer(container, context) { - return `[${container.children - .map((child) => child.visit(this)) - .join(', ')}]`; - } - visitIcu(icu, context) { - const strCases = Object.keys(icu.cases).map( - (k) => `${k} {${icu.cases[k].visit(this)}}` - ); - return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`; - } - visitTagPlaceholder(ph, context) { - return ph.isVoid - ? `` - : `${ph.children - .map((child) => child.visit(this)) - .join(', ')}`; - } - visitPlaceholder(ph, context) { - return ph.value - ? `${ph.value}` - : ``; - } - visitIcuPlaceholder(ph, context) { - return `${ph.value.visit(this)}`; - } + visitText(text, context) { + return text.value; + } + visitContainer(container, context) { + return `[${container.children.map(child => child.visit(this)).join(', ')}]`; + } + visitIcu(icu, context) { + const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`); + return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`; + } + visitTagPlaceholder(ph, context) { + return ph.isVoid ? + `` : + `${ph.children.map(child => child.visit(this)).join(', ')}`; + } + visitPlaceholder(ph, context) { + return ph.value ? `${ph.value}` : ``; + } + visitIcuPlaceholder(ph, context) { + return `${ph.value.visit(this)}`; + } } const serializerVisitor$1 = new _SerializerVisitor(); function serializeNodes(nodes) { - return nodes.map((a) => a.visit(serializerVisitor$1, null)); + return nodes.map(a => a.visit(serializerVisitor$1, null)); } /** * Serialize the i18n ast to something xml-like in order to generate an UID. @@ -1269,13 +1044,11 @@ * @internal */ class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor { - visitIcu(icu, context) { - let strCases = Object.keys(icu.cases).map( - (k) => `${k} {${icu.cases[k].visit(this)}}` - ); - // Do not take the expression into account - return `{${icu.type}, ${strCases.join(', ')}}`; - } + visitIcu(icu, context) { + let strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`); + // Do not take the expression into account + return `{${icu.type}, ${strCases.join(', ')}}`; + } } /** * Compute the SHA1 of the given string @@ -1286,58 +1059,51 @@ * DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT. */ function sha1(str) { - const utf8 = utf8Encode(str); - const words32 = bytesToWords32(utf8, Endian.Big); - const len = utf8.length * 8; - const w = newArray(80); - let a = 0x67452301, - b = 0xefcdab89, - c = 0x98badcfe, - d = 0x10325476, - e = 0xc3d2e1f0; - words32[len >> 5] |= 0x80 << (24 - (len % 32)); - words32[(((len + 64) >> 9) << 4) + 15] = len; - for (let i = 0; i < words32.length; i += 16) { - const h0 = a, - h1 = b, - h2 = c, - h3 = d, - h4 = e; - for (let j = 0; j < 80; j++) { - if (j < 16) { - w[j] = words32[i + j]; - } else { - w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); - } - const fkVal = fk(j, b, c, d); - const f = fkVal[0]; - const k = fkVal[1]; - const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32); - e = d; - d = c; - c = rol32(b, 30); - b = a; - a = temp; - } - a = add32(a, h0); - b = add32(b, h1); - c = add32(c, h2); - d = add32(d, h3); - e = add32(e, h4); - } - return bytesToHexString(words32ToByteString([a, b, c, d, e])); + const utf8 = utf8Encode(str); + const words32 = bytesToWords32(utf8, Endian.Big); + const len = utf8.length * 8; + const w = newArray(80); + let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476, e = 0xc3d2e1f0; + words32[len >> 5] |= 0x80 << (24 - len % 32); + words32[((len + 64 >> 9) << 4) + 15] = len; + for (let i = 0; i < words32.length; i += 16) { + const h0 = a, h1 = b, h2 = c, h3 = d, h4 = e; + for (let j = 0; j < 80; j++) { + if (j < 16) { + w[j] = words32[i + j]; + } + else { + w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); + } + const fkVal = fk(j, b, c, d); + const f = fkVal[0]; + const k = fkVal[1]; + const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32); + e = d; + d = c; + c = rol32(b, 30); + b = a; + a = temp; + } + a = add32(a, h0); + b = add32(b, h1); + c = add32(c, h2); + d = add32(d, h3); + e = add32(e, h4); + } + return bytesToHexString(words32ToByteString([a, b, c, d, e])); } function fk(index, b, c, d) { - if (index < 20) { - return [(b & c) | (~b & d), 0x5a827999]; - } - if (index < 40) { - return [b ^ c ^ d, 0x6ed9eba1]; - } - if (index < 60) { - return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc]; - } - return [b ^ c ^ d, 0xca62c1d6]; + if (index < 20) { + return [(b & c) | (~b & d), 0x5a827999]; + } + if (index < 40) { + return [b ^ c ^ d, 0x6ed9eba1]; + } + if (index < 60) { + return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc]; + } + return [b ^ c ^ d, 0xca62c1d6]; } /** * Compute the fingerprint of the given string @@ -1348,162 +1114,156 @@ * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java */ function fingerprint(str) { - const utf8 = utf8Encode(str); - let hi = hash32(utf8, 0); - let lo = hash32(utf8, 102072); - if (hi == 0 && (lo == 0 || lo == 1)) { - hi = hi ^ 0x130f9bef; - lo = lo ^ -0x6b5f56d8; - } - return [hi, lo]; + const utf8 = utf8Encode(str); + let hi = hash32(utf8, 0); + let lo = hash32(utf8, 102072); + if (hi == 0 && (lo == 0 || lo == 1)) { + hi = hi ^ 0x130f9bef; + lo = lo ^ -0x6b5f56d8; + } + return [hi, lo]; } function computeMsgId(msg, meaning = '') { - let msgFingerprint = fingerprint(msg); - if (meaning) { - const meaningFingerprint = fingerprint(meaning); - msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint); - } - const hi = msgFingerprint[0]; - const lo = msgFingerprint[1]; - return wordsToDecimalString(hi & 0x7fffffff, lo); + let msgFingerprint = fingerprint(msg); + if (meaning) { + const meaningFingerprint = fingerprint(meaning); + msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint); + } + const hi = msgFingerprint[0]; + const lo = msgFingerprint[1]; + return wordsToDecimalString(hi & 0x7fffffff, lo); } function hash32(bytes, c) { - let a = 0x9e3779b9, - b = 0x9e3779b9; - let i; - const len = bytes.length; - for (i = 0; i + 12 <= len; i += 12) { + let a = 0x9e3779b9, b = 0x9e3779b9; + let i; + const len = bytes.length; + for (i = 0; i + 12 <= len; i += 12) { + a = add32(a, wordAt(bytes, i, Endian.Little)); + b = add32(b, wordAt(bytes, i + 4, Endian.Little)); + c = add32(c, wordAt(bytes, i + 8, Endian.Little)); + const res = mix(a, b, c); + a = res[0], b = res[1], c = res[2]; + } a = add32(a, wordAt(bytes, i, Endian.Little)); b = add32(b, wordAt(bytes, i + 4, Endian.Little)); - c = add32(c, wordAt(bytes, i + 8, Endian.Little)); - const res = mix(a, b, c); - (a = res[0]), (b = res[1]), (c = res[2]); - } - a = add32(a, wordAt(bytes, i, Endian.Little)); - b = add32(b, wordAt(bytes, i + 4, Endian.Little)); - // the first byte of c is reserved for the length - c = add32(c, len); - c = add32(c, wordAt(bytes, i + 8, Endian.Little) << 8); - return mix(a, b, c)[2]; + // the first byte of c is reserved for the length + c = add32(c, len); + c = add32(c, wordAt(bytes, i + 8, Endian.Little) << 8); + return mix(a, b, c)[2]; } // clang-format off function mix(a, b, c) { - a = sub32(a, b); - a = sub32(a, c); - a ^= c >>> 13; - b = sub32(b, c); - b = sub32(b, a); - b ^= a << 8; - c = sub32(c, a); - c = sub32(c, b); - c ^= b >>> 13; - a = sub32(a, b); - a = sub32(a, c); - a ^= c >>> 12; - b = sub32(b, c); - b = sub32(b, a); - b ^= a << 16; - c = sub32(c, a); - c = sub32(c, b); - c ^= b >>> 5; - a = sub32(a, b); - a = sub32(a, c); - a ^= c >>> 3; - b = sub32(b, c); - b = sub32(b, a); - b ^= a << 10; - c = sub32(c, a); - c = sub32(c, b); - c ^= b >>> 15; - return [a, b, c]; + a = sub32(a, b); + a = sub32(a, c); + a ^= c >>> 13; + b = sub32(b, c); + b = sub32(b, a); + b ^= a << 8; + c = sub32(c, a); + c = sub32(c, b); + c ^= b >>> 13; + a = sub32(a, b); + a = sub32(a, c); + a ^= c >>> 12; + b = sub32(b, c); + b = sub32(b, a); + b ^= a << 16; + c = sub32(c, a); + c = sub32(c, b); + c ^= b >>> 5; + a = sub32(a, b); + a = sub32(a, c); + a ^= c >>> 3; + b = sub32(b, c); + b = sub32(b, a); + b ^= a << 10; + c = sub32(c, a); + c = sub32(c, b); + c ^= b >>> 15; + return [a, b, c]; } // clang-format on // Utils var Endian; (function (Endian) { - Endian[(Endian['Little'] = 0)] = 'Little'; - Endian[(Endian['Big'] = 1)] = 'Big'; + Endian[Endian["Little"] = 0] = "Little"; + Endian[Endian["Big"] = 1] = "Big"; })(Endian || (Endian = {})); function add32(a, b) { - return add32to64(a, b)[1]; + return add32to64(a, b)[1]; } function add32to64(a, b) { - const low = (a & 0xffff) + (b & 0xffff); - const high = (a >>> 16) + (b >>> 16) + (low >>> 16); - return [high >>> 16, (high << 16) | (low & 0xffff)]; + const low = (a & 0xffff) + (b & 0xffff); + const high = (a >>> 16) + (b >>> 16) + (low >>> 16); + return [high >>> 16, (high << 16) | (low & 0xffff)]; } function add64(a, b) { - const ah = a[0], - al = a[1]; - const bh = b[0], - bl = b[1]; - const result = add32to64(al, bl); - const carry = result[0]; - const l = result[1]; - const h = add32(add32(ah, bh), carry); - return [h, l]; + const ah = a[0], al = a[1]; + const bh = b[0], bl = b[1]; + const result = add32to64(al, bl); + const carry = result[0]; + const l = result[1]; + const h = add32(add32(ah, bh), carry); + return [h, l]; } function sub32(a, b) { - const low = (a & 0xffff) - (b & 0xffff); - const high = (a >> 16) - (b >> 16) + (low >> 16); - return (high << 16) | (low & 0xffff); + const low = (a & 0xffff) - (b & 0xffff); + const high = (a >> 16) - (b >> 16) + (low >> 16); + return (high << 16) | (low & 0xffff); } // Rotate a 32b number left `count` position function rol32(a, count) { - return (a << count) | (a >>> (32 - count)); + return (a << count) | (a >>> (32 - count)); } // Rotate a 64b number left `count` position function rol64(num, count) { - const hi = num[0], - lo = num[1]; - const h = (hi << count) | (lo >>> (32 - count)); - const l = (lo << count) | (hi >>> (32 - count)); - return [h, l]; + const hi = num[0], lo = num[1]; + const h = (hi << count) | (lo >>> (32 - count)); + const l = (lo << count) | (hi >>> (32 - count)); + return [h, l]; } function bytesToWords32(bytes, endian) { - const size = (bytes.length + 3) >>> 2; - const words32 = []; - for (let i = 0; i < size; i++) { - words32[i] = wordAt(bytes, i * 4, endian); - } - return words32; + const size = (bytes.length + 3) >>> 2; + const words32 = []; + for (let i = 0; i < size; i++) { + words32[i] = wordAt(bytes, i * 4, endian); + } + return words32; } function byteAt(bytes, index) { - return index >= bytes.length ? 0 : bytes[index]; + return index >= bytes.length ? 0 : bytes[index]; } function wordAt(bytes, index, endian) { - let word = 0; - if (endian === Endian.Big) { - for (let i = 0; i < 4; i++) { - word += byteAt(bytes, index + i) << (24 - 8 * i); + let word = 0; + if (endian === Endian.Big) { + for (let i = 0; i < 4; i++) { + word += byteAt(bytes, index + i) << (24 - 8 * i); + } } - } else { - for (let i = 0; i < 4; i++) { - word += byteAt(bytes, index + i) << (8 * i); + else { + for (let i = 0; i < 4; i++) { + word += byteAt(bytes, index + i) << 8 * i; + } } - } - return word; + return word; } function words32ToByteString(words32) { - return words32.reduce( - (bytes, word) => bytes.concat(word32ToByteString(word)), - [] - ); + return words32.reduce((bytes, word) => bytes.concat(word32ToByteString(word)), []); } function word32ToByteString(word) { - let bytes = []; - for (let i = 0; i < 4; i++) { - bytes.push((word >>> (8 * (3 - i))) & 0xff); - } - return bytes; + let bytes = []; + for (let i = 0; i < 4; i++) { + bytes.push((word >>> 8 * (3 - i)) & 0xff); + } + return bytes; } function bytesToHexString(bytes) { - let hex = ''; - for (let i = 0; i < bytes.length; i++) { - const b = byteAt(bytes, i); - hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16); - } - return hex.toLowerCase(); + let hex = ''; + for (let i = 0; i < bytes.length; i++) { + const b = byteAt(bytes, i); + hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16); + } + return hex.toLowerCase(); } /** * Create a shared exponentiation pool for base-256 computations. This shared pool provides memoized @@ -1521,14 +1281,14 @@ * Based on https://www.danvk.org/hex2dec.html */ function wordsToDecimalString(hi, lo) { - // Encode the four bytes in lo in the lower digits of the decimal number. - // Note: the multiplication results in lo itself but represented by a big integer using its - // decimal digits. - const decimal = base256.toThePowerOf(0).multiplyBy(lo); - // Encode the four bytes in hi above the four lo bytes. lo is a maximum of (2^8)^4, which is why - // this multiplication factor is applied. - base256.toThePowerOf(4).multiplyByAndAddTo(hi, decimal); - return decimal.toString(); + // Encode the four bytes in lo in the lower digits of the decimal number. + // Note: the multiplication results in lo itself but represented by a big integer using its + // decimal digits. + const decimal = base256.toThePowerOf(0).multiplyBy(lo); + // Encode the four bytes in hi above the four lo bytes. lo is a maximum of (2^8)^4, which is why + // this multiplication factor is applied. + base256.toThePowerOf(4).multiplyByAndAddTo(hi, decimal); + return decimal.toString(); } /** @@ -1541,64 +1301,64 @@ //// Types exports.TypeModifier = void 0; (function (TypeModifier) { - TypeModifier[(TypeModifier['None'] = 0)] = 'None'; - TypeModifier[(TypeModifier['Const'] = 1)] = 'Const'; + TypeModifier[TypeModifier["None"] = 0] = "None"; + TypeModifier[TypeModifier["Const"] = 1] = "Const"; })(exports.TypeModifier || (exports.TypeModifier = {})); class Type { - constructor(modifiers = exports.TypeModifier.None) { - this.modifiers = modifiers; - } - hasModifier(modifier) { - return (this.modifiers & modifier) !== 0; - } + constructor(modifiers = exports.TypeModifier.None) { + this.modifiers = modifiers; + } + hasModifier(modifier) { + return (this.modifiers & modifier) !== 0; + } } exports.BuiltinTypeName = void 0; (function (BuiltinTypeName) { - BuiltinTypeName[(BuiltinTypeName['Dynamic'] = 0)] = 'Dynamic'; - BuiltinTypeName[(BuiltinTypeName['Bool'] = 1)] = 'Bool'; - BuiltinTypeName[(BuiltinTypeName['String'] = 2)] = 'String'; - BuiltinTypeName[(BuiltinTypeName['Int'] = 3)] = 'Int'; - BuiltinTypeName[(BuiltinTypeName['Number'] = 4)] = 'Number'; - BuiltinTypeName[(BuiltinTypeName['Function'] = 5)] = 'Function'; - BuiltinTypeName[(BuiltinTypeName['Inferred'] = 6)] = 'Inferred'; - BuiltinTypeName[(BuiltinTypeName['None'] = 7)] = 'None'; + BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic"; + BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool"; + BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String"; + BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int"; + BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number"; + BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function"; + BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred"; + BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None"; })(exports.BuiltinTypeName || (exports.BuiltinTypeName = {})); class BuiltinType extends Type { - constructor(name, modifiers) { - super(modifiers); - this.name = name; - } - visitType(visitor, context) { - return visitor.visitBuiltinType(this, context); - } + constructor(name, modifiers) { + super(modifiers); + this.name = name; + } + visitType(visitor, context) { + return visitor.visitBuiltinType(this, context); + } } class ExpressionType extends Type { - constructor(value, modifiers, typeParams = null) { - super(modifiers); - this.value = value; - this.typeParams = typeParams; - } - visitType(visitor, context) { - return visitor.visitExpressionType(this, context); - } + constructor(value, modifiers, typeParams = null) { + super(modifiers); + this.value = value; + this.typeParams = typeParams; + } + visitType(visitor, context) { + return visitor.visitExpressionType(this, context); + } } class ArrayType extends Type { - constructor(of, modifiers) { - super(modifiers); - this.of = of; - } - visitType(visitor, context) { - return visitor.visitArrayType(this, context); - } + constructor(of, modifiers) { + super(modifiers); + this.of = of; + } + visitType(visitor, context) { + return visitor.visitArrayType(this, context); + } } class MapType extends Type { - constructor(valueType, modifiers) { - super(modifiers); - this.valueType = valueType || null; - } - visitType(visitor, context) { - return visitor.visitMapType(this, context); - } + constructor(valueType, modifiers) { + super(modifiers); + this.valueType = valueType || null; + } + visitType(visitor, context) { + return visitor.visitMapType(this, context); + } } const DYNAMIC_TYPE = new BuiltinType(exports.BuiltinTypeName.Dynamic); const INFERRED_TYPE = new BuiltinType(exports.BuiltinTypeName.Inferred); @@ -1611,607 +1371,425 @@ ///// Expressions exports.UnaryOperator = void 0; (function (UnaryOperator) { - UnaryOperator[(UnaryOperator['Minus'] = 0)] = 'Minus'; - UnaryOperator[(UnaryOperator['Plus'] = 1)] = 'Plus'; + UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus"; + UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus"; })(exports.UnaryOperator || (exports.UnaryOperator = {})); exports.BinaryOperator = void 0; (function (BinaryOperator) { - BinaryOperator[(BinaryOperator['Equals'] = 0)] = 'Equals'; - BinaryOperator[(BinaryOperator['NotEquals'] = 1)] = 'NotEquals'; - BinaryOperator[(BinaryOperator['Identical'] = 2)] = 'Identical'; - BinaryOperator[(BinaryOperator['NotIdentical'] = 3)] = 'NotIdentical'; - BinaryOperator[(BinaryOperator['Minus'] = 4)] = 'Minus'; - BinaryOperator[(BinaryOperator['Plus'] = 5)] = 'Plus'; - BinaryOperator[(BinaryOperator['Divide'] = 6)] = 'Divide'; - BinaryOperator[(BinaryOperator['Multiply'] = 7)] = 'Multiply'; - BinaryOperator[(BinaryOperator['Modulo'] = 8)] = 'Modulo'; - BinaryOperator[(BinaryOperator['And'] = 9)] = 'And'; - BinaryOperator[(BinaryOperator['Or'] = 10)] = 'Or'; - BinaryOperator[(BinaryOperator['BitwiseAnd'] = 11)] = 'BitwiseAnd'; - BinaryOperator[(BinaryOperator['Lower'] = 12)] = 'Lower'; - BinaryOperator[(BinaryOperator['LowerEquals'] = 13)] = 'LowerEquals'; - BinaryOperator[(BinaryOperator['Bigger'] = 14)] = 'Bigger'; - BinaryOperator[(BinaryOperator['BiggerEquals'] = 15)] = 'BiggerEquals'; - BinaryOperator[(BinaryOperator['NullishCoalesce'] = 16)] = - 'NullishCoalesce'; + BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals"; + BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals"; + BinaryOperator[BinaryOperator["Identical"] = 2] = "Identical"; + BinaryOperator[BinaryOperator["NotIdentical"] = 3] = "NotIdentical"; + BinaryOperator[BinaryOperator["Minus"] = 4] = "Minus"; + BinaryOperator[BinaryOperator["Plus"] = 5] = "Plus"; + BinaryOperator[BinaryOperator["Divide"] = 6] = "Divide"; + BinaryOperator[BinaryOperator["Multiply"] = 7] = "Multiply"; + BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo"; + BinaryOperator[BinaryOperator["And"] = 9] = "And"; + BinaryOperator[BinaryOperator["Or"] = 10] = "Or"; + BinaryOperator[BinaryOperator["BitwiseAnd"] = 11] = "BitwiseAnd"; + BinaryOperator[BinaryOperator["Lower"] = 12] = "Lower"; + BinaryOperator[BinaryOperator["LowerEquals"] = 13] = "LowerEquals"; + BinaryOperator[BinaryOperator["Bigger"] = 14] = "Bigger"; + BinaryOperator[BinaryOperator["BiggerEquals"] = 15] = "BiggerEquals"; + BinaryOperator[BinaryOperator["NullishCoalesce"] = 16] = "NullishCoalesce"; })(exports.BinaryOperator || (exports.BinaryOperator = {})); function nullSafeIsEquivalent(base, other) { - if (base == null || other == null) { - return base == other; - } - return base.isEquivalent(other); + if (base == null || other == null) { + return base == other; + } + return base.isEquivalent(other); } function areAllEquivalentPredicate(base, other, equivalentPredicate) { - const len = base.length; - if (len !== other.length) { - return false; - } - for (let i = 0; i < len; i++) { - if (!equivalentPredicate(base[i], other[i])) { - return false; + const len = base.length; + if (len !== other.length) { + return false; + } + for (let i = 0; i < len; i++) { + if (!equivalentPredicate(base[i], other[i])) { + return false; + } } - } - return true; + return true; } function areAllEquivalent(base, other) { - return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => - baseElement.isEquivalent(otherElement) - ); + return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement)); } class Expression { - constructor(type, sourceSpan) { - this.type = type || null; - this.sourceSpan = sourceSpan || null; - } - prop(name, sourceSpan) { - return new ReadPropExpr(this, name, null, sourceSpan); - } - key(index, type, sourceSpan) { - return new ReadKeyExpr(this, index, type, sourceSpan); - } - callFn(params, sourceSpan, pure) { - return new InvokeFunctionExpr(this, params, null, sourceSpan, pure); - } - instantiate(params, type, sourceSpan) { - return new InstantiateExpr(this, params, type, sourceSpan); - } - conditional(trueCase, falseCase = null, sourceSpan) { - return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan); - } - equals(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Equals, - this, - rhs, - null, - sourceSpan - ); - } - notEquals(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.NotEquals, - this, - rhs, - null, - sourceSpan - ); - } - identical(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Identical, - this, - rhs, - null, - sourceSpan - ); - } - notIdentical(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.NotIdentical, - this, - rhs, - null, - sourceSpan - ); - } - minus(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Minus, - this, - rhs, - null, - sourceSpan - ); - } - plus(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Plus, - this, - rhs, - null, - sourceSpan - ); - } - divide(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Divide, - this, - rhs, - null, - sourceSpan - ); - } - multiply(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Multiply, - this, - rhs, - null, - sourceSpan - ); - } - modulo(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Modulo, - this, - rhs, - null, - sourceSpan - ); - } - and(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.And, - this, - rhs, - null, - sourceSpan - ); - } - bitwiseAnd(rhs, sourceSpan, parens = true) { - return new BinaryOperatorExpr( - exports.BinaryOperator.BitwiseAnd, - this, - rhs, - null, - sourceSpan, - parens - ); - } - or(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Or, - this, - rhs, - null, - sourceSpan - ); - } - lower(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Lower, - this, - rhs, - null, - sourceSpan - ); - } - lowerEquals(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.LowerEquals, - this, - rhs, - null, - sourceSpan - ); - } - bigger(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.Bigger, - this, - rhs, - null, - sourceSpan - ); - } - biggerEquals(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.BiggerEquals, - this, - rhs, - null, - sourceSpan - ); - } - isBlank(sourceSpan) { - // Note: We use equals by purpose here to compare to null and undefined in JS. - // We use the typed null to allow strictNullChecks to narrow types. - return this.equals(TYPED_NULL_EXPR, sourceSpan); - } - nullishCoalesce(rhs, sourceSpan) { - return new BinaryOperatorExpr( - exports.BinaryOperator.NullishCoalesce, - this, - rhs, - null, - sourceSpan - ); - } - toStmt() { - return new ExpressionStatement(this, null); - } + constructor(type, sourceSpan) { + this.type = type || null; + this.sourceSpan = sourceSpan || null; + } + prop(name, sourceSpan) { + return new ReadPropExpr(this, name, null, sourceSpan); + } + key(index, type, sourceSpan) { + return new ReadKeyExpr(this, index, type, sourceSpan); + } + callFn(params, sourceSpan, pure) { + return new InvokeFunctionExpr(this, params, null, sourceSpan, pure); + } + instantiate(params, type, sourceSpan) { + return new InstantiateExpr(this, params, type, sourceSpan); + } + conditional(trueCase, falseCase = null, sourceSpan) { + return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan); + } + equals(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Equals, this, rhs, null, sourceSpan); + } + notEquals(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.NotEquals, this, rhs, null, sourceSpan); + } + identical(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Identical, this, rhs, null, sourceSpan); + } + notIdentical(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.NotIdentical, this, rhs, null, sourceSpan); + } + minus(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Minus, this, rhs, null, sourceSpan); + } + plus(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Plus, this, rhs, null, sourceSpan); + } + divide(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Divide, this, rhs, null, sourceSpan); + } + multiply(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Multiply, this, rhs, null, sourceSpan); + } + modulo(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Modulo, this, rhs, null, sourceSpan); + } + and(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.And, this, rhs, null, sourceSpan); + } + bitwiseAnd(rhs, sourceSpan, parens = true) { + return new BinaryOperatorExpr(exports.BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens); + } + or(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Or, this, rhs, null, sourceSpan); + } + lower(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Lower, this, rhs, null, sourceSpan); + } + lowerEquals(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.LowerEquals, this, rhs, null, sourceSpan); + } + bigger(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.Bigger, this, rhs, null, sourceSpan); + } + biggerEquals(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan); + } + isBlank(sourceSpan) { + // Note: We use equals by purpose here to compare to null and undefined in JS. + // We use the typed null to allow strictNullChecks to narrow types. + return this.equals(TYPED_NULL_EXPR, sourceSpan); + } + nullishCoalesce(rhs, sourceSpan) { + return new BinaryOperatorExpr(exports.BinaryOperator.NullishCoalesce, this, rhs, null, sourceSpan); + } + toStmt() { + return new ExpressionStatement(this, null); + } } class ReadVarExpr extends Expression { - constructor(name, type, sourceSpan) { - super(type, sourceSpan); - this.name = name; - } - isEquivalent(e) { - return e instanceof ReadVarExpr && this.name === e.name; - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitReadVarExpr(this, context); - } - set(value) { - return new WriteVarExpr(this.name, value, null, this.sourceSpan); - } + constructor(name, type, sourceSpan) { + super(type, sourceSpan); + this.name = name; + } + isEquivalent(e) { + return e instanceof ReadVarExpr && this.name === e.name; + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitReadVarExpr(this, context); + } + set(value) { + return new WriteVarExpr(this.name, value, null, this.sourceSpan); + } } class TypeofExpr extends Expression { - constructor(expr, type, sourceSpan) { - super(type, sourceSpan); - this.expr = expr; - } - visitExpression(visitor, context) { - return visitor.visitTypeofExpr(this, context); - } - isEquivalent(e) { - return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr); - } - isConstant() { - return this.expr.isConstant(); - } + constructor(expr, type, sourceSpan) { + super(type, sourceSpan); + this.expr = expr; + } + visitExpression(visitor, context) { + return visitor.visitTypeofExpr(this, context); + } + isEquivalent(e) { + return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr); + } + isConstant() { + return this.expr.isConstant(); + } } class WrappedNodeExpr extends Expression { - constructor(node, type, sourceSpan) { - super(type, sourceSpan); - this.node = node; - } - isEquivalent(e) { - return e instanceof WrappedNodeExpr && this.node === e.node; - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitWrappedNodeExpr(this, context); - } + constructor(node, type, sourceSpan) { + super(type, sourceSpan); + this.node = node; + } + isEquivalent(e) { + return e instanceof WrappedNodeExpr && this.node === e.node; + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitWrappedNodeExpr(this, context); + } } class WriteVarExpr extends Expression { - constructor(name, value, type, sourceSpan) { - super(type || value.type, sourceSpan); - this.name = name; - this.value = value; - } - isEquivalent(e) { - return ( - e instanceof WriteVarExpr && - this.name === e.name && - this.value.isEquivalent(e.value) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitWriteVarExpr(this, context); - } - toDeclStmt(type, modifiers) { - return new DeclareVarStmt( - this.name, - this.value, - type, - modifiers, - this.sourceSpan - ); - } - toConstDecl() { - return this.toDeclStmt(INFERRED_TYPE, exports.StmtModifier.Final); - } - } - class WriteKeyExpr extends Expression { - constructor(receiver, index, value, type, sourceSpan) { - super(type || value.type, sourceSpan); - this.receiver = receiver; - this.index = index; - this.value = value; - } - isEquivalent(e) { - return ( - e instanceof WriteKeyExpr && - this.receiver.isEquivalent(e.receiver) && - this.index.isEquivalent(e.index) && - this.value.isEquivalent(e.value) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitWriteKeyExpr(this, context); - } + constructor(name, value, type, sourceSpan) { + super(type || value.type, sourceSpan); + this.name = name; + this.value = value; + } + isEquivalent(e) { + return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitWriteVarExpr(this, context); + } + toDeclStmt(type, modifiers) { + return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan); + } + toConstDecl() { + return this.toDeclStmt(INFERRED_TYPE, exports.StmtModifier.Final); + } + } + class WriteKeyExpr extends Expression { + constructor(receiver, index, value, type, sourceSpan) { + super(type || value.type, sourceSpan); + this.receiver = receiver; + this.index = index; + this.value = value; + } + isEquivalent(e) { + return e instanceof WriteKeyExpr && this.receiver.isEquivalent(e.receiver) && + this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitWriteKeyExpr(this, context); + } } class WritePropExpr extends Expression { - constructor(receiver, name, value, type, sourceSpan) { - super(type || value.type, sourceSpan); - this.receiver = receiver; - this.name = name; - this.value = value; - } - isEquivalent(e) { - return ( - e instanceof WritePropExpr && - this.receiver.isEquivalent(e.receiver) && - this.name === e.name && - this.value.isEquivalent(e.value) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitWritePropExpr(this, context); - } + constructor(receiver, name, value, type, sourceSpan) { + super(type || value.type, sourceSpan); + this.receiver = receiver; + this.name = name; + this.value = value; + } + isEquivalent(e) { + return e instanceof WritePropExpr && this.receiver.isEquivalent(e.receiver) && + this.name === e.name && this.value.isEquivalent(e.value); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitWritePropExpr(this, context); + } } class InvokeFunctionExpr extends Expression { - constructor(fn, args, type, sourceSpan, pure = false) { - super(type, sourceSpan); - this.fn = fn; - this.args = args; - this.pure = pure; - } - isEquivalent(e) { - return ( - e instanceof InvokeFunctionExpr && - this.fn.isEquivalent(e.fn) && - areAllEquivalent(this.args, e.args) && - this.pure === e.pure - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitInvokeFunctionExpr(this, context); - } + constructor(fn, args, type, sourceSpan, pure = false) { + super(type, sourceSpan); + this.fn = fn; + this.args = args; + this.pure = pure; + } + isEquivalent(e) { + return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) && + areAllEquivalent(this.args, e.args) && this.pure === e.pure; + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitInvokeFunctionExpr(this, context); + } } class TaggedTemplateExpr extends Expression { - constructor(tag, template, type, sourceSpan) { - super(type, sourceSpan); - this.tag = tag; - this.template = template; - } - isEquivalent(e) { - return ( - e instanceof TaggedTemplateExpr && - this.tag.isEquivalent(e.tag) && - areAllEquivalentPredicate( - this.template.elements, - e.template.elements, - (a, b) => a.text === b.text - ) && - areAllEquivalent(this.template.expressions, e.template.expressions) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitTaggedTemplateExpr(this, context); - } + constructor(tag, template, type, sourceSpan) { + super(type, sourceSpan); + this.tag = tag; + this.template = template; + } + isEquivalent(e) { + return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) && + areAllEquivalentPredicate(this.template.elements, e.template.elements, (a, b) => a.text === b.text) && + areAllEquivalent(this.template.expressions, e.template.expressions); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitTaggedTemplateExpr(this, context); + } } class InstantiateExpr extends Expression { - constructor(classExpr, args, type, sourceSpan) { - super(type, sourceSpan); - this.classExpr = classExpr; - this.args = args; - } - isEquivalent(e) { - return ( - e instanceof InstantiateExpr && - this.classExpr.isEquivalent(e.classExpr) && - areAllEquivalent(this.args, e.args) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitInstantiateExpr(this, context); - } + constructor(classExpr, args, type, sourceSpan) { + super(type, sourceSpan); + this.classExpr = classExpr; + this.args = args; + } + isEquivalent(e) { + return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) && + areAllEquivalent(this.args, e.args); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitInstantiateExpr(this, context); + } } class LiteralExpr extends Expression { - constructor(value, type, sourceSpan) { - super(type, sourceSpan); - this.value = value; - } - isEquivalent(e) { - return e instanceof LiteralExpr && this.value === e.value; - } - isConstant() { - return true; - } - visitExpression(visitor, context) { - return visitor.visitLiteralExpr(this, context); - } + constructor(value, type, sourceSpan) { + super(type, sourceSpan); + this.value = value; + } + isEquivalent(e) { + return e instanceof LiteralExpr && this.value === e.value; + } + isConstant() { + return true; + } + visitExpression(visitor, context) { + return visitor.visitLiteralExpr(this, context); + } } class TemplateLiteral { - constructor(elements, expressions) { - this.elements = elements; - this.expressions = expressions; - } + constructor(elements, expressions) { + this.elements = elements; + this.expressions = expressions; + } } class TemplateLiteralElement { - constructor(text, sourceSpan, rawText) { - var _a; - this.text = text; - this.sourceSpan = sourceSpan; - // If `rawText` is not provided, try to extract the raw string from its - // associated `sourceSpan`. If that is also not available, "fake" the raw - // string instead by escaping the following control sequences: - // - "\" would otherwise indicate that the next character is a control character. - // - "`" and "${" are template string control sequences that would otherwise prematurely - // indicate the end of the template literal element. - this.rawText = - (_a = - rawText !== null && rawText !== void 0 - ? rawText - : sourceSpan === null || sourceSpan === void 0 - ? void 0 - : sourceSpan.toString()) !== null && _a !== void 0 - ? _a - : escapeForTemplateLiteral(escapeSlashes(text)); - } + constructor(text, sourceSpan, rawText) { + var _a; + this.text = text; + this.sourceSpan = sourceSpan; + // If `rawText` is not provided, try to extract the raw string from its + // associated `sourceSpan`. If that is also not available, "fake" the raw + // string instead by escaping the following control sequences: + // - "\" would otherwise indicate that the next character is a control character. + // - "`" and "${" are template string control sequences that would otherwise prematurely + // indicate the end of the template literal element. + this.rawText = + (_a = rawText !== null && rawText !== void 0 ? rawText : sourceSpan === null || sourceSpan === void 0 ? void 0 : sourceSpan.toString()) !== null && _a !== void 0 ? _a : escapeForTemplateLiteral(escapeSlashes(text)); + } } class LiteralPiece { - constructor(text, sourceSpan) { - this.text = text; - this.sourceSpan = sourceSpan; - } + constructor(text, sourceSpan) { + this.text = text; + this.sourceSpan = sourceSpan; + } } class PlaceholderPiece { - /** - * Create a new instance of a `PlaceholderPiece`. - * - * @param text the name of this placeholder (e.g. `PH_1`). - * @param sourceSpan the location of this placeholder in its localized message the source code. - * @param associatedMessage reference to another message that this placeholder is associated with. - * The `associatedMessage` is mainly used to provide a relationship to an ICU message that has - * been extracted out from the message containing the placeholder. - */ - constructor(text, sourceSpan, associatedMessage) { - this.text = text; - this.sourceSpan = sourceSpan; - this.associatedMessage = associatedMessage; - } + /** + * Create a new instance of a `PlaceholderPiece`. + * + * @param text the name of this placeholder (e.g. `PH_1`). + * @param sourceSpan the location of this placeholder in its localized message the source code. + * @param associatedMessage reference to another message that this placeholder is associated with. + * The `associatedMessage` is mainly used to provide a relationship to an ICU message that has + * been extracted out from the message containing the placeholder. + */ + constructor(text, sourceSpan, associatedMessage) { + this.text = text; + this.sourceSpan = sourceSpan; + this.associatedMessage = associatedMessage; + } } const MEANING_SEPARATOR$1 = '|'; const ID_SEPARATOR$1 = '@@'; const LEGACY_ID_INDICATOR = '␟'; class LocalizedString extends Expression { - constructor( - metaBlock, - messageParts, - placeHolderNames, - expressions, - sourceSpan - ) { - super(STRING_TYPE, sourceSpan); - this.metaBlock = metaBlock; - this.messageParts = messageParts; - this.placeHolderNames = placeHolderNames; - this.expressions = expressions; - } - isEquivalent(e) { - // return e instanceof LocalizedString && this.message === e.message; - return false; - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitLocalizedString(this, context); - } - /** - * Serialize the given `meta` and `messagePart` into "cooked" and "raw" strings that can be used - * in a `$localize` tagged string. The format of the metadata is the same as that parsed by - * `parseI18nMeta()`. - * - * @param meta The metadata to serialize - * @param messagePart The first part of the tagged string - */ - serializeI18nHead() { - let metaBlock = this.metaBlock.description || ''; - if (this.metaBlock.meaning) { - metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR$1}${metaBlock}`; - } - if (this.metaBlock.customId) { - metaBlock = `${metaBlock}${ID_SEPARATOR$1}${this.metaBlock.customId}`; - } - if (this.metaBlock.legacyIds) { - this.metaBlock.legacyIds.forEach((legacyId) => { - metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`; - }); - } - return createCookedRawString( - metaBlock, - this.messageParts[0].text, - this.getMessagePartSourceSpan(0) - ); - } - getMessagePartSourceSpan(i) { - var _a, _b; - return (_b = - (_a = this.messageParts[i]) === null || _a === void 0 - ? void 0 - : _a.sourceSpan) !== null && _b !== void 0 - ? _b - : this.sourceSpan; - } - getPlaceholderSourceSpan(i) { - var _a, _b, _c, _d; - return (_d = - (_b = - (_a = this.placeHolderNames[i]) === null || _a === void 0 - ? void 0 - : _a.sourceSpan) !== null && _b !== void 0 - ? _b - : (_c = this.expressions[i]) === null || _c === void 0 - ? void 0 - : _c.sourceSpan) !== null && _d !== void 0 - ? _d - : this.sourceSpan; - } - /** - * Serialize the given `placeholderName` and `messagePart` into "cooked" and "raw" strings that - * can be used in a `$localize` tagged string. - * - * The format is `:[@@]:`. - * - * The `associated-id` is the message id of the (usually an ICU) message to which this placeholder - * refers. - * - * @param partIndex The index of the message part to serialize. - */ - serializeI18nTemplatePart(partIndex) { - var _a; - const placeholder = this.placeHolderNames[partIndex - 1]; - const messagePart = this.messageParts[partIndex]; - let metaBlock = placeholder.text; - if ( - ((_a = placeholder.associatedMessage) === null || _a === void 0 - ? void 0 - : _a.legacyIds.length) === 0 - ) { - metaBlock += `${ID_SEPARATOR$1}${computeMsgId( - placeholder.associatedMessage.messageString, - placeholder.associatedMessage.meaning - )}`; - } - return createCookedRawString( - metaBlock, - messagePart.text, - this.getMessagePartSourceSpan(partIndex) - ); - } + constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) { + super(STRING_TYPE, sourceSpan); + this.metaBlock = metaBlock; + this.messageParts = messageParts; + this.placeHolderNames = placeHolderNames; + this.expressions = expressions; + } + isEquivalent(e) { + // return e instanceof LocalizedString && this.message === e.message; + return false; + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitLocalizedString(this, context); + } + /** + * Serialize the given `meta` and `messagePart` into "cooked" and "raw" strings that can be used + * in a `$localize` tagged string. The format of the metadata is the same as that parsed by + * `parseI18nMeta()`. + * + * @param meta The metadata to serialize + * @param messagePart The first part of the tagged string + */ + serializeI18nHead() { + let metaBlock = this.metaBlock.description || ''; + if (this.metaBlock.meaning) { + metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR$1}${metaBlock}`; + } + if (this.metaBlock.customId) { + metaBlock = `${metaBlock}${ID_SEPARATOR$1}${this.metaBlock.customId}`; + } + if (this.metaBlock.legacyIds) { + this.metaBlock.legacyIds.forEach(legacyId => { + metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`; + }); + } + return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0)); + } + getMessagePartSourceSpan(i) { + var _a, _b; + return (_b = (_a = this.messageParts[i]) === null || _a === void 0 ? void 0 : _a.sourceSpan) !== null && _b !== void 0 ? _b : this.sourceSpan; + } + getPlaceholderSourceSpan(i) { + var _a, _b, _c, _d; + return (_d = (_b = (_a = this.placeHolderNames[i]) === null || _a === void 0 ? void 0 : _a.sourceSpan) !== null && _b !== void 0 ? _b : (_c = this.expressions[i]) === null || _c === void 0 ? void 0 : _c.sourceSpan) !== null && _d !== void 0 ? _d : this.sourceSpan; + } + /** + * Serialize the given `placeholderName` and `messagePart` into "cooked" and "raw" strings that + * can be used in a `$localize` tagged string. + * + * The format is `:[@@]:`. + * + * The `associated-id` is the message id of the (usually an ICU) message to which this placeholder + * refers. + * + * @param partIndex The index of the message part to serialize. + */ + serializeI18nTemplatePart(partIndex) { + var _a; + const placeholder = this.placeHolderNames[partIndex - 1]; + const messagePart = this.messageParts[partIndex]; + let metaBlock = placeholder.text; + if (((_a = placeholder.associatedMessage) === null || _a === void 0 ? void 0 : _a.legacyIds.length) === 0) { + metaBlock += `${ID_SEPARATOR$1}${computeMsgId(placeholder.associatedMessage.messageString, placeholder.associatedMessage.meaning)}`; + } + return createCookedRawString(metaBlock, messagePart.text, this.getMessagePartSourceSpan(partIndex)); + } } const escapeSlashes = (str) => str.replace(/\\/g, '\\\\'); const escapeStartingColon = (str) => str.replace(/^:/, '\\:'); const escapeColons = (str) => str.replace(/:/g, '\\:'); - const escapeForTemplateLiteral = (str) => - str.replace(/`/g, '\\`').replace(/\${/g, '$\\{'); + const escapeForTemplateLiteral = (str) => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{'); /** * Creates a `{cooked, raw}` object from the `metaBlock` and `messagePart`. * @@ -2227,812 +1805,684 @@ * @param messagePart The message part of the string */ function createCookedRawString(metaBlock, messagePart, range) { - if (metaBlock === '') { - return { - cooked: messagePart, - raw: escapeForTemplateLiteral( - escapeStartingColon(escapeSlashes(messagePart)) - ), - range, - }; - } else { - return { - cooked: `:${metaBlock}:${messagePart}`, - raw: escapeForTemplateLiteral( - `:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes( - messagePart - )}` - ), - range, - }; - } + if (metaBlock === '') { + return { + cooked: messagePart, + raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))), + range, + }; + } + else { + return { + cooked: `:${metaBlock}:${messagePart}`, + raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`), + range, + }; + } } class ExternalExpr extends Expression { - constructor(value, type, typeParams = null, sourceSpan) { - super(type, sourceSpan); - this.value = value; - this.typeParams = typeParams; - } - isEquivalent(e) { - return ( - e instanceof ExternalExpr && - this.value.name === e.value.name && - this.value.moduleName === e.value.moduleName && - this.value.runtime === e.value.runtime - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitExternalExpr(this, context); - } + constructor(value, type, typeParams = null, sourceSpan) { + super(type, sourceSpan); + this.value = value; + this.typeParams = typeParams; + } + isEquivalent(e) { + return e instanceof ExternalExpr && this.value.name === e.value.name && + this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime; + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitExternalExpr(this, context); + } } class ExternalReference { - constructor(moduleName, name, runtime) { - this.moduleName = moduleName; - this.name = name; - this.runtime = runtime; - } + constructor(moduleName, name, runtime) { + this.moduleName = moduleName; + this.name = name; + this.runtime = runtime; + } } class ConditionalExpr extends Expression { - constructor(condition, trueCase, falseCase = null, type, sourceSpan) { - super(type || trueCase.type, sourceSpan); - this.condition = condition; - this.falseCase = falseCase; - this.trueCase = trueCase; - } - isEquivalent(e) { - return ( - e instanceof ConditionalExpr && - this.condition.isEquivalent(e.condition) && - this.trueCase.isEquivalent(e.trueCase) && - nullSafeIsEquivalent(this.falseCase, e.falseCase) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitConditionalExpr(this, context); - } + constructor(condition, trueCase, falseCase = null, type, sourceSpan) { + super(type || trueCase.type, sourceSpan); + this.condition = condition; + this.falseCase = falseCase; + this.trueCase = trueCase; + } + isEquivalent(e) { + return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) && + this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitConditionalExpr(this, context); + } } class NotExpr extends Expression { - constructor(condition, sourceSpan) { - super(BOOL_TYPE, sourceSpan); - this.condition = condition; - } - isEquivalent(e) { - return e instanceof NotExpr && this.condition.isEquivalent(e.condition); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitNotExpr(this, context); - } + constructor(condition, sourceSpan) { + super(BOOL_TYPE, sourceSpan); + this.condition = condition; + } + isEquivalent(e) { + return e instanceof NotExpr && this.condition.isEquivalent(e.condition); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitNotExpr(this, context); + } } class FnParam { - constructor(name, type = null) { - this.name = name; - this.type = type; - } - isEquivalent(param) { - return this.name === param.name; - } + constructor(name, type = null) { + this.name = name; + this.type = type; + } + isEquivalent(param) { + return this.name === param.name; + } } class FunctionExpr extends Expression { - constructor(params, statements, type, sourceSpan, name) { - super(type, sourceSpan); - this.params = params; - this.statements = statements; - this.name = name; - } - isEquivalent(e) { - return ( - e instanceof FunctionExpr && - areAllEquivalent(this.params, e.params) && - areAllEquivalent(this.statements, e.statements) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitFunctionExpr(this, context); - } - toDeclStmt(name, modifiers) { - return new DeclareFunctionStmt( - name, - this.params, - this.statements, - this.type, - modifiers, - this.sourceSpan - ); - } + constructor(params, statements, type, sourceSpan, name) { + super(type, sourceSpan); + this.params = params; + this.statements = statements; + this.name = name; + } + isEquivalent(e) { + return e instanceof FunctionExpr && areAllEquivalent(this.params, e.params) && + areAllEquivalent(this.statements, e.statements); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitFunctionExpr(this, context); + } + toDeclStmt(name, modifiers) { + return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan); + } } class UnaryOperatorExpr extends Expression { - constructor(operator, expr, type, sourceSpan, parens = true) { - super(type || NUMBER_TYPE, sourceSpan); - this.operator = operator; - this.expr = expr; - this.parens = parens; - } - isEquivalent(e) { - return ( - e instanceof UnaryOperatorExpr && - this.operator === e.operator && - this.expr.isEquivalent(e.expr) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitUnaryOperatorExpr(this, context); - } + constructor(operator, expr, type, sourceSpan, parens = true) { + super(type || NUMBER_TYPE, sourceSpan); + this.operator = operator; + this.expr = expr; + this.parens = parens; + } + isEquivalent(e) { + return e instanceof UnaryOperatorExpr && this.operator === e.operator && + this.expr.isEquivalent(e.expr); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitUnaryOperatorExpr(this, context); + } } class BinaryOperatorExpr extends Expression { - constructor(operator, lhs, rhs, type, sourceSpan, parens = true) { - super(type || lhs.type, sourceSpan); - this.operator = operator; - this.rhs = rhs; - this.parens = parens; - this.lhs = lhs; - } - isEquivalent(e) { - return ( - e instanceof BinaryOperatorExpr && - this.operator === e.operator && - this.lhs.isEquivalent(e.lhs) && - this.rhs.isEquivalent(e.rhs) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitBinaryOperatorExpr(this, context); - } + constructor(operator, lhs, rhs, type, sourceSpan, parens = true) { + super(type || lhs.type, sourceSpan); + this.operator = operator; + this.rhs = rhs; + this.parens = parens; + this.lhs = lhs; + } + isEquivalent(e) { + return e instanceof BinaryOperatorExpr && this.operator === e.operator && + this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitBinaryOperatorExpr(this, context); + } } class ReadPropExpr extends Expression { - constructor(receiver, name, type, sourceSpan) { - super(type, sourceSpan); - this.receiver = receiver; - this.name = name; - } - isEquivalent(e) { - return ( - e instanceof ReadPropExpr && - this.receiver.isEquivalent(e.receiver) && - this.name === e.name - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitReadPropExpr(this, context); - } - set(value) { - return new WritePropExpr( - this.receiver, - this.name, - value, - null, - this.sourceSpan - ); - } + constructor(receiver, name, type, sourceSpan) { + super(type, sourceSpan); + this.receiver = receiver; + this.name = name; + } + isEquivalent(e) { + return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) && + this.name === e.name; + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitReadPropExpr(this, context); + } + set(value) { + return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan); + } } class ReadKeyExpr extends Expression { - constructor(receiver, index, type, sourceSpan) { - super(type, sourceSpan); - this.receiver = receiver; - this.index = index; - } - isEquivalent(e) { - return ( - e instanceof ReadKeyExpr && - this.receiver.isEquivalent(e.receiver) && - this.index.isEquivalent(e.index) - ); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitReadKeyExpr(this, context); - } - set(value) { - return new WriteKeyExpr( - this.receiver, - this.index, - value, - null, - this.sourceSpan - ); - } + constructor(receiver, index, type, sourceSpan) { + super(type, sourceSpan); + this.receiver = receiver; + this.index = index; + } + isEquivalent(e) { + return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) && + this.index.isEquivalent(e.index); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitReadKeyExpr(this, context); + } + set(value) { + return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan); + } } class LiteralArrayExpr extends Expression { - constructor(entries, type, sourceSpan) { - super(type, sourceSpan); - this.entries = entries; - } - isConstant() { - return this.entries.every((e) => e.isConstant()); - } - isEquivalent(e) { - return ( - e instanceof LiteralArrayExpr && - areAllEquivalent(this.entries, e.entries) - ); - } - visitExpression(visitor, context) { - return visitor.visitLiteralArrayExpr(this, context); - } + constructor(entries, type, sourceSpan) { + super(type, sourceSpan); + this.entries = entries; + } + isConstant() { + return this.entries.every(e => e.isConstant()); + } + isEquivalent(e) { + return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries); + } + visitExpression(visitor, context) { + return visitor.visitLiteralArrayExpr(this, context); + } } class LiteralMapEntry { - constructor(key, value, quoted) { - this.key = key; - this.value = value; - this.quoted = quoted; - } - isEquivalent(e) { - return this.key === e.key && this.value.isEquivalent(e.value); - } + constructor(key, value, quoted) { + this.key = key; + this.value = value; + this.quoted = quoted; + } + isEquivalent(e) { + return this.key === e.key && this.value.isEquivalent(e.value); + } } class LiteralMapExpr extends Expression { - constructor(entries, type, sourceSpan) { - super(type, sourceSpan); - this.entries = entries; - this.valueType = null; - if (type) { - this.valueType = type.valueType; - } - } - isEquivalent(e) { - return ( - e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries) - ); - } - isConstant() { - return this.entries.every((e) => e.value.isConstant()); - } - visitExpression(visitor, context) { - return visitor.visitLiteralMapExpr(this, context); - } + constructor(entries, type, sourceSpan) { + super(type, sourceSpan); + this.entries = entries; + this.valueType = null; + if (type) { + this.valueType = type.valueType; + } + } + isEquivalent(e) { + return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries); + } + isConstant() { + return this.entries.every(e => e.value.isConstant()); + } + visitExpression(visitor, context) { + return visitor.visitLiteralMapExpr(this, context); + } } class CommaExpr extends Expression { - constructor(parts, sourceSpan) { - super(parts[parts.length - 1].type, sourceSpan); - this.parts = parts; - } - isEquivalent(e) { - return e instanceof CommaExpr && areAllEquivalent(this.parts, e.parts); - } - isConstant() { - return false; - } - visitExpression(visitor, context) { - return visitor.visitCommaExpr(this, context); - } + constructor(parts, sourceSpan) { + super(parts[parts.length - 1].type, sourceSpan); + this.parts = parts; + } + isEquivalent(e) { + return e instanceof CommaExpr && areAllEquivalent(this.parts, e.parts); + } + isConstant() { + return false; + } + visitExpression(visitor, context) { + return visitor.visitCommaExpr(this, context); + } } const NULL_EXPR = new LiteralExpr(null, null, null); const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null); //// Statements exports.StmtModifier = void 0; (function (StmtModifier) { - StmtModifier[(StmtModifier['None'] = 0)] = 'None'; - StmtModifier[(StmtModifier['Final'] = 1)] = 'Final'; - StmtModifier[(StmtModifier['Private'] = 2)] = 'Private'; - StmtModifier[(StmtModifier['Exported'] = 4)] = 'Exported'; - StmtModifier[(StmtModifier['Static'] = 8)] = 'Static'; + StmtModifier[StmtModifier["None"] = 0] = "None"; + StmtModifier[StmtModifier["Final"] = 1] = "Final"; + StmtModifier[StmtModifier["Private"] = 2] = "Private"; + StmtModifier[StmtModifier["Exported"] = 4] = "Exported"; + StmtModifier[StmtModifier["Static"] = 8] = "Static"; })(exports.StmtModifier || (exports.StmtModifier = {})); class LeadingComment { - constructor(text, multiline, trailingNewline) { - this.text = text; - this.multiline = multiline; - this.trailingNewline = trailingNewline; - } - toString() { - return this.multiline ? ` ${this.text} ` : this.text; - } + constructor(text, multiline, trailingNewline) { + this.text = text; + this.multiline = multiline; + this.trailingNewline = trailingNewline; + } + toString() { + return this.multiline ? ` ${this.text} ` : this.text; + } } class JSDocComment extends LeadingComment { - constructor(tags) { - super('', /* multiline */ true, /* trailingNewline */ true); - this.tags = tags; - } - toString() { - return serializeTags(this.tags); - } + constructor(tags) { + super('', /* multiline */ true, /* trailingNewline */ true); + this.tags = tags; + } + toString() { + return serializeTags(this.tags); + } } class Statement { - constructor( - modifiers = exports.StmtModifier.None, - sourceSpan = null, - leadingComments - ) { - this.modifiers = modifiers; - this.sourceSpan = sourceSpan; - this.leadingComments = leadingComments; - } - hasModifier(modifier) { - return (this.modifiers & modifier) !== 0; - } - addLeadingComment(leadingComment) { - var _a; - this.leadingComments = - (_a = this.leadingComments) !== null && _a !== void 0 ? _a : []; - this.leadingComments.push(leadingComment); - } + constructor(modifiers = exports.StmtModifier.None, sourceSpan = null, leadingComments) { + this.modifiers = modifiers; + this.sourceSpan = sourceSpan; + this.leadingComments = leadingComments; + } + hasModifier(modifier) { + return (this.modifiers & modifier) !== 0; + } + addLeadingComment(leadingComment) { + var _a; + this.leadingComments = (_a = this.leadingComments) !== null && _a !== void 0 ? _a : []; + this.leadingComments.push(leadingComment); + } } class DeclareVarStmt extends Statement { - constructor(name, value, type, modifiers, sourceSpan, leadingComments) { - super(modifiers, sourceSpan, leadingComments); - this.name = name; - this.value = value; - this.type = type || (value && value.type) || null; - } - isEquivalent(stmt) { - return ( - stmt instanceof DeclareVarStmt && - this.name === stmt.name && - (this.value - ? !!stmt.value && this.value.isEquivalent(stmt.value) - : !stmt.value) - ); - } - visitStatement(visitor, context) { - return visitor.visitDeclareVarStmt(this, context); - } + constructor(name, value, type, modifiers, sourceSpan, leadingComments) { + super(modifiers, sourceSpan, leadingComments); + this.name = name; + this.value = value; + this.type = type || (value && value.type) || null; + } + isEquivalent(stmt) { + return stmt instanceof DeclareVarStmt && this.name === stmt.name && + (this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value); + } + visitStatement(visitor, context) { + return visitor.visitDeclareVarStmt(this, context); + } } class DeclareFunctionStmt extends Statement { - constructor( - name, - params, - statements, - type, - modifiers, - sourceSpan, - leadingComments - ) { - super(modifiers, sourceSpan, leadingComments); - this.name = name; - this.params = params; - this.statements = statements; - this.type = type || null; - } - isEquivalent(stmt) { - return ( - stmt instanceof DeclareFunctionStmt && - areAllEquivalent(this.params, stmt.params) && - areAllEquivalent(this.statements, stmt.statements) - ); - } - visitStatement(visitor, context) { - return visitor.visitDeclareFunctionStmt(this, context); - } + constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) { + super(modifiers, sourceSpan, leadingComments); + this.name = name; + this.params = params; + this.statements = statements; + this.type = type || null; + } + isEquivalent(stmt) { + return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) && + areAllEquivalent(this.statements, stmt.statements); + } + visitStatement(visitor, context) { + return visitor.visitDeclareFunctionStmt(this, context); + } } class ExpressionStatement extends Statement { - constructor(expr, sourceSpan, leadingComments) { - super(exports.StmtModifier.None, sourceSpan, leadingComments); - this.expr = expr; - } - isEquivalent(stmt) { - return ( - stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr) - ); - } - visitStatement(visitor, context) { - return visitor.visitExpressionStmt(this, context); - } + constructor(expr, sourceSpan, leadingComments) { + super(exports.StmtModifier.None, sourceSpan, leadingComments); + this.expr = expr; + } + isEquivalent(stmt) { + return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr); + } + visitStatement(visitor, context) { + return visitor.visitExpressionStmt(this, context); + } } class ReturnStatement extends Statement { - constructor(value, sourceSpan = null, leadingComments) { - super(exports.StmtModifier.None, sourceSpan, leadingComments); - this.value = value; - } - isEquivalent(stmt) { - return ( - stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value) - ); - } - visitStatement(visitor, context) { - return visitor.visitReturnStmt(this, context); - } + constructor(value, sourceSpan = null, leadingComments) { + super(exports.StmtModifier.None, sourceSpan, leadingComments); + this.value = value; + } + isEquivalent(stmt) { + return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value); + } + visitStatement(visitor, context) { + return visitor.visitReturnStmt(this, context); + } } class IfStmt extends Statement { - constructor( - condition, - trueCase, - falseCase = [], - sourceSpan, - leadingComments - ) { - super(exports.StmtModifier.None, sourceSpan, leadingComments); - this.condition = condition; - this.trueCase = trueCase; - this.falseCase = falseCase; - } - isEquivalent(stmt) { - return ( - stmt instanceof IfStmt && - this.condition.isEquivalent(stmt.condition) && - areAllEquivalent(this.trueCase, stmt.trueCase) && - areAllEquivalent(this.falseCase, stmt.falseCase) - ); - } - visitStatement(visitor, context) { - return visitor.visitIfStmt(this, context); - } + constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) { + super(exports.StmtModifier.None, sourceSpan, leadingComments); + this.condition = condition; + this.trueCase = trueCase; + this.falseCase = falseCase; + } + isEquivalent(stmt) { + return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) && + areAllEquivalent(this.trueCase, stmt.trueCase) && + areAllEquivalent(this.falseCase, stmt.falseCase); + } + visitStatement(visitor, context) { + return visitor.visitIfStmt(this, context); + } } class RecursiveAstVisitor$1 { - visitType(ast, context) { - return ast; - } - visitExpression(ast, context) { - if (ast.type) { - ast.type.visitType(this, context); - } - return ast; - } - visitBuiltinType(type, context) { - return this.visitType(type, context); - } - visitExpressionType(type, context) { - type.value.visitExpression(this, context); - if (type.typeParams !== null) { - type.typeParams.forEach((param) => this.visitType(param, context)); - } - return this.visitType(type, context); - } - visitArrayType(type, context) { - return this.visitType(type, context); - } - visitMapType(type, context) { - return this.visitType(type, context); - } - visitWrappedNodeExpr(ast, context) { - return ast; - } - visitTypeofExpr(ast, context) { - return this.visitExpression(ast, context); - } - visitReadVarExpr(ast, context) { - return this.visitExpression(ast, context); - } - visitWriteVarExpr(ast, context) { - ast.value.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitWriteKeyExpr(ast, context) { - ast.receiver.visitExpression(this, context); - ast.index.visitExpression(this, context); - ast.value.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitWritePropExpr(ast, context) { - ast.receiver.visitExpression(this, context); - ast.value.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitInvokeFunctionExpr(ast, context) { - ast.fn.visitExpression(this, context); - this.visitAllExpressions(ast.args, context); - return this.visitExpression(ast, context); - } - visitTaggedTemplateExpr(ast, context) { - ast.tag.visitExpression(this, context); - this.visitAllExpressions(ast.template.expressions, context); - return this.visitExpression(ast, context); - } - visitInstantiateExpr(ast, context) { - ast.classExpr.visitExpression(this, context); - this.visitAllExpressions(ast.args, context); - return this.visitExpression(ast, context); - } - visitLiteralExpr(ast, context) { - return this.visitExpression(ast, context); - } - visitLocalizedString(ast, context) { - return this.visitExpression(ast, context); - } - visitExternalExpr(ast, context) { - if (ast.typeParams) { - ast.typeParams.forEach((type) => type.visitType(this, context)); - } - return this.visitExpression(ast, context); - } - visitConditionalExpr(ast, context) { - ast.condition.visitExpression(this, context); - ast.trueCase.visitExpression(this, context); - ast.falseCase.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitNotExpr(ast, context) { - ast.condition.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitFunctionExpr(ast, context) { - this.visitAllStatements(ast.statements, context); - return this.visitExpression(ast, context); - } - visitUnaryOperatorExpr(ast, context) { - ast.expr.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitBinaryOperatorExpr(ast, context) { - ast.lhs.visitExpression(this, context); - ast.rhs.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitReadPropExpr(ast, context) { - ast.receiver.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitReadKeyExpr(ast, context) { - ast.receiver.visitExpression(this, context); - ast.index.visitExpression(this, context); - return this.visitExpression(ast, context); - } - visitLiteralArrayExpr(ast, context) { - this.visitAllExpressions(ast.entries, context); - return this.visitExpression(ast, context); - } - visitLiteralMapExpr(ast, context) { - ast.entries.forEach((entry) => - entry.value.visitExpression(this, context) - ); - return this.visitExpression(ast, context); - } - visitCommaExpr(ast, context) { - this.visitAllExpressions(ast.parts, context); - return this.visitExpression(ast, context); - } - visitAllExpressions(exprs, context) { - exprs.forEach((expr) => expr.visitExpression(this, context)); - } - visitDeclareVarStmt(stmt, context) { - if (stmt.value) { - stmt.value.visitExpression(this, context); - } - if (stmt.type) { - stmt.type.visitType(this, context); - } - return stmt; - } - visitDeclareFunctionStmt(stmt, context) { - this.visitAllStatements(stmt.statements, context); - if (stmt.type) { - stmt.type.visitType(this, context); - } - return stmt; - } - visitExpressionStmt(stmt, context) { - stmt.expr.visitExpression(this, context); - return stmt; - } - visitReturnStmt(stmt, context) { - stmt.value.visitExpression(this, context); - return stmt; - } - visitIfStmt(stmt, context) { - stmt.condition.visitExpression(this, context); - this.visitAllStatements(stmt.trueCase, context); - this.visitAllStatements(stmt.falseCase, context); - return stmt; - } - visitAllStatements(stmts, context) { - stmts.forEach((stmt) => stmt.visitStatement(this, context)); - } + visitType(ast, context) { + return ast; + } + visitExpression(ast, context) { + if (ast.type) { + ast.type.visitType(this, context); + } + return ast; + } + visitBuiltinType(type, context) { + return this.visitType(type, context); + } + visitExpressionType(type, context) { + type.value.visitExpression(this, context); + if (type.typeParams !== null) { + type.typeParams.forEach(param => this.visitType(param, context)); + } + return this.visitType(type, context); + } + visitArrayType(type, context) { + return this.visitType(type, context); + } + visitMapType(type, context) { + return this.visitType(type, context); + } + visitWrappedNodeExpr(ast, context) { + return ast; + } + visitTypeofExpr(ast, context) { + return this.visitExpression(ast, context); + } + visitReadVarExpr(ast, context) { + return this.visitExpression(ast, context); + } + visitWriteVarExpr(ast, context) { + ast.value.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitWriteKeyExpr(ast, context) { + ast.receiver.visitExpression(this, context); + ast.index.visitExpression(this, context); + ast.value.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitWritePropExpr(ast, context) { + ast.receiver.visitExpression(this, context); + ast.value.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitInvokeFunctionExpr(ast, context) { + ast.fn.visitExpression(this, context); + this.visitAllExpressions(ast.args, context); + return this.visitExpression(ast, context); + } + visitTaggedTemplateExpr(ast, context) { + ast.tag.visitExpression(this, context); + this.visitAllExpressions(ast.template.expressions, context); + return this.visitExpression(ast, context); + } + visitInstantiateExpr(ast, context) { + ast.classExpr.visitExpression(this, context); + this.visitAllExpressions(ast.args, context); + return this.visitExpression(ast, context); + } + visitLiteralExpr(ast, context) { + return this.visitExpression(ast, context); + } + visitLocalizedString(ast, context) { + return this.visitExpression(ast, context); + } + visitExternalExpr(ast, context) { + if (ast.typeParams) { + ast.typeParams.forEach(type => type.visitType(this, context)); + } + return this.visitExpression(ast, context); + } + visitConditionalExpr(ast, context) { + ast.condition.visitExpression(this, context); + ast.trueCase.visitExpression(this, context); + ast.falseCase.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitNotExpr(ast, context) { + ast.condition.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitFunctionExpr(ast, context) { + this.visitAllStatements(ast.statements, context); + return this.visitExpression(ast, context); + } + visitUnaryOperatorExpr(ast, context) { + ast.expr.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitBinaryOperatorExpr(ast, context) { + ast.lhs.visitExpression(this, context); + ast.rhs.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitReadPropExpr(ast, context) { + ast.receiver.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitReadKeyExpr(ast, context) { + ast.receiver.visitExpression(this, context); + ast.index.visitExpression(this, context); + return this.visitExpression(ast, context); + } + visitLiteralArrayExpr(ast, context) { + this.visitAllExpressions(ast.entries, context); + return this.visitExpression(ast, context); + } + visitLiteralMapExpr(ast, context) { + ast.entries.forEach((entry) => entry.value.visitExpression(this, context)); + return this.visitExpression(ast, context); + } + visitCommaExpr(ast, context) { + this.visitAllExpressions(ast.parts, context); + return this.visitExpression(ast, context); + } + visitAllExpressions(exprs, context) { + exprs.forEach(expr => expr.visitExpression(this, context)); + } + visitDeclareVarStmt(stmt, context) { + if (stmt.value) { + stmt.value.visitExpression(this, context); + } + if (stmt.type) { + stmt.type.visitType(this, context); + } + return stmt; + } + visitDeclareFunctionStmt(stmt, context) { + this.visitAllStatements(stmt.statements, context); + if (stmt.type) { + stmt.type.visitType(this, context); + } + return stmt; + } + visitExpressionStmt(stmt, context) { + stmt.expr.visitExpression(this, context); + return stmt; + } + visitReturnStmt(stmt, context) { + stmt.value.visitExpression(this, context); + return stmt; + } + visitIfStmt(stmt, context) { + stmt.condition.visitExpression(this, context); + this.visitAllStatements(stmt.trueCase, context); + this.visitAllStatements(stmt.falseCase, context); + return stmt; + } + visitAllStatements(stmts, context) { + stmts.forEach(stmt => stmt.visitStatement(this, context)); + } } function leadingComment(text, multiline = false, trailingNewline = true) { - return new LeadingComment(text, multiline, trailingNewline); + return new LeadingComment(text, multiline, trailingNewline); } function jsDocComment(tags = []) { - return new JSDocComment(tags); + return new JSDocComment(tags); } function variable(name, type, sourceSpan) { - return new ReadVarExpr(name, type, sourceSpan); + return new ReadVarExpr(name, type, sourceSpan); } function importExpr(id, typeParams = null, sourceSpan) { - return new ExternalExpr(id, null, typeParams, sourceSpan); + return new ExternalExpr(id, null, typeParams, sourceSpan); } function importType(id, typeParams, typeModifiers) { - return id != null - ? expressionType(importExpr(id, typeParams, null), typeModifiers) - : null; + return id != null ? expressionType(importExpr(id, typeParams, null), typeModifiers) : null; } function expressionType(expr, typeModifiers, typeParams) { - return new ExpressionType(expr, typeModifiers, typeParams); + return new ExpressionType(expr, typeModifiers, typeParams); } function typeofExpr(expr) { - return new TypeofExpr(expr); + return new TypeofExpr(expr); } function literalArr(values, type, sourceSpan) { - return new LiteralArrayExpr(values, type, sourceSpan); + return new LiteralArrayExpr(values, type, sourceSpan); } function literalMap(values, type = null) { - return new LiteralMapExpr( - values.map((e) => new LiteralMapEntry(e.key, e.value, e.quoted)), - type, - null - ); + return new LiteralMapExpr(values.map(e => new LiteralMapEntry(e.key, e.value, e.quoted)), type, null); } function unary(operator, expr, type, sourceSpan) { - return new UnaryOperatorExpr(operator, expr, type, sourceSpan); + return new UnaryOperatorExpr(operator, expr, type, sourceSpan); } function not(expr, sourceSpan) { - return new NotExpr(expr, sourceSpan); + return new NotExpr(expr, sourceSpan); } function fn(params, body, type, sourceSpan, name) { - return new FunctionExpr(params, body, type, sourceSpan, name); - } - function ifStmt( - condition, - thenClause, - elseClause, - sourceSpan, - leadingComments - ) { - return new IfStmt( - condition, - thenClause, - elseClause, - sourceSpan, - leadingComments - ); + return new FunctionExpr(params, body, type, sourceSpan, name); + } + function ifStmt(condition, thenClause, elseClause, sourceSpan, leadingComments) { + return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments); } function taggedTemplate(tag, template, type, sourceSpan) { - return new TaggedTemplateExpr(tag, template, type, sourceSpan); + return new TaggedTemplateExpr(tag, template, type, sourceSpan); } function literal(value, type, sourceSpan) { - return new LiteralExpr(value, type, sourceSpan); - } - function localizedString( - metaBlock, - messageParts, - placeholderNames, - expressions, - sourceSpan - ) { - return new LocalizedString( - metaBlock, - messageParts, - placeholderNames, - expressions, - sourceSpan - ); + return new LiteralExpr(value, type, sourceSpan); + } + function localizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan) { + return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan); } function isNull(exp) { - return exp instanceof LiteralExpr && exp.value === null; + return exp instanceof LiteralExpr && exp.value === null; } /* * Serializes a `Tag` into a string. * Returns a string like " @foo {bar} baz" (note the leading whitespace before `@foo`). */ function tagToString(tag) { - let out = ''; - if (tag.tagName) { - out += ` @${tag.tagName}`; - } - if (tag.text) { - if (tag.text.match(/\/\*|\*\//)) { - throw new Error('JSDoc text cannot contain "/*" and "*/"'); - } - out += ' ' + tag.text.replace(/@/g, '\\@'); - } - return out; + let out = ''; + if (tag.tagName) { + out += ` @${tag.tagName}`; + } + if (tag.text) { + if (tag.text.match(/\/\*|\*\//)) { + throw new Error('JSDoc text cannot contain "/*" and "*/"'); + } + out += ' ' + tag.text.replace(/@/g, '\\@'); + } + return out; } function serializeTags(tags) { - if (tags.length === 0) return ''; - if (tags.length === 1 && tags[0].tagName && !tags[0].text) { - // The JSDOC comment is a single simple tag: e.g `/** @tagname */`. - return `*${tagToString(tags[0])} `; - } - let out = '*\n'; - for (const tag of tags) { - out += ' *'; - // If the tagToString is multi-line, insert " * " prefixes on lines. - out += tagToString(tag).replace(/\n/g, '\n * '); - out += '\n'; - } - out += ' '; - return out; + if (tags.length === 0) + return ''; + if (tags.length === 1 && tags[0].tagName && !tags[0].text) { + // The JSDOC comment is a single simple tag: e.g `/** @tagname */`. + return `*${tagToString(tags[0])} `; + } + let out = '*\n'; + for (const tag of tags) { + out += ' *'; + // If the tagToString is multi-line, insert " * " prefixes on lines. + out += tagToString(tag).replace(/\n/g, '\n * '); + out += '\n'; + } + out += ' '; + return out; } - var output_ast = /*#__PURE__*/ Object.freeze({ - __proto__: null, - get TypeModifier() { - return exports.TypeModifier; - }, - Type: Type, - get BuiltinTypeName() { - return exports.BuiltinTypeName; - }, - BuiltinType: BuiltinType, - ExpressionType: ExpressionType, - ArrayType: ArrayType, - MapType: MapType, - DYNAMIC_TYPE: DYNAMIC_TYPE, - INFERRED_TYPE: INFERRED_TYPE, - BOOL_TYPE: BOOL_TYPE, - INT_TYPE: INT_TYPE, - NUMBER_TYPE: NUMBER_TYPE, - STRING_TYPE: STRING_TYPE, - FUNCTION_TYPE: FUNCTION_TYPE, - NONE_TYPE: NONE_TYPE, - get UnaryOperator() { - return exports.UnaryOperator; - }, - get BinaryOperator() { - return exports.BinaryOperator; - }, - nullSafeIsEquivalent: nullSafeIsEquivalent, - areAllEquivalent: areAllEquivalent, - Expression: Expression, - ReadVarExpr: ReadVarExpr, - TypeofExpr: TypeofExpr, - WrappedNodeExpr: WrappedNodeExpr, - WriteVarExpr: WriteVarExpr, - WriteKeyExpr: WriteKeyExpr, - WritePropExpr: WritePropExpr, - InvokeFunctionExpr: InvokeFunctionExpr, - TaggedTemplateExpr: TaggedTemplateExpr, - InstantiateExpr: InstantiateExpr, - LiteralExpr: LiteralExpr, - TemplateLiteral: TemplateLiteral, - TemplateLiteralElement: TemplateLiteralElement, - LiteralPiece: LiteralPiece, - PlaceholderPiece: PlaceholderPiece, - LocalizedString: LocalizedString, - ExternalExpr: ExternalExpr, - ExternalReference: ExternalReference, - ConditionalExpr: ConditionalExpr, - NotExpr: NotExpr, - FnParam: FnParam, - FunctionExpr: FunctionExpr, - UnaryOperatorExpr: UnaryOperatorExpr, - BinaryOperatorExpr: BinaryOperatorExpr, - ReadPropExpr: ReadPropExpr, - ReadKeyExpr: ReadKeyExpr, - LiteralArrayExpr: LiteralArrayExpr, - LiteralMapEntry: LiteralMapEntry, - LiteralMapExpr: LiteralMapExpr, - CommaExpr: CommaExpr, - NULL_EXPR: NULL_EXPR, - TYPED_NULL_EXPR: TYPED_NULL_EXPR, - get StmtModifier() { - return exports.StmtModifier; - }, - LeadingComment: LeadingComment, - JSDocComment: JSDocComment, - Statement: Statement, - DeclareVarStmt: DeclareVarStmt, - DeclareFunctionStmt: DeclareFunctionStmt, - ExpressionStatement: ExpressionStatement, - ReturnStatement: ReturnStatement, - IfStmt: IfStmt, - RecursiveAstVisitor: RecursiveAstVisitor$1, - leadingComment: leadingComment, - jsDocComment: jsDocComment, - variable: variable, - importExpr: importExpr, - importType: importType, - expressionType: expressionType, - typeofExpr: typeofExpr, - literalArr: literalArr, - literalMap: literalMap, - unary: unary, - not: not, - fn: fn, - ifStmt: ifStmt, - taggedTemplate: taggedTemplate, - literal: literal, - localizedString: localizedString, - isNull: isNull, + var output_ast = /*#__PURE__*/Object.freeze({ + __proto__: null, + get TypeModifier () { return exports.TypeModifier; }, + Type: Type, + get BuiltinTypeName () { return exports.BuiltinTypeName; }, + BuiltinType: BuiltinType, + ExpressionType: ExpressionType, + ArrayType: ArrayType, + MapType: MapType, + DYNAMIC_TYPE: DYNAMIC_TYPE, + INFERRED_TYPE: INFERRED_TYPE, + BOOL_TYPE: BOOL_TYPE, + INT_TYPE: INT_TYPE, + NUMBER_TYPE: NUMBER_TYPE, + STRING_TYPE: STRING_TYPE, + FUNCTION_TYPE: FUNCTION_TYPE, + NONE_TYPE: NONE_TYPE, + get UnaryOperator () { return exports.UnaryOperator; }, + get BinaryOperator () { return exports.BinaryOperator; }, + nullSafeIsEquivalent: nullSafeIsEquivalent, + areAllEquivalent: areAllEquivalent, + Expression: Expression, + ReadVarExpr: ReadVarExpr, + TypeofExpr: TypeofExpr, + WrappedNodeExpr: WrappedNodeExpr, + WriteVarExpr: WriteVarExpr, + WriteKeyExpr: WriteKeyExpr, + WritePropExpr: WritePropExpr, + InvokeFunctionExpr: InvokeFunctionExpr, + TaggedTemplateExpr: TaggedTemplateExpr, + InstantiateExpr: InstantiateExpr, + LiteralExpr: LiteralExpr, + TemplateLiteral: TemplateLiteral, + TemplateLiteralElement: TemplateLiteralElement, + LiteralPiece: LiteralPiece, + PlaceholderPiece: PlaceholderPiece, + LocalizedString: LocalizedString, + ExternalExpr: ExternalExpr, + ExternalReference: ExternalReference, + ConditionalExpr: ConditionalExpr, + NotExpr: NotExpr, + FnParam: FnParam, + FunctionExpr: FunctionExpr, + UnaryOperatorExpr: UnaryOperatorExpr, + BinaryOperatorExpr: BinaryOperatorExpr, + ReadPropExpr: ReadPropExpr, + ReadKeyExpr: ReadKeyExpr, + LiteralArrayExpr: LiteralArrayExpr, + LiteralMapEntry: LiteralMapEntry, + LiteralMapExpr: LiteralMapExpr, + CommaExpr: CommaExpr, + NULL_EXPR: NULL_EXPR, + TYPED_NULL_EXPR: TYPED_NULL_EXPR, + get StmtModifier () { return exports.StmtModifier; }, + LeadingComment: LeadingComment, + JSDocComment: JSDocComment, + Statement: Statement, + DeclareVarStmt: DeclareVarStmt, + DeclareFunctionStmt: DeclareFunctionStmt, + ExpressionStatement: ExpressionStatement, + ReturnStatement: ReturnStatement, + IfStmt: IfStmt, + RecursiveAstVisitor: RecursiveAstVisitor$1, + leadingComment: leadingComment, + jsDocComment: jsDocComment, + variable: variable, + importExpr: importExpr, + importType: importType, + expressionType: expressionType, + typeofExpr: typeofExpr, + literalArr: literalArr, + literalMap: literalMap, + unary: unary, + not: not, + fn: fn, + ifStmt: ifStmt, + taggedTemplate: taggedTemplate, + literal: literal, + localizedString: localizedString, + isNull: isNull }); /** @@ -3074,32 +2524,31 @@ * change the referenced expression. */ class FixupExpression extends Expression { - constructor(resolved) { - super(resolved.type); - this.resolved = resolved; - this.original = resolved; - } - visitExpression(visitor, context) { - if (context === KEY_CONTEXT) { - // When producing a key we want to traverse the constant not the - // variable used to refer to it. - return this.original.visitExpression(visitor, context); - } else { - return this.resolved.visitExpression(visitor, context); - } - } - isEquivalent(e) { - return ( - e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved) - ); - } - isConstant() { - return true; - } - fixup(expression) { - this.resolved = expression; - this.shared = true; - } + constructor(resolved) { + super(resolved.type); + this.resolved = resolved; + this.original = resolved; + } + visitExpression(visitor, context) { + if (context === KEY_CONTEXT) { + // When producing a key we want to traverse the constant not the + // variable used to refer to it. + return this.original.visitExpression(visitor, context); + } + else { + return this.resolved.visitExpression(visitor, context); + } + } + isEquivalent(e) { + return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved); + } + isConstant() { + return true; + } + fixup(expression) { + this.resolved = expression; + this.shared = true; + } } /** * A constant pool allows a code emitter to share constant in an output context. @@ -3107,147 +2556,118 @@ * The constant pool also supports sharing access to ivy definitions references. */ class ConstantPool { - constructor(isClosureCompilerEnabled = false) { - this.isClosureCompilerEnabled = isClosureCompilerEnabled; - this.statements = []; - this.literals = new Map(); - this.literalFactories = new Map(); - this.nextNameIndex = 0; - } - getConstLiteral(literal, forceShared) { - if ( - (literal instanceof LiteralExpr && !isLongStringLiteral(literal)) || - literal instanceof FixupExpression - ) { - // Do no put simple literals into the constant pool or try to produce a constant for a - // reference to a constant. - return literal; - } - const key = this.keyOf(literal); - let fixup = this.literals.get(key); - let newValue = false; - if (!fixup) { - fixup = new FixupExpression(literal); - this.literals.set(key, fixup); - newValue = true; - } - if ((!newValue && !fixup.shared) || (newValue && forceShared)) { - // Replace the expression with a variable - const name = this.freshName(); - let definition; - let usage; - if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) { - // For string literals, Closure will **always** inline the string at - // **all** usages, duplicating it each time. For large strings, this - // unnecessarily bloats bundle size. To work around this restriction, we - // wrap the string in a function, and call that function for each usage. - // This tricks Closure into using inline logic for functions instead of - // string literals. Function calls are only inlined if the body is small - // enough to be worth it. By doing this, very large strings will be - // shared across multiple usages, rather than duplicating the string at - // each usage site. - // - // const myStr = function() { return "very very very long string"; }; - // const usage1 = myStr(); - // const usage2 = myStr(); - definition = variable(name).set( - new FunctionExpr( - [], // Params. - [ - // Statements. - new ReturnStatement(literal), - ] - ) - ); - usage = variable(name).callFn([]); - } else { - // Just declare and use the variable directly, without a function call - // indirection. This saves a few bytes and avoids an unnecessary call. - definition = variable(name).set(literal); - usage = variable(name); - } - this.statements.push( - definition.toDeclStmt(INFERRED_TYPE, exports.StmtModifier.Final) - ); - fixup.fixup(usage); - } - return fixup; - } - getLiteralFactory(literal) { - // Create a pure function that builds an array of a mix of constant and variable expressions - if (literal instanceof LiteralArrayExpr) { - const argumentsForKey = literal.entries.map((e) => - e.isConstant() ? e : UNKNOWN_VALUE_KEY - ); - const key = this.keyOf(literalArr(argumentsForKey)); - return this._getLiteralFactory(key, literal.entries, (entries) => - literalArr(entries) - ); - } else { - const expressionForKey = literalMap( - literal.entries.map((e) => ({ - key: e.key, - value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY, - quoted: e.quoted, - })) - ); - const key = this.keyOf(expressionForKey); - return this._getLiteralFactory( - key, - literal.entries.map((e) => e.value), - (entries) => - literalMap( - entries.map((value, index) => ({ - key: literal.entries[index].key, - value, - quoted: literal.entries[index].quoted, - })) - ) - ); - } - } - _getLiteralFactory(key, values, resultMap) { - let literalFactory = this.literalFactories.get(key); - const literalFactoryArguments = values.filter((e) => !e.isConstant()); - if (!literalFactory) { - const resultExpressions = values.map((e, index) => - e.isConstant() ? this.getConstLiteral(e, true) : variable(`a${index}`) - ); - const parameters = resultExpressions - .filter(isVariable) - .map((e) => new FnParam(e.name, DYNAMIC_TYPE)); - const pureFunctionDeclaration = fn( - parameters, - [new ReturnStatement(resultMap(resultExpressions))], - INFERRED_TYPE - ); - const name = this.freshName(); - this.statements.push( - variable(name) - .set(pureFunctionDeclaration) - .toDeclStmt(INFERRED_TYPE, exports.StmtModifier.Final) - ); - literalFactory = variable(name); - this.literalFactories.set(key, literalFactory); - } - return { literalFactory, literalFactoryArguments }; - } - /** - * Produce a unique name. - * - * The name might be unique among different prefixes if any of the prefixes end in - * a digit so the prefix should be a constant string (not based on user input) and - * must not end in a digit. - */ - uniqueName(prefix) { - return `${prefix}${this.nextNameIndex++}`; - } - freshName() { - return this.uniqueName(CONSTANT_PREFIX); - } - keyOf(expression) { - return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT); - } + constructor(isClosureCompilerEnabled = false) { + this.isClosureCompilerEnabled = isClosureCompilerEnabled; + this.statements = []; + this.literals = new Map(); + this.literalFactories = new Map(); + this.nextNameIndex = 0; + } + getConstLiteral(literal, forceShared) { + if ((literal instanceof LiteralExpr && !isLongStringLiteral(literal)) || + literal instanceof FixupExpression) { + // Do no put simple literals into the constant pool or try to produce a constant for a + // reference to a constant. + return literal; + } + const key = this.keyOf(literal); + let fixup = this.literals.get(key); + let newValue = false; + if (!fixup) { + fixup = new FixupExpression(literal); + this.literals.set(key, fixup); + newValue = true; + } + if ((!newValue && !fixup.shared) || (newValue && forceShared)) { + // Replace the expression with a variable + const name = this.freshName(); + let definition; + let usage; + if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) { + // For string literals, Closure will **always** inline the string at + // **all** usages, duplicating it each time. For large strings, this + // unnecessarily bloats bundle size. To work around this restriction, we + // wrap the string in a function, and call that function for each usage. + // This tricks Closure into using inline logic for functions instead of + // string literals. Function calls are only inlined if the body is small + // enough to be worth it. By doing this, very large strings will be + // shared across multiple usages, rather than duplicating the string at + // each usage site. + // + // const myStr = function() { return "very very very long string"; }; + // const usage1 = myStr(); + // const usage2 = myStr(); + definition = variable(name).set(new FunctionExpr([], // Params. + [ + // Statements. + new ReturnStatement(literal), + ])); + usage = variable(name).callFn([]); + } + else { + // Just declare and use the variable directly, without a function call + // indirection. This saves a few bytes and avoids an unnecessary call. + definition = variable(name).set(literal); + usage = variable(name); + } + this.statements.push(definition.toDeclStmt(INFERRED_TYPE, exports.StmtModifier.Final)); + fixup.fixup(usage); + } + return fixup; + } + getLiteralFactory(literal) { + // Create a pure function that builds an array of a mix of constant and variable expressions + if (literal instanceof LiteralArrayExpr) { + const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY); + const key = this.keyOf(literalArr(argumentsForKey)); + return this._getLiteralFactory(key, literal.entries, entries => literalArr(entries)); + } + else { + const expressionForKey = literalMap(literal.entries.map(e => ({ + key: e.key, + value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY, + quoted: e.quoted + }))); + const key = this.keyOf(expressionForKey); + return this._getLiteralFactory(key, literal.entries.map(e => e.value), entries => literalMap(entries.map((value, index) => ({ + key: literal.entries[index].key, + value, + quoted: literal.entries[index].quoted + })))); + } + } + _getLiteralFactory(key, values, resultMap) { + let literalFactory = this.literalFactories.get(key); + const literalFactoryArguments = values.filter((e => !e.isConstant())); + if (!literalFactory) { + const resultExpressions = values.map((e, index) => e.isConstant() ? this.getConstLiteral(e, true) : variable(`a${index}`)); + const parameters = resultExpressions.filter(isVariable).map(e => new FnParam(e.name, DYNAMIC_TYPE)); + const pureFunctionDeclaration = fn(parameters, [new ReturnStatement(resultMap(resultExpressions))], INFERRED_TYPE); + const name = this.freshName(); + this.statements.push(variable(name) + .set(pureFunctionDeclaration) + .toDeclStmt(INFERRED_TYPE, exports.StmtModifier.Final)); + literalFactory = variable(name); + this.literalFactories.set(key, literalFactory); + } + return { literalFactory, literalFactoryArguments }; + } + /** + * Produce a unique name. + * + * The name might be unique among different prefixes if any of the prefixes end in + * a digit so the prefix should be a constant string (not based on user input) and + * must not end in a digit. + */ + uniqueName(prefix) { + return `${prefix}${this.nextNameIndex++}`; + } + freshName() { + return this.uniqueName(CONSTANT_PREFIX); + } + keyOf(expression) { + return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT); + } } /** * Visitor used to determine if 2 expressions are equivalent and can be shared in the @@ -3256,71 +2676,60 @@ * When the id (string) generated by the visitor is equal, expressions are considered equivalent. */ class KeyVisitor { - constructor() { - this.visitWrappedNodeExpr = invalid$1; - this.visitWriteVarExpr = invalid$1; - this.visitWriteKeyExpr = invalid$1; - this.visitWritePropExpr = invalid$1; - this.visitInvokeFunctionExpr = invalid$1; - this.visitTaggedTemplateExpr = invalid$1; - this.visitInstantiateExpr = invalid$1; - this.visitConditionalExpr = invalid$1; - this.visitNotExpr = invalid$1; - this.visitAssertNotNullExpr = invalid$1; - this.visitCastExpr = invalid$1; - this.visitFunctionExpr = invalid$1; - this.visitUnaryOperatorExpr = invalid$1; - this.visitBinaryOperatorExpr = invalid$1; - this.visitReadPropExpr = invalid$1; - this.visitReadKeyExpr = invalid$1; - this.visitCommaExpr = invalid$1; - this.visitLocalizedString = invalid$1; - } - visitLiteralExpr(ast) { - return `${ - typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value - }`; - } - visitLiteralArrayExpr(ast, context) { - return `[${ast.entries - .map((entry) => entry.visitExpression(this, context)) - .join(',')}]`; - } - visitLiteralMapExpr(ast, context) { - const mapKey = (entry) => { - const quote = entry.quoted ? '"' : ''; - return `${quote}${entry.key}${quote}`; - }; - const mapEntry = (entry) => - `${mapKey(entry)}:${entry.value.visitExpression(this, context)}`; - return `{${ast.entries.map(mapEntry).join(',')}`; - } - visitExternalExpr(ast) { - return ast.value.moduleName - ? `EX:${ast.value.moduleName}:${ast.value.name}` - : `EX:${ast.value.runtime.name}`; - } - visitReadVarExpr(node) { - return `VAR:${node.name}`; - } - visitTypeofExpr(node, context) { - return `TYPEOF:${node.expr.visitExpression(this, context)}`; - } + constructor() { + this.visitWrappedNodeExpr = invalid$1; + this.visitWriteVarExpr = invalid$1; + this.visitWriteKeyExpr = invalid$1; + this.visitWritePropExpr = invalid$1; + this.visitInvokeFunctionExpr = invalid$1; + this.visitTaggedTemplateExpr = invalid$1; + this.visitInstantiateExpr = invalid$1; + this.visitConditionalExpr = invalid$1; + this.visitNotExpr = invalid$1; + this.visitAssertNotNullExpr = invalid$1; + this.visitCastExpr = invalid$1; + this.visitFunctionExpr = invalid$1; + this.visitUnaryOperatorExpr = invalid$1; + this.visitBinaryOperatorExpr = invalid$1; + this.visitReadPropExpr = invalid$1; + this.visitReadKeyExpr = invalid$1; + this.visitCommaExpr = invalid$1; + this.visitLocalizedString = invalid$1; + } + visitLiteralExpr(ast) { + return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`; + } + visitLiteralArrayExpr(ast, context) { + return `[${ast.entries.map(entry => entry.visitExpression(this, context)).join(',')}]`; + } + visitLiteralMapExpr(ast, context) { + const mapKey = (entry) => { + const quote = entry.quoted ? '"' : ''; + return `${quote}${entry.key}${quote}`; + }; + const mapEntry = (entry) => `${mapKey(entry)}:${entry.value.visitExpression(this, context)}`; + return `{${ast.entries.map(mapEntry).join(',')}`; + } + visitExternalExpr(ast) { + return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` : + `EX:${ast.value.runtime.name}`; + } + visitReadVarExpr(node) { + return `VAR:${node.name}`; + } + visitTypeofExpr(node, context) { + return `TYPEOF:${node.expr.visitExpression(this, context)}`; + } } function invalid$1(arg) { - throw new Error( - `Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}` - ); + throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`); } function isVariable(e) { - return e instanceof ReadVarExpr; + return e instanceof ReadVarExpr; } function isLongStringLiteral(expr) { - return ( - expr instanceof LiteralExpr && - typeof expr.value === 'string' && - expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS - ); + return expr instanceof LiteralExpr && typeof expr.value === 'string' && + expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS; } /** @@ -3331,7 +2740,8 @@ * found in the LICENSE file at https://angular.io/license */ const CORE = '@angular/core'; - class Identifiers {} + class Identifiers { + } /* Methods */ Identifiers.NEW_METHOD = 'factory'; Identifiers.TRANSFORM_METHOD = 'transform'; @@ -3345,175 +2755,52 @@ Identifiers.elementStart = { name: 'ɵɵelementStart', moduleName: CORE }; Identifiers.elementEnd = { name: 'ɵɵelementEnd', moduleName: CORE }; Identifiers.advance = { name: 'ɵɵadvance', moduleName: CORE }; - Identifiers.syntheticHostProperty = { - name: 'ɵɵsyntheticHostProperty', - moduleName: CORE, - }; - Identifiers.syntheticHostListener = { - name: 'ɵɵsyntheticHostListener', - moduleName: CORE, - }; + Identifiers.syntheticHostProperty = { name: 'ɵɵsyntheticHostProperty', moduleName: CORE }; + Identifiers.syntheticHostListener = { name: 'ɵɵsyntheticHostListener', moduleName: CORE }; Identifiers.attribute = { name: 'ɵɵattribute', moduleName: CORE }; - Identifiers.attributeInterpolate1 = { - name: 'ɵɵattributeInterpolate1', - moduleName: CORE, - }; - Identifiers.attributeInterpolate2 = { - name: 'ɵɵattributeInterpolate2', - moduleName: CORE, - }; - Identifiers.attributeInterpolate3 = { - name: 'ɵɵattributeInterpolate3', - moduleName: CORE, - }; - Identifiers.attributeInterpolate4 = { - name: 'ɵɵattributeInterpolate4', - moduleName: CORE, - }; - Identifiers.attributeInterpolate5 = { - name: 'ɵɵattributeInterpolate5', - moduleName: CORE, - }; - Identifiers.attributeInterpolate6 = { - name: 'ɵɵattributeInterpolate6', - moduleName: CORE, - }; - Identifiers.attributeInterpolate7 = { - name: 'ɵɵattributeInterpolate7', - moduleName: CORE, - }; - Identifiers.attributeInterpolate8 = { - name: 'ɵɵattributeInterpolate8', - moduleName: CORE, - }; - Identifiers.attributeInterpolateV = { - name: 'ɵɵattributeInterpolateV', - moduleName: CORE, - }; + Identifiers.attributeInterpolate1 = { name: 'ɵɵattributeInterpolate1', moduleName: CORE }; + Identifiers.attributeInterpolate2 = { name: 'ɵɵattributeInterpolate2', moduleName: CORE }; + Identifiers.attributeInterpolate3 = { name: 'ɵɵattributeInterpolate3', moduleName: CORE }; + Identifiers.attributeInterpolate4 = { name: 'ɵɵattributeInterpolate4', moduleName: CORE }; + Identifiers.attributeInterpolate5 = { name: 'ɵɵattributeInterpolate5', moduleName: CORE }; + Identifiers.attributeInterpolate6 = { name: 'ɵɵattributeInterpolate6', moduleName: CORE }; + Identifiers.attributeInterpolate7 = { name: 'ɵɵattributeInterpolate7', moduleName: CORE }; + Identifiers.attributeInterpolate8 = { name: 'ɵɵattributeInterpolate8', moduleName: CORE }; + Identifiers.attributeInterpolateV = { name: 'ɵɵattributeInterpolateV', moduleName: CORE }; Identifiers.classProp = { name: 'ɵɵclassProp', moduleName: CORE }; - Identifiers.elementContainerStart = { - name: 'ɵɵelementContainerStart', - moduleName: CORE, - }; - Identifiers.elementContainerEnd = { - name: 'ɵɵelementContainerEnd', - moduleName: CORE, - }; - Identifiers.elementContainer = { - name: 'ɵɵelementContainer', - moduleName: CORE, - }; + Identifiers.elementContainerStart = { name: 'ɵɵelementContainerStart', moduleName: CORE }; + Identifiers.elementContainerEnd = { name: 'ɵɵelementContainerEnd', moduleName: CORE }; + Identifiers.elementContainer = { name: 'ɵɵelementContainer', moduleName: CORE }; Identifiers.styleMap = { name: 'ɵɵstyleMap', moduleName: CORE }; - Identifiers.styleMapInterpolate1 = { - name: 'ɵɵstyleMapInterpolate1', - moduleName: CORE, - }; - Identifiers.styleMapInterpolate2 = { - name: 'ɵɵstyleMapInterpolate2', - moduleName: CORE, - }; - Identifiers.styleMapInterpolate3 = { - name: 'ɵɵstyleMapInterpolate3', - moduleName: CORE, - }; - Identifiers.styleMapInterpolate4 = { - name: 'ɵɵstyleMapInterpolate4', - moduleName: CORE, - }; - Identifiers.styleMapInterpolate5 = { - name: 'ɵɵstyleMapInterpolate5', - moduleName: CORE, - }; - Identifiers.styleMapInterpolate6 = { - name: 'ɵɵstyleMapInterpolate6', - moduleName: CORE, - }; - Identifiers.styleMapInterpolate7 = { - name: 'ɵɵstyleMapInterpolate7', - moduleName: CORE, - }; - Identifiers.styleMapInterpolate8 = { - name: 'ɵɵstyleMapInterpolate8', - moduleName: CORE, - }; - Identifiers.styleMapInterpolateV = { - name: 'ɵɵstyleMapInterpolateV', - moduleName: CORE, - }; + Identifiers.styleMapInterpolate1 = { name: 'ɵɵstyleMapInterpolate1', moduleName: CORE }; + Identifiers.styleMapInterpolate2 = { name: 'ɵɵstyleMapInterpolate2', moduleName: CORE }; + Identifiers.styleMapInterpolate3 = { name: 'ɵɵstyleMapInterpolate3', moduleName: CORE }; + Identifiers.styleMapInterpolate4 = { name: 'ɵɵstyleMapInterpolate4', moduleName: CORE }; + Identifiers.styleMapInterpolate5 = { name: 'ɵɵstyleMapInterpolate5', moduleName: CORE }; + Identifiers.styleMapInterpolate6 = { name: 'ɵɵstyleMapInterpolate6', moduleName: CORE }; + Identifiers.styleMapInterpolate7 = { name: 'ɵɵstyleMapInterpolate7', moduleName: CORE }; + Identifiers.styleMapInterpolate8 = { name: 'ɵɵstyleMapInterpolate8', moduleName: CORE }; + Identifiers.styleMapInterpolateV = { name: 'ɵɵstyleMapInterpolateV', moduleName: CORE }; Identifiers.classMap = { name: 'ɵɵclassMap', moduleName: CORE }; - Identifiers.classMapInterpolate1 = { - name: 'ɵɵclassMapInterpolate1', - moduleName: CORE, - }; - Identifiers.classMapInterpolate2 = { - name: 'ɵɵclassMapInterpolate2', - moduleName: CORE, - }; - Identifiers.classMapInterpolate3 = { - name: 'ɵɵclassMapInterpolate3', - moduleName: CORE, - }; - Identifiers.classMapInterpolate4 = { - name: 'ɵɵclassMapInterpolate4', - moduleName: CORE, - }; - Identifiers.classMapInterpolate5 = { - name: 'ɵɵclassMapInterpolate5', - moduleName: CORE, - }; - Identifiers.classMapInterpolate6 = { - name: 'ɵɵclassMapInterpolate6', - moduleName: CORE, - }; - Identifiers.classMapInterpolate7 = { - name: 'ɵɵclassMapInterpolate7', - moduleName: CORE, - }; - Identifiers.classMapInterpolate8 = { - name: 'ɵɵclassMapInterpolate8', - moduleName: CORE, - }; - Identifiers.classMapInterpolateV = { - name: 'ɵɵclassMapInterpolateV', - moduleName: CORE, - }; + Identifiers.classMapInterpolate1 = { name: 'ɵɵclassMapInterpolate1', moduleName: CORE }; + Identifiers.classMapInterpolate2 = { name: 'ɵɵclassMapInterpolate2', moduleName: CORE }; + Identifiers.classMapInterpolate3 = { name: 'ɵɵclassMapInterpolate3', moduleName: CORE }; + Identifiers.classMapInterpolate4 = { name: 'ɵɵclassMapInterpolate4', moduleName: CORE }; + Identifiers.classMapInterpolate5 = { name: 'ɵɵclassMapInterpolate5', moduleName: CORE }; + Identifiers.classMapInterpolate6 = { name: 'ɵɵclassMapInterpolate6', moduleName: CORE }; + Identifiers.classMapInterpolate7 = { name: 'ɵɵclassMapInterpolate7', moduleName: CORE }; + Identifiers.classMapInterpolate8 = { name: 'ɵɵclassMapInterpolate8', moduleName: CORE }; + Identifiers.classMapInterpolateV = { name: 'ɵɵclassMapInterpolateV', moduleName: CORE }; Identifiers.styleProp = { name: 'ɵɵstyleProp', moduleName: CORE }; - Identifiers.stylePropInterpolate1 = { - name: 'ɵɵstylePropInterpolate1', - moduleName: CORE, - }; - Identifiers.stylePropInterpolate2 = { - name: 'ɵɵstylePropInterpolate2', - moduleName: CORE, - }; - Identifiers.stylePropInterpolate3 = { - name: 'ɵɵstylePropInterpolate3', - moduleName: CORE, - }; - Identifiers.stylePropInterpolate4 = { - name: 'ɵɵstylePropInterpolate4', - moduleName: CORE, - }; - Identifiers.stylePropInterpolate5 = { - name: 'ɵɵstylePropInterpolate5', - moduleName: CORE, - }; - Identifiers.stylePropInterpolate6 = { - name: 'ɵɵstylePropInterpolate6', - moduleName: CORE, - }; - Identifiers.stylePropInterpolate7 = { - name: 'ɵɵstylePropInterpolate7', - moduleName: CORE, - }; - Identifiers.stylePropInterpolate8 = { - name: 'ɵɵstylePropInterpolate8', - moduleName: CORE, - }; - Identifiers.stylePropInterpolateV = { - name: 'ɵɵstylePropInterpolateV', - moduleName: CORE, - }; + Identifiers.stylePropInterpolate1 = { name: 'ɵɵstylePropInterpolate1', moduleName: CORE }; + Identifiers.stylePropInterpolate2 = { name: 'ɵɵstylePropInterpolate2', moduleName: CORE }; + Identifiers.stylePropInterpolate3 = { name: 'ɵɵstylePropInterpolate3', moduleName: CORE }; + Identifiers.stylePropInterpolate4 = { name: 'ɵɵstylePropInterpolate4', moduleName: CORE }; + Identifiers.stylePropInterpolate5 = { name: 'ɵɵstylePropInterpolate5', moduleName: CORE }; + Identifiers.stylePropInterpolate6 = { name: 'ɵɵstylePropInterpolate6', moduleName: CORE }; + Identifiers.stylePropInterpolate7 = { name: 'ɵɵstylePropInterpolate7', moduleName: CORE }; + Identifiers.stylePropInterpolate8 = { name: 'ɵɵstylePropInterpolate8', moduleName: CORE }; + Identifiers.stylePropInterpolateV = { name: 'ɵɵstylePropInterpolateV', moduleName: CORE }; Identifiers.nextContext = { name: 'ɵɵnextContext', moduleName: CORE }; Identifiers.resetView = { name: 'ɵɵresetView', moduleName: CORE }; Identifiers.templateCreate = { name: 'ɵɵtemplate', moduleName: CORE }; @@ -3522,42 +2809,15 @@ Identifiers.disableBindings = { name: 'ɵɵdisableBindings', moduleName: CORE }; Identifiers.getCurrentView = { name: 'ɵɵgetCurrentView', moduleName: CORE }; Identifiers.textInterpolate = { name: 'ɵɵtextInterpolate', moduleName: CORE }; - Identifiers.textInterpolate1 = { - name: 'ɵɵtextInterpolate1', - moduleName: CORE, - }; - Identifiers.textInterpolate2 = { - name: 'ɵɵtextInterpolate2', - moduleName: CORE, - }; - Identifiers.textInterpolate3 = { - name: 'ɵɵtextInterpolate3', - moduleName: CORE, - }; - Identifiers.textInterpolate4 = { - name: 'ɵɵtextInterpolate4', - moduleName: CORE, - }; - Identifiers.textInterpolate5 = { - name: 'ɵɵtextInterpolate5', - moduleName: CORE, - }; - Identifiers.textInterpolate6 = { - name: 'ɵɵtextInterpolate6', - moduleName: CORE, - }; - Identifiers.textInterpolate7 = { - name: 'ɵɵtextInterpolate7', - moduleName: CORE, - }; - Identifiers.textInterpolate8 = { - name: 'ɵɵtextInterpolate8', - moduleName: CORE, - }; - Identifiers.textInterpolateV = { - name: 'ɵɵtextInterpolateV', - moduleName: CORE, - }; + Identifiers.textInterpolate1 = { name: 'ɵɵtextInterpolate1', moduleName: CORE }; + Identifiers.textInterpolate2 = { name: 'ɵɵtextInterpolate2', moduleName: CORE }; + Identifiers.textInterpolate3 = { name: 'ɵɵtextInterpolate3', moduleName: CORE }; + Identifiers.textInterpolate4 = { name: 'ɵɵtextInterpolate4', moduleName: CORE }; + Identifiers.textInterpolate5 = { name: 'ɵɵtextInterpolate5', moduleName: CORE }; + Identifiers.textInterpolate6 = { name: 'ɵɵtextInterpolate6', moduleName: CORE }; + Identifiers.textInterpolate7 = { name: 'ɵɵtextInterpolate7', moduleName: CORE }; + Identifiers.textInterpolate8 = { name: 'ɵɵtextInterpolate8', moduleName: CORE }; + Identifiers.textInterpolateV = { name: 'ɵɵtextInterpolateV', moduleName: CORE }; Identifiers.restoreView = { name: 'ɵɵrestoreView', moduleName: CORE }; Identifiers.pureFunction0 = { name: 'ɵɵpureFunction0', moduleName: CORE }; Identifiers.pureFunction1 = { name: 'ɵɵpureFunction1', moduleName: CORE }; @@ -3576,46 +2836,16 @@ Identifiers.pipeBindV = { name: 'ɵɵpipeBindV', moduleName: CORE }; Identifiers.hostProperty = { name: 'ɵɵhostProperty', moduleName: CORE }; Identifiers.property = { name: 'ɵɵproperty', moduleName: CORE }; - Identifiers.propertyInterpolate = { - name: 'ɵɵpropertyInterpolate', - moduleName: CORE, - }; - Identifiers.propertyInterpolate1 = { - name: 'ɵɵpropertyInterpolate1', - moduleName: CORE, - }; - Identifiers.propertyInterpolate2 = { - name: 'ɵɵpropertyInterpolate2', - moduleName: CORE, - }; - Identifiers.propertyInterpolate3 = { - name: 'ɵɵpropertyInterpolate3', - moduleName: CORE, - }; - Identifiers.propertyInterpolate4 = { - name: 'ɵɵpropertyInterpolate4', - moduleName: CORE, - }; - Identifiers.propertyInterpolate5 = { - name: 'ɵɵpropertyInterpolate5', - moduleName: CORE, - }; - Identifiers.propertyInterpolate6 = { - name: 'ɵɵpropertyInterpolate6', - moduleName: CORE, - }; - Identifiers.propertyInterpolate7 = { - name: 'ɵɵpropertyInterpolate7', - moduleName: CORE, - }; - Identifiers.propertyInterpolate8 = { - name: 'ɵɵpropertyInterpolate8', - moduleName: CORE, - }; - Identifiers.propertyInterpolateV = { - name: 'ɵɵpropertyInterpolateV', - moduleName: CORE, - }; + Identifiers.propertyInterpolate = { name: 'ɵɵpropertyInterpolate', moduleName: CORE }; + Identifiers.propertyInterpolate1 = { name: 'ɵɵpropertyInterpolate1', moduleName: CORE }; + Identifiers.propertyInterpolate2 = { name: 'ɵɵpropertyInterpolate2', moduleName: CORE }; + Identifiers.propertyInterpolate3 = { name: 'ɵɵpropertyInterpolate3', moduleName: CORE }; + Identifiers.propertyInterpolate4 = { name: 'ɵɵpropertyInterpolate4', moduleName: CORE }; + Identifiers.propertyInterpolate5 = { name: 'ɵɵpropertyInterpolate5', moduleName: CORE }; + Identifiers.propertyInterpolate6 = { name: 'ɵɵpropertyInterpolate6', moduleName: CORE }; + Identifiers.propertyInterpolate7 = { name: 'ɵɵpropertyInterpolate7', moduleName: CORE }; + Identifiers.propertyInterpolate8 = { name: 'ɵɵpropertyInterpolate8', moduleName: CORE }; + Identifiers.propertyInterpolateV = { name: 'ɵɵpropertyInterpolateV', moduleName: CORE }; Identifiers.i18n = { name: 'ɵɵi18n', moduleName: CORE }; Identifiers.i18nAttributes = { name: 'ɵɵi18nAttributes', moduleName: CORE }; Identifiers.i18nExp = { name: 'ɵɵi18nExp', moduleName: CORE }; @@ -3631,170 +2861,89 @@ Identifiers.injectAttribute = { name: 'ɵɵinjectAttribute', moduleName: CORE }; Identifiers.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE }; Identifiers.invalidFactory = { name: 'ɵɵinvalidFactory', moduleName: CORE }; - Identifiers.invalidFactoryDep = { - name: 'ɵɵinvalidFactoryDep', - moduleName: CORE, - }; - Identifiers.templateRefExtractor = { - name: 'ɵɵtemplateRefExtractor', - moduleName: CORE, - }; + Identifiers.invalidFactoryDep = { name: 'ɵɵinvalidFactoryDep', moduleName: CORE }; + Identifiers.templateRefExtractor = { name: 'ɵɵtemplateRefExtractor', moduleName: CORE }; Identifiers.forwardRef = { name: 'forwardRef', moduleName: CORE }; - Identifiers.resolveForwardRef = { - name: 'resolveForwardRef', - moduleName: CORE, - }; - Identifiers.ɵɵdefineInjectable = { - name: 'ɵɵdefineInjectable', - moduleName: CORE, - }; - Identifiers.declareInjectable = { - name: 'ɵɵngDeclareInjectable', - moduleName: CORE, - }; - Identifiers.InjectableDeclaration = { - name: 'ɵɵInjectableDeclaration', - moduleName: CORE, - }; + Identifiers.resolveForwardRef = { name: 'resolveForwardRef', moduleName: CORE }; + Identifiers.ɵɵdefineInjectable = { name: 'ɵɵdefineInjectable', moduleName: CORE }; + Identifiers.declareInjectable = { name: 'ɵɵngDeclareInjectable', moduleName: CORE }; + Identifiers.InjectableDeclaration = { name: 'ɵɵInjectableDeclaration', moduleName: CORE }; Identifiers.resolveWindow = { name: 'ɵɵresolveWindow', moduleName: CORE }; Identifiers.resolveDocument = { name: 'ɵɵresolveDocument', moduleName: CORE }; Identifiers.resolveBody = { name: 'ɵɵresolveBody', moduleName: CORE }; Identifiers.defineComponent = { name: 'ɵɵdefineComponent', moduleName: CORE }; - Identifiers.declareComponent = { - name: 'ɵɵngDeclareComponent', - moduleName: CORE, - }; - Identifiers.setComponentScope = { - name: 'ɵɵsetComponentScope', - moduleName: CORE, - }; + Identifiers.declareComponent = { name: 'ɵɵngDeclareComponent', moduleName: CORE }; + Identifiers.setComponentScope = { name: 'ɵɵsetComponentScope', moduleName: CORE }; Identifiers.ChangeDetectionStrategy = { - name: 'ChangeDetectionStrategy', - moduleName: CORE, + name: 'ChangeDetectionStrategy', + moduleName: CORE, }; Identifiers.ViewEncapsulation = { - name: 'ViewEncapsulation', - moduleName: CORE, + name: 'ViewEncapsulation', + moduleName: CORE, }; Identifiers.ComponentDeclaration = { - name: 'ɵɵComponentDeclaration', - moduleName: CORE, + name: 'ɵɵComponentDeclaration', + moduleName: CORE, }; Identifiers.FactoryDeclaration = { - name: 'ɵɵFactoryDeclaration', - moduleName: CORE, + name: 'ɵɵFactoryDeclaration', + moduleName: CORE, }; Identifiers.declareFactory = { name: 'ɵɵngDeclareFactory', moduleName: CORE }; Identifiers.FactoryTarget = { name: 'ɵɵFactoryTarget', moduleName: CORE }; Identifiers.defineDirective = { name: 'ɵɵdefineDirective', moduleName: CORE }; - Identifiers.declareDirective = { - name: 'ɵɵngDeclareDirective', - moduleName: CORE, - }; + Identifiers.declareDirective = { name: 'ɵɵngDeclareDirective', moduleName: CORE }; Identifiers.DirectiveDeclaration = { - name: 'ɵɵDirectiveDeclaration', - moduleName: CORE, + name: 'ɵɵDirectiveDeclaration', + moduleName: CORE, }; Identifiers.InjectorDef = { name: 'ɵɵInjectorDef', moduleName: CORE }; - Identifiers.InjectorDeclaration = { - name: 'ɵɵInjectorDeclaration', - moduleName: CORE, - }; + Identifiers.InjectorDeclaration = { name: 'ɵɵInjectorDeclaration', moduleName: CORE }; Identifiers.defineInjector = { name: 'ɵɵdefineInjector', moduleName: CORE }; - Identifiers.declareInjector = { - name: 'ɵɵngDeclareInjector', - moduleName: CORE, - }; + Identifiers.declareInjector = { name: 'ɵɵngDeclareInjector', moduleName: CORE }; Identifiers.NgModuleDeclaration = { - name: 'ɵɵNgModuleDeclaration', - moduleName: CORE, + name: 'ɵɵNgModuleDeclaration', + moduleName: CORE, }; Identifiers.ModuleWithProviders = { - name: 'ModuleWithProviders', - moduleName: CORE, + name: 'ModuleWithProviders', + moduleName: CORE, }; Identifiers.defineNgModule = { name: 'ɵɵdefineNgModule', moduleName: CORE }; - Identifiers.declareNgModule = { - name: 'ɵɵngDeclareNgModule', - moduleName: CORE, - }; - Identifiers.setNgModuleScope = { - name: 'ɵɵsetNgModuleScope', - moduleName: CORE, - }; - Identifiers.registerNgModuleType = { - name: 'ɵɵregisterNgModuleType', - moduleName: CORE, - }; + Identifiers.declareNgModule = { name: 'ɵɵngDeclareNgModule', moduleName: CORE }; + Identifiers.setNgModuleScope = { name: 'ɵɵsetNgModuleScope', moduleName: CORE }; + Identifiers.registerNgModuleType = { name: 'ɵɵregisterNgModuleType', moduleName: CORE }; Identifiers.PipeDeclaration = { name: 'ɵɵPipeDeclaration', moduleName: CORE }; Identifiers.definePipe = { name: 'ɵɵdefinePipe', moduleName: CORE }; Identifiers.declarePipe = { name: 'ɵɵngDeclarePipe', moduleName: CORE }; - Identifiers.declareClassMetadata = { - name: 'ɵɵngDeclareClassMetadata', - moduleName: CORE, - }; - Identifiers.setClassMetadata = { - name: 'ɵsetClassMetadata', - moduleName: CORE, - }; + Identifiers.declareClassMetadata = { name: 'ɵɵngDeclareClassMetadata', moduleName: CORE }; + Identifiers.setClassMetadata = { name: 'ɵsetClassMetadata', moduleName: CORE }; Identifiers.queryRefresh = { name: 'ɵɵqueryRefresh', moduleName: CORE }; Identifiers.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE }; Identifiers.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE }; Identifiers.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE }; - Identifiers.NgOnChangesFeature = { - name: 'ɵɵNgOnChangesFeature', - moduleName: CORE, - }; - Identifiers.InheritDefinitionFeature = { - name: 'ɵɵInheritDefinitionFeature', - moduleName: CORE, - }; - Identifiers.CopyDefinitionFeature = { - name: 'ɵɵCopyDefinitionFeature', - moduleName: CORE, - }; - Identifiers.StandaloneFeature = { - name: 'ɵɵStandaloneFeature', - moduleName: CORE, - }; - Identifiers.ProvidersFeature = { - name: 'ɵɵProvidersFeature', - moduleName: CORE, - }; - Identifiers.HostDirectivesFeature = { - name: 'ɵɵHostDirectivesFeature', - moduleName: CORE, - }; + Identifiers.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE }; + Identifiers.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE }; + Identifiers.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE }; + Identifiers.StandaloneFeature = { name: 'ɵɵStandaloneFeature', moduleName: CORE }; + Identifiers.ProvidersFeature = { name: 'ɵɵProvidersFeature', moduleName: CORE }; + Identifiers.HostDirectivesFeature = { name: 'ɵɵHostDirectivesFeature', moduleName: CORE }; Identifiers.listener = { name: 'ɵɵlistener', moduleName: CORE }; Identifiers.getInheritedFactory = { - name: 'ɵɵgetInheritedFactory', - moduleName: CORE, + name: 'ɵɵgetInheritedFactory', + moduleName: CORE, }; // sanitization-related functions Identifiers.sanitizeHtml = { name: 'ɵɵsanitizeHtml', moduleName: CORE }; Identifiers.sanitizeStyle = { name: 'ɵɵsanitizeStyle', moduleName: CORE }; - Identifiers.sanitizeResourceUrl = { - name: 'ɵɵsanitizeResourceUrl', - moduleName: CORE, - }; + Identifiers.sanitizeResourceUrl = { name: 'ɵɵsanitizeResourceUrl', moduleName: CORE }; Identifiers.sanitizeScript = { name: 'ɵɵsanitizeScript', moduleName: CORE }; Identifiers.sanitizeUrl = { name: 'ɵɵsanitizeUrl', moduleName: CORE }; - Identifiers.sanitizeUrlOrResourceUrl = { - name: 'ɵɵsanitizeUrlOrResourceUrl', - moduleName: CORE, - }; - Identifiers.trustConstantHtml = { - name: 'ɵɵtrustConstantHtml', - moduleName: CORE, - }; - Identifiers.trustConstantResourceUrl = { - name: 'ɵɵtrustConstantResourceUrl', - moduleName: CORE, - }; - Identifiers.validateIframeAttribute = { - name: 'ɵɵvalidateIframeAttribute', - moduleName: CORE, - }; + Identifiers.sanitizeUrlOrResourceUrl = { name: 'ɵɵsanitizeUrlOrResourceUrl', moduleName: CORE }; + Identifiers.trustConstantHtml = { name: 'ɵɵtrustConstantHtml', moduleName: CORE }; + Identifiers.trustConstantResourceUrl = { name: 'ɵɵtrustConstantResourceUrl', moduleName: CORE }; + Identifiers.validateIframeAttribute = { name: 'ɵɵvalidateIframeAttribute', moduleName: CORE }; /** * @license @@ -3807,150 +2956,142 @@ const VERSION$1 = 3; const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,'; class SourceMapGenerator { - constructor(file = null) { - this.file = file; - this.sourcesContent = new Map(); - this.lines = []; - this.lastCol0 = 0; - this.hasMappings = false; - } - // The content is `null` when the content is expected to be loaded using the URL - addSource(url, content = null) { - if (!this.sourcesContent.has(url)) { - this.sourcesContent.set(url, content); - } - return this; - } - addLine() { - this.lines.push([]); - this.lastCol0 = 0; - return this; - } - addMapping(col0, sourceUrl, sourceLine0, sourceCol0) { - if (!this.currentLine) { - throw new Error(`A line must be added before mappings can be added`); - } - if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) { - throw new Error(`Unknown source file "${sourceUrl}"`); - } - if (col0 == null) { - throw new Error(`The column in the generated code must be provided`); - } - if (col0 < this.lastCol0) { - throw new Error(`Mapping should be added in output order`); - } - if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) { - throw new Error( - `The source location must be provided when a source url is provided` - ); - } - this.hasMappings = true; - this.lastCol0 = col0; - this.currentLine.push({ col0, sourceUrl, sourceLine0, sourceCol0 }); - return this; - } - /** - * @internal strip this from published d.ts files due to - * https://github.com/microsoft/TypeScript/issues/36216 - */ - get currentLine() { - return this.lines.slice(-1)[0]; - } - toJSON() { - if (!this.hasMappings) { - return null; - } - const sourcesIndex = new Map(); - const sources = []; - const sourcesContent = []; - Array.from(this.sourcesContent.keys()).forEach((url, i) => { - sourcesIndex.set(url, i); - sources.push(url); - sourcesContent.push(this.sourcesContent.get(url) || null); - }); - let mappings = ''; - let lastCol0 = 0; - let lastSourceIndex = 0; - let lastSourceLine0 = 0; - let lastSourceCol0 = 0; - this.lines.forEach((segments) => { - lastCol0 = 0; - mappings += segments - .map((segment) => { - // zero-based starting column of the line in the generated code - let segAsStr = toBase64VLQ(segment.col0 - lastCol0); - lastCol0 = segment.col0; - if (segment.sourceUrl != null) { - // zero-based index into the “sources” list - segAsStr += toBase64VLQ( - sourcesIndex.get(segment.sourceUrl) - lastSourceIndex - ); - lastSourceIndex = sourcesIndex.get(segment.sourceUrl); - // the zero-based starting line in the original source - segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0); - lastSourceLine0 = segment.sourceLine0; - // the zero-based starting column in the original source - segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0); - lastSourceCol0 = segment.sourceCol0; - } - return segAsStr; - }) - .join(','); - mappings += ';'; - }); - mappings = mappings.slice(0, -1); - return { - file: this.file || '', - version: VERSION$1, - sourceRoot: '', - sources: sources, - sourcesContent: sourcesContent, - mappings: mappings, - }; - } - toJsComment() { - return this.hasMappings - ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) - : ''; - } + constructor(file = null) { + this.file = file; + this.sourcesContent = new Map(); + this.lines = []; + this.lastCol0 = 0; + this.hasMappings = false; + } + // The content is `null` when the content is expected to be loaded using the URL + addSource(url, content = null) { + if (!this.sourcesContent.has(url)) { + this.sourcesContent.set(url, content); + } + return this; + } + addLine() { + this.lines.push([]); + this.lastCol0 = 0; + return this; + } + addMapping(col0, sourceUrl, sourceLine0, sourceCol0) { + if (!this.currentLine) { + throw new Error(`A line must be added before mappings can be added`); + } + if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) { + throw new Error(`Unknown source file "${sourceUrl}"`); + } + if (col0 == null) { + throw new Error(`The column in the generated code must be provided`); + } + if (col0 < this.lastCol0) { + throw new Error(`Mapping should be added in output order`); + } + if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) { + throw new Error(`The source location must be provided when a source url is provided`); + } + this.hasMappings = true; + this.lastCol0 = col0; + this.currentLine.push({ col0, sourceUrl, sourceLine0, sourceCol0 }); + return this; + } + /** + * @internal strip this from published d.ts files due to + * https://github.com/microsoft/TypeScript/issues/36216 + */ + get currentLine() { + return this.lines.slice(-1)[0]; + } + toJSON() { + if (!this.hasMappings) { + return null; + } + const sourcesIndex = new Map(); + const sources = []; + const sourcesContent = []; + Array.from(this.sourcesContent.keys()).forEach((url, i) => { + sourcesIndex.set(url, i); + sources.push(url); + sourcesContent.push(this.sourcesContent.get(url) || null); + }); + let mappings = ''; + let lastCol0 = 0; + let lastSourceIndex = 0; + let lastSourceLine0 = 0; + let lastSourceCol0 = 0; + this.lines.forEach(segments => { + lastCol0 = 0; + mappings += segments + .map(segment => { + // zero-based starting column of the line in the generated code + let segAsStr = toBase64VLQ(segment.col0 - lastCol0); + lastCol0 = segment.col0; + if (segment.sourceUrl != null) { + // zero-based index into the “sources” list + segAsStr += + toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex); + lastSourceIndex = sourcesIndex.get(segment.sourceUrl); + // the zero-based starting line in the original source + segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0); + lastSourceLine0 = segment.sourceLine0; + // the zero-based starting column in the original source + segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0); + lastSourceCol0 = segment.sourceCol0; + } + return segAsStr; + }) + .join(','); + mappings += ';'; + }); + mappings = mappings.slice(0, -1); + return { + 'file': this.file || '', + 'version': VERSION$1, + 'sourceRoot': '', + 'sources': sources, + 'sourcesContent': sourcesContent, + 'mappings': mappings, + }; + } + toJsComment() { + return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) : + ''; + } } function toBase64String(value) { - let b64 = ''; - const encoded = utf8Encode(value); - for (let i = 0; i < encoded.length; ) { - const i1 = encoded[i++]; - const i2 = i < encoded.length ? encoded[i++] : null; - const i3 = i < encoded.length ? encoded[i++] : null; - b64 += toBase64Digit(i1 >> 2); - b64 += toBase64Digit(((i1 & 3) << 4) | (i2 === null ? 0 : i2 >> 4)); - b64 += - i2 === null - ? '=' - : toBase64Digit(((i2 & 15) << 2) | (i3 === null ? 0 : i3 >> 6)); - b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63); - } - return b64; + let b64 = ''; + const encoded = utf8Encode(value); + for (let i = 0; i < encoded.length;) { + const i1 = encoded[i++]; + const i2 = i < encoded.length ? encoded[i++] : null; + const i3 = i < encoded.length ? encoded[i++] : null; + b64 += toBase64Digit(i1 >> 2); + b64 += toBase64Digit(((i1 & 3) << 4) | (i2 === null ? 0 : i2 >> 4)); + b64 += i2 === null ? '=' : toBase64Digit(((i2 & 15) << 2) | (i3 === null ? 0 : i3 >> 6)); + b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63); + } + return b64; } function toBase64VLQ(value) { - value = value < 0 ? (-value << 1) + 1 : value << 1; - let out = ''; - do { - let digit = value & 31; - value = value >> 5; - if (value > 0) { - digit = digit | 32; - } - out += toBase64Digit(digit); - } while (value > 0); - return out; - } - const B64_DIGITS = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + value = value < 0 ? ((-value) << 1) + 1 : value << 1; + let out = ''; + do { + let digit = value & 31; + value = value >> 5; + if (value > 0) { + digit = digit | 32; + } + out += toBase64Digit(digit); + } while (value > 0); + return out; + } + const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; function toBase64Digit(value) { - if (value < 0 || value >= 64) { - throw new Error(`Can only encode value in the range [0, 63]`); - } - return B64_DIGITS[value]; + if (value < 0 || value >= 64) { + throw new Error(`Can only encode value in the range [0, 63]`); + } + return B64_DIGITS[value]; } /** @@ -3964,521 +3105,501 @@ const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i; const _INDENT_WITH = ' '; class _EmittedLine { - constructor(indent) { - this.indent = indent; - this.partsLength = 0; - this.parts = []; - this.srcSpans = []; - } + constructor(indent) { + this.indent = indent; + this.partsLength = 0; + this.parts = []; + this.srcSpans = []; + } } class EmitterVisitorContext { - constructor(_indent) { - this._indent = _indent; - this._lines = [new _EmittedLine(_indent)]; - } - static createRoot() { - return new EmitterVisitorContext(0); - } - /** - * @internal strip this from published d.ts files due to - * https://github.com/microsoft/TypeScript/issues/36216 - */ - get _currentLine() { - return this._lines[this._lines.length - 1]; - } - println(from, lastPart = '') { - this.print(from || null, lastPart, true); - } - lineIsEmpty() { - return this._currentLine.parts.length === 0; - } - lineLength() { - return ( - this._currentLine.indent * _INDENT_WITH.length + - this._currentLine.partsLength - ); - } - print(from, part, newLine = false) { - if (part.length > 0) { - this._currentLine.parts.push(part); - this._currentLine.partsLength += part.length; - this._currentLine.srcSpans.push((from && from.sourceSpan) || null); - } - if (newLine) { - this._lines.push(new _EmittedLine(this._indent)); - } - } - removeEmptyLastLine() { - if (this.lineIsEmpty()) { - this._lines.pop(); - } - } - incIndent() { - this._indent++; - if (this.lineIsEmpty()) { - this._currentLine.indent = this._indent; - } - } - decIndent() { - this._indent--; - if (this.lineIsEmpty()) { - this._currentLine.indent = this._indent; - } - } - toSource() { - return this.sourceLines - .map((l) => - l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '' - ) - .join('\n'); - } - toSourceMapGenerator(genFilePath, startsAtLine = 0) { - const map = new SourceMapGenerator(genFilePath); - let firstOffsetMapped = false; - const mapFirstOffsetIfNeeded = () => { - if (!firstOffsetMapped) { - // Add a single space so that tools won't try to load the file from disk. - // Note: We are using virtual urls like `ng:///`, so we have to - // provide a content here. - map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0); - firstOffsetMapped = true; - } - }; - for (let i = 0; i < startsAtLine; i++) { - map.addLine(); - mapFirstOffsetIfNeeded(); - } - this.sourceLines.forEach((line, lineIdx) => { - map.addLine(); - const spans = line.srcSpans; - const parts = line.parts; - let col0 = line.indent * _INDENT_WITH.length; - let spanIdx = 0; - // skip leading parts without source spans - while (spanIdx < spans.length && !spans[spanIdx]) { - col0 += parts[spanIdx].length; - spanIdx++; - } - if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) { - firstOffsetMapped = true; - } else { - mapFirstOffsetIfNeeded(); - } - while (spanIdx < spans.length) { - const span = spans[spanIdx]; - const source = span.start.file; - const sourceLine = span.start.line; - const sourceCol = span.start.col; - map - .addSource(source.url, source.content) - .addMapping(col0, source.url, sourceLine, sourceCol); - col0 += parts[spanIdx].length; - spanIdx++; - // assign parts without span or the same span to the previous segment - while ( - spanIdx < spans.length && - (span === spans[spanIdx] || !spans[spanIdx]) - ) { - col0 += parts[spanIdx].length; - spanIdx++; + constructor(_indent) { + this._indent = _indent; + this._lines = [new _EmittedLine(_indent)]; + } + static createRoot() { + return new EmitterVisitorContext(0); + } + /** + * @internal strip this from published d.ts files due to + * https://github.com/microsoft/TypeScript/issues/36216 + */ + get _currentLine() { + return this._lines[this._lines.length - 1]; + } + println(from, lastPart = '') { + this.print(from || null, lastPart, true); + } + lineIsEmpty() { + return this._currentLine.parts.length === 0; + } + lineLength() { + return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength; + } + print(from, part, newLine = false) { + if (part.length > 0) { + this._currentLine.parts.push(part); + this._currentLine.partsLength += part.length; + this._currentLine.srcSpans.push(from && from.sourceSpan || null); + } + if (newLine) { + this._lines.push(new _EmittedLine(this._indent)); } - } - }); - return map; - } - spanOf(line, column) { - const emittedLine = this._lines[line]; - if (emittedLine) { - let columnsLeft = column - _createIndent(emittedLine.indent).length; - for ( - let partIndex = 0; - partIndex < emittedLine.parts.length; - partIndex++ - ) { - const part = emittedLine.parts[partIndex]; - if (part.length > columnsLeft) { - return emittedLine.srcSpans[partIndex]; - } - columnsLeft -= part.length; - } } - return null; - } - /** - * @internal strip this from published d.ts files due to - * https://github.com/microsoft/TypeScript/issues/36216 - */ - get sourceLines() { - if ( - this._lines.length && - this._lines[this._lines.length - 1].parts.length === 0 - ) { - return this._lines.slice(0, -1); - } - return this._lines; - } + removeEmptyLastLine() { + if (this.lineIsEmpty()) { + this._lines.pop(); + } + } + incIndent() { + this._indent++; + if (this.lineIsEmpty()) { + this._currentLine.indent = this._indent; + } + } + decIndent() { + this._indent--; + if (this.lineIsEmpty()) { + this._currentLine.indent = this._indent; + } + } + toSource() { + return this.sourceLines + .map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '') + .join('\n'); + } + toSourceMapGenerator(genFilePath, startsAtLine = 0) { + const map = new SourceMapGenerator(genFilePath); + let firstOffsetMapped = false; + const mapFirstOffsetIfNeeded = () => { + if (!firstOffsetMapped) { + // Add a single space so that tools won't try to load the file from disk. + // Note: We are using virtual urls like `ng:///`, so we have to + // provide a content here. + map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0); + firstOffsetMapped = true; + } + }; + for (let i = 0; i < startsAtLine; i++) { + map.addLine(); + mapFirstOffsetIfNeeded(); + } + this.sourceLines.forEach((line, lineIdx) => { + map.addLine(); + const spans = line.srcSpans; + const parts = line.parts; + let col0 = line.indent * _INDENT_WITH.length; + let spanIdx = 0; + // skip leading parts without source spans + while (spanIdx < spans.length && !spans[spanIdx]) { + col0 += parts[spanIdx].length; + spanIdx++; + } + if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) { + firstOffsetMapped = true; + } + else { + mapFirstOffsetIfNeeded(); + } + while (spanIdx < spans.length) { + const span = spans[spanIdx]; + const source = span.start.file; + const sourceLine = span.start.line; + const sourceCol = span.start.col; + map.addSource(source.url, source.content) + .addMapping(col0, source.url, sourceLine, sourceCol); + col0 += parts[spanIdx].length; + spanIdx++; + // assign parts without span or the same span to the previous segment + while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) { + col0 += parts[spanIdx].length; + spanIdx++; + } + } + }); + return map; + } + spanOf(line, column) { + const emittedLine = this._lines[line]; + if (emittedLine) { + let columnsLeft = column - _createIndent(emittedLine.indent).length; + for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) { + const part = emittedLine.parts[partIndex]; + if (part.length > columnsLeft) { + return emittedLine.srcSpans[partIndex]; + } + columnsLeft -= part.length; + } + } + return null; + } + /** + * @internal strip this from published d.ts files due to + * https://github.com/microsoft/TypeScript/issues/36216 + */ + get sourceLines() { + if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) { + return this._lines.slice(0, -1); + } + return this._lines; + } } class AbstractEmitterVisitor { - constructor(_escapeDollarInStrings) { - this._escapeDollarInStrings = _escapeDollarInStrings; - } - printLeadingComments(stmt, ctx) { - if (stmt.leadingComments === undefined) { - return; - } - for (const comment of stmt.leadingComments) { - if (comment instanceof JSDocComment) { - ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline); - } else { - if (comment.multiline) { - ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline); - } else { - comment.text.split('\n').forEach((line) => { - ctx.println(stmt, `// ${line}`); - }); + constructor(_escapeDollarInStrings) { + this._escapeDollarInStrings = _escapeDollarInStrings; + } + printLeadingComments(stmt, ctx) { + if (stmt.leadingComments === undefined) { + return; + } + for (const comment of stmt.leadingComments) { + if (comment instanceof JSDocComment) { + ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline); + } + else { + if (comment.multiline) { + ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline); + } + else { + comment.text.split('\n').forEach((line) => { + ctx.println(stmt, `// ${line}`); + }); + } + } } - } } - } - visitExpressionStmt(stmt, ctx) { - this.printLeadingComments(stmt, ctx); - stmt.expr.visitExpression(this, ctx); - ctx.println(stmt, ';'); - return null; - } - visitReturnStmt(stmt, ctx) { - this.printLeadingComments(stmt, ctx); - ctx.print(stmt, `return `); - stmt.value.visitExpression(this, ctx); - ctx.println(stmt, ';'); - return null; - } - visitIfStmt(stmt, ctx) { - this.printLeadingComments(stmt, ctx); - ctx.print(stmt, `if (`); - stmt.condition.visitExpression(this, ctx); - ctx.print(stmt, `) {`); - const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0; - if (stmt.trueCase.length <= 1 && !hasElseCase) { - ctx.print(stmt, ` `); - this.visitAllStatements(stmt.trueCase, ctx); - ctx.removeEmptyLastLine(); - ctx.print(stmt, ` `); - } else { - ctx.println(); - ctx.incIndent(); - this.visitAllStatements(stmt.trueCase, ctx); - ctx.decIndent(); - if (hasElseCase) { - ctx.println(stmt, `} else {`); - ctx.incIndent(); - this.visitAllStatements(stmt.falseCase, ctx); - ctx.decIndent(); - } + visitExpressionStmt(stmt, ctx) { + this.printLeadingComments(stmt, ctx); + stmt.expr.visitExpression(this, ctx); + ctx.println(stmt, ';'); + return null; } - ctx.println(stmt, `}`); - return null; - } - visitWriteVarExpr(expr, ctx) { - const lineWasEmpty = ctx.lineIsEmpty(); - if (!lineWasEmpty) { - ctx.print(expr, '('); + visitReturnStmt(stmt, ctx) { + this.printLeadingComments(stmt, ctx); + ctx.print(stmt, `return `); + stmt.value.visitExpression(this, ctx); + ctx.println(stmt, ';'); + return null; } - ctx.print(expr, `${expr.name} = `); - expr.value.visitExpression(this, ctx); - if (!lineWasEmpty) { - ctx.print(expr, ')'); + visitIfStmt(stmt, ctx) { + this.printLeadingComments(stmt, ctx); + ctx.print(stmt, `if (`); + stmt.condition.visitExpression(this, ctx); + ctx.print(stmt, `) {`); + const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0; + if (stmt.trueCase.length <= 1 && !hasElseCase) { + ctx.print(stmt, ` `); + this.visitAllStatements(stmt.trueCase, ctx); + ctx.removeEmptyLastLine(); + ctx.print(stmt, ` `); + } + else { + ctx.println(); + ctx.incIndent(); + this.visitAllStatements(stmt.trueCase, ctx); + ctx.decIndent(); + if (hasElseCase) { + ctx.println(stmt, `} else {`); + ctx.incIndent(); + this.visitAllStatements(stmt.falseCase, ctx); + ctx.decIndent(); + } + } + ctx.println(stmt, `}`); + return null; } - return null; - } - visitWriteKeyExpr(expr, ctx) { - const lineWasEmpty = ctx.lineIsEmpty(); - if (!lineWasEmpty) { - ctx.print(expr, '('); - } - expr.receiver.visitExpression(this, ctx); - ctx.print(expr, `[`); - expr.index.visitExpression(this, ctx); - ctx.print(expr, `] = `); - expr.value.visitExpression(this, ctx); - if (!lineWasEmpty) { - ctx.print(expr, ')'); + visitWriteVarExpr(expr, ctx) { + const lineWasEmpty = ctx.lineIsEmpty(); + if (!lineWasEmpty) { + ctx.print(expr, '('); + } + ctx.print(expr, `${expr.name} = `); + expr.value.visitExpression(this, ctx); + if (!lineWasEmpty) { + ctx.print(expr, ')'); + } + return null; } - return null; - } - visitWritePropExpr(expr, ctx) { - const lineWasEmpty = ctx.lineIsEmpty(); - if (!lineWasEmpty) { - ctx.print(expr, '('); - } - expr.receiver.visitExpression(this, ctx); - ctx.print(expr, `.${expr.name} = `); - expr.value.visitExpression(this, ctx); - if (!lineWasEmpty) { - ctx.print(expr, ')'); + visitWriteKeyExpr(expr, ctx) { + const lineWasEmpty = ctx.lineIsEmpty(); + if (!lineWasEmpty) { + ctx.print(expr, '('); + } + expr.receiver.visitExpression(this, ctx); + ctx.print(expr, `[`); + expr.index.visitExpression(this, ctx); + ctx.print(expr, `] = `); + expr.value.visitExpression(this, ctx); + if (!lineWasEmpty) { + ctx.print(expr, ')'); + } + return null; } - return null; - } - visitInvokeFunctionExpr(expr, ctx) { - expr.fn.visitExpression(this, ctx); - ctx.print(expr, `(`); - this.visitAllExpressions(expr.args, ctx, ','); - ctx.print(expr, `)`); - return null; - } - visitTaggedTemplateExpr(expr, ctx) { - expr.tag.visitExpression(this, ctx); - ctx.print(expr, '`' + expr.template.elements[0].rawText); - for (let i = 1; i < expr.template.elements.length; i++) { - ctx.print(expr, '${'); - expr.template.expressions[i - 1].visitExpression(this, ctx); - ctx.print(expr, `}${expr.template.elements[i].rawText}`); - } - ctx.print(expr, '`'); - return null; - } - visitWrappedNodeExpr(ast, ctx) { - throw new Error('Abstract emitter cannot visit WrappedNodeExpr.'); - } - visitTypeofExpr(expr, ctx) { - ctx.print(expr, 'typeof '); - expr.expr.visitExpression(this, ctx); - } - visitReadVarExpr(ast, ctx) { - ctx.print(ast, ast.name); - return null; - } - visitInstantiateExpr(ast, ctx) { - ctx.print(ast, `new `); - ast.classExpr.visitExpression(this, ctx); - ctx.print(ast, `(`); - this.visitAllExpressions(ast.args, ctx, ','); - ctx.print(ast, `)`); - return null; - } - visitLiteralExpr(ast, ctx) { - const value = ast.value; - if (typeof value === 'string') { - ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings)); - } else { - ctx.print(ast, `${value}`); + visitWritePropExpr(expr, ctx) { + const lineWasEmpty = ctx.lineIsEmpty(); + if (!lineWasEmpty) { + ctx.print(expr, '('); + } + expr.receiver.visitExpression(this, ctx); + ctx.print(expr, `.${expr.name} = `); + expr.value.visitExpression(this, ctx); + if (!lineWasEmpty) { + ctx.print(expr, ')'); + } + return null; } - return null; - } - visitLocalizedString(ast, ctx) { - const head = ast.serializeI18nHead(); - ctx.print(ast, '$localize `' + head.raw); - for (let i = 1; i < ast.messageParts.length; i++) { - ctx.print(ast, '${'); - ast.expressions[i - 1].visitExpression(this, ctx); - ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`); - } - ctx.print(ast, '`'); - return null; - } - visitConditionalExpr(ast, ctx) { - ctx.print(ast, `(`); - ast.condition.visitExpression(this, ctx); - ctx.print(ast, '? '); - ast.trueCase.visitExpression(this, ctx); - ctx.print(ast, ': '); - ast.falseCase.visitExpression(this, ctx); - ctx.print(ast, `)`); - return null; - } - visitNotExpr(ast, ctx) { - ctx.print(ast, '!'); - ast.condition.visitExpression(this, ctx); - return null; - } - visitUnaryOperatorExpr(ast, ctx) { - let opStr; - switch (ast.operator) { - case exports.UnaryOperator.Plus: - opStr = '+'; - break; - case exports.UnaryOperator.Minus: - opStr = '-'; - break; - default: - throw new Error(`Unknown operator ${ast.operator}`); - } - if (ast.parens) ctx.print(ast, `(`); - ctx.print(ast, opStr); - ast.expr.visitExpression(this, ctx); - if (ast.parens) ctx.print(ast, `)`); - return null; - } - visitBinaryOperatorExpr(ast, ctx) { - let opStr; - switch (ast.operator) { - case exports.BinaryOperator.Equals: - opStr = '=='; - break; - case exports.BinaryOperator.Identical: - opStr = '==='; - break; - case exports.BinaryOperator.NotEquals: - opStr = '!='; - break; - case exports.BinaryOperator.NotIdentical: - opStr = '!=='; - break; - case exports.BinaryOperator.And: - opStr = '&&'; - break; - case exports.BinaryOperator.BitwiseAnd: - opStr = '&'; - break; - case exports.BinaryOperator.Or: - opStr = '||'; - break; - case exports.BinaryOperator.Plus: - opStr = '+'; - break; - case exports.BinaryOperator.Minus: - opStr = '-'; - break; - case exports.BinaryOperator.Divide: - opStr = '/'; - break; - case exports.BinaryOperator.Multiply: - opStr = '*'; - break; - case exports.BinaryOperator.Modulo: - opStr = '%'; - break; - case exports.BinaryOperator.Lower: - opStr = '<'; - break; - case exports.BinaryOperator.LowerEquals: - opStr = '<='; - break; - case exports.BinaryOperator.Bigger: - opStr = '>'; - break; - case exports.BinaryOperator.BiggerEquals: - opStr = '>='; - break; - case exports.BinaryOperator.NullishCoalesce: - opStr = '??'; - break; - default: - throw new Error(`Unknown operator ${ast.operator}`); - } - if (ast.parens) ctx.print(ast, `(`); - ast.lhs.visitExpression(this, ctx); - ctx.print(ast, ` ${opStr} `); - ast.rhs.visitExpression(this, ctx); - if (ast.parens) ctx.print(ast, `)`); - return null; - } - visitReadPropExpr(ast, ctx) { - ast.receiver.visitExpression(this, ctx); - ctx.print(ast, `.`); - ctx.print(ast, ast.name); - return null; - } - visitReadKeyExpr(ast, ctx) { - ast.receiver.visitExpression(this, ctx); - ctx.print(ast, `[`); - ast.index.visitExpression(this, ctx); - ctx.print(ast, `]`); - return null; - } - visitLiteralArrayExpr(ast, ctx) { - ctx.print(ast, `[`); - this.visitAllExpressions(ast.entries, ctx, ','); - ctx.print(ast, `]`); - return null; - } - visitLiteralMapExpr(ast, ctx) { - ctx.print(ast, `{`); - this.visitAllObjects( - (entry) => { - ctx.print( - ast, - `${escapeIdentifier( - entry.key, - this._escapeDollarInStrings, - entry.quoted - )}:` - ); - entry.value.visitExpression(this, ctx); - }, - ast.entries, - ctx, - ',' - ); - ctx.print(ast, `}`); - return null; - } - visitCommaExpr(ast, ctx) { - ctx.print(ast, '('); - this.visitAllExpressions(ast.parts, ctx, ','); - ctx.print(ast, ')'); - return null; - } - visitAllExpressions(expressions, ctx, separator) { - this.visitAllObjects( - (expr) => expr.visitExpression(this, ctx), - expressions, - ctx, - separator - ); - } - visitAllObjects(handler, expressions, ctx, separator) { - let incrementedIndent = false; - for (let i = 0; i < expressions.length; i++) { - if (i > 0) { - if (ctx.lineLength() > 80) { - ctx.print(null, separator, true); - if (!incrementedIndent) { + visitInvokeFunctionExpr(expr, ctx) { + expr.fn.visitExpression(this, ctx); + ctx.print(expr, `(`); + this.visitAllExpressions(expr.args, ctx, ','); + ctx.print(expr, `)`); + return null; + } + visitTaggedTemplateExpr(expr, ctx) { + expr.tag.visitExpression(this, ctx); + ctx.print(expr, '`' + expr.template.elements[0].rawText); + for (let i = 1; i < expr.template.elements.length; i++) { + ctx.print(expr, '${'); + expr.template.expressions[i - 1].visitExpression(this, ctx); + ctx.print(expr, `}${expr.template.elements[i].rawText}`); + } + ctx.print(expr, '`'); + return null; + } + visitWrappedNodeExpr(ast, ctx) { + throw new Error('Abstract emitter cannot visit WrappedNodeExpr.'); + } + visitTypeofExpr(expr, ctx) { + ctx.print(expr, 'typeof '); + expr.expr.visitExpression(this, ctx); + } + visitReadVarExpr(ast, ctx) { + ctx.print(ast, ast.name); + return null; + } + visitInstantiateExpr(ast, ctx) { + ctx.print(ast, `new `); + ast.classExpr.visitExpression(this, ctx); + ctx.print(ast, `(`); + this.visitAllExpressions(ast.args, ctx, ','); + ctx.print(ast, `)`); + return null; + } + visitLiteralExpr(ast, ctx) { + const value = ast.value; + if (typeof value === 'string') { + ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings)); + } + else { + ctx.print(ast, `${value}`); + } + return null; + } + visitLocalizedString(ast, ctx) { + const head = ast.serializeI18nHead(); + ctx.print(ast, '$localize `' + head.raw); + for (let i = 1; i < ast.messageParts.length; i++) { + ctx.print(ast, '${'); + ast.expressions[i - 1].visitExpression(this, ctx); + ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`); + } + ctx.print(ast, '`'); + return null; + } + visitConditionalExpr(ast, ctx) { + ctx.print(ast, `(`); + ast.condition.visitExpression(this, ctx); + ctx.print(ast, '? '); + ast.trueCase.visitExpression(this, ctx); + ctx.print(ast, ': '); + ast.falseCase.visitExpression(this, ctx); + ctx.print(ast, `)`); + return null; + } + visitNotExpr(ast, ctx) { + ctx.print(ast, '!'); + ast.condition.visitExpression(this, ctx); + return null; + } + visitUnaryOperatorExpr(ast, ctx) { + let opStr; + switch (ast.operator) { + case exports.UnaryOperator.Plus: + opStr = '+'; + break; + case exports.UnaryOperator.Minus: + opStr = '-'; + break; + default: + throw new Error(`Unknown operator ${ast.operator}`); + } + if (ast.parens) + ctx.print(ast, `(`); + ctx.print(ast, opStr); + ast.expr.visitExpression(this, ctx); + if (ast.parens) + ctx.print(ast, `)`); + return null; + } + visitBinaryOperatorExpr(ast, ctx) { + let opStr; + switch (ast.operator) { + case exports.BinaryOperator.Equals: + opStr = '=='; + break; + case exports.BinaryOperator.Identical: + opStr = '==='; + break; + case exports.BinaryOperator.NotEquals: + opStr = '!='; + break; + case exports.BinaryOperator.NotIdentical: + opStr = '!=='; + break; + case exports.BinaryOperator.And: + opStr = '&&'; + break; + case exports.BinaryOperator.BitwiseAnd: + opStr = '&'; + break; + case exports.BinaryOperator.Or: + opStr = '||'; + break; + case exports.BinaryOperator.Plus: + opStr = '+'; + break; + case exports.BinaryOperator.Minus: + opStr = '-'; + break; + case exports.BinaryOperator.Divide: + opStr = '/'; + break; + case exports.BinaryOperator.Multiply: + opStr = '*'; + break; + case exports.BinaryOperator.Modulo: + opStr = '%'; + break; + case exports.BinaryOperator.Lower: + opStr = '<'; + break; + case exports.BinaryOperator.LowerEquals: + opStr = '<='; + break; + case exports.BinaryOperator.Bigger: + opStr = '>'; + break; + case exports.BinaryOperator.BiggerEquals: + opStr = '>='; + break; + case exports.BinaryOperator.NullishCoalesce: + opStr = '??'; + break; + default: + throw new Error(`Unknown operator ${ast.operator}`); + } + if (ast.parens) + ctx.print(ast, `(`); + ast.lhs.visitExpression(this, ctx); + ctx.print(ast, ` ${opStr} `); + ast.rhs.visitExpression(this, ctx); + if (ast.parens) + ctx.print(ast, `)`); + return null; + } + visitReadPropExpr(ast, ctx) { + ast.receiver.visitExpression(this, ctx); + ctx.print(ast, `.`); + ctx.print(ast, ast.name); + return null; + } + visitReadKeyExpr(ast, ctx) { + ast.receiver.visitExpression(this, ctx); + ctx.print(ast, `[`); + ast.index.visitExpression(this, ctx); + ctx.print(ast, `]`); + return null; + } + visitLiteralArrayExpr(ast, ctx) { + ctx.print(ast, `[`); + this.visitAllExpressions(ast.entries, ctx, ','); + ctx.print(ast, `]`); + return null; + } + visitLiteralMapExpr(ast, ctx) { + ctx.print(ast, `{`); + this.visitAllObjects(entry => { + ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`); + entry.value.visitExpression(this, ctx); + }, ast.entries, ctx, ','); + ctx.print(ast, `}`); + return null; + } + visitCommaExpr(ast, ctx) { + ctx.print(ast, '('); + this.visitAllExpressions(ast.parts, ctx, ','); + ctx.print(ast, ')'); + return null; + } + visitAllExpressions(expressions, ctx, separator) { + this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator); + } + visitAllObjects(handler, expressions, ctx, separator) { + let incrementedIndent = false; + for (let i = 0; i < expressions.length; i++) { + if (i > 0) { + if (ctx.lineLength() > 80) { + ctx.print(null, separator, true); + if (!incrementedIndent) { + // continuation are marked with double indent. + ctx.incIndent(); + ctx.incIndent(); + incrementedIndent = true; + } + } + else { + ctx.print(null, separator, false); + } + } + handler(expressions[i]); + } + if (incrementedIndent) { // continuation are marked with double indent. - ctx.incIndent(); - ctx.incIndent(); - incrementedIndent = true; - } - } else { - ctx.print(null, separator, false); + ctx.decIndent(); + ctx.decIndent(); } - } - handler(expressions[i]); } - if (incrementedIndent) { - // continuation are marked with double indent. - ctx.decIndent(); - ctx.decIndent(); + visitAllStatements(statements, ctx) { + statements.forEach((stmt) => stmt.visitStatement(this, ctx)); } - } - visitAllStatements(statements, ctx) { - statements.forEach((stmt) => stmt.visitStatement(this, ctx)); - } } function escapeIdentifier(input, escapeDollar, alwaysQuote = true) { - if (input == null) { - return null; - } - const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => { - if (match[0] == '$') { - return escapeDollar ? '\\$' : '$'; - } else if (match[0] == '\n') { - return '\\n'; - } else if (match[0] == '\r') { - return '\\r'; - } else { - return `\\${match[0]}`; - } - }); - const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body); - return requiresQuotes ? `'${body}'` : body; + if (input == null) { + return null; + } + const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => { + if (match[0] == '$') { + return escapeDollar ? '\\$' : '$'; + } + else if (match[0] == '\n') { + return '\\n'; + } + else if (match[0] == '\r') { + return '\\r'; + } + else { + return `\\${match[0]}`; + } + }); + const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body); + return requiresQuotes ? `'${body}'` : body; } function _createIndent(count) { - let res = ''; - for (let i = 0; i < count; i++) { - res += _INDENT_WITH; - } - return res; + let res = ''; + for (let i = 0; i < count; i++) { + res += _INDENT_WITH; + } + return res; } /** @@ -4489,70 +3610,52 @@ * found in the LICENSE file at https://angular.io/license */ function typeWithParameters(type, numParams) { - if (numParams === 0) { - return expressionType(type); - } - const params = []; - for (let i = 0; i < numParams; i++) { - params.push(DYNAMIC_TYPE); - } - return expressionType(type, undefined, params); + if (numParams === 0) { + return expressionType(type); + } + const params = []; + for (let i = 0; i < numParams; i++) { + params.push(DYNAMIC_TYPE); + } + return expressionType(type, undefined, params); } const ANIMATE_SYMBOL_PREFIX = '@'; function prepareSyntheticPropertyName(name) { - return `${ANIMATE_SYMBOL_PREFIX}${name}`; + return `${ANIMATE_SYMBOL_PREFIX}${name}`; } function prepareSyntheticListenerName(name, phase) { - return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`; + return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`; } function getSafePropertyAccessString(accessor, name) { - const escapedName = escapeIdentifier(name, false, false); - return escapedName !== name - ? `${accessor}[${escapedName}]` - : `${accessor}.${name}`; + const escapedName = escapeIdentifier(name, false, false); + return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`; } function prepareSyntheticListenerFunctionName(name, phase) { - return `animation_${name}_${phase}`; + return `animation_${name}_${phase}`; } function jitOnlyGuardedExpression(expr) { - return guardedExpression('ngJitMode', expr); + return guardedExpression('ngJitMode', expr); } function devOnlyGuardedExpression(expr) { - return guardedExpression('ngDevMode', expr); + return guardedExpression('ngDevMode', expr); } function guardedExpression(guard, expr) { - const guardExpr = new ExternalExpr({ name: guard, moduleName: null }); - const guardNotDefined = new BinaryOperatorExpr( - exports.BinaryOperator.Identical, - new TypeofExpr(guardExpr), - literal('undefined') - ); - const guardUndefinedOrTrue = new BinaryOperatorExpr( - exports.BinaryOperator.Or, - guardNotDefined, - guardExpr, - /* type */ undefined, - /* sourceSpan */ undefined, - true - ); - return new BinaryOperatorExpr( - exports.BinaryOperator.And, - guardUndefinedOrTrue, - expr - ); + const guardExpr = new ExternalExpr({ name: guard, moduleName: null }); + const guardNotDefined = new BinaryOperatorExpr(exports.BinaryOperator.Identical, new TypeofExpr(guardExpr), literal('undefined')); + const guardUndefinedOrTrue = new BinaryOperatorExpr(exports.BinaryOperator.Or, guardNotDefined, guardExpr, /* type */ undefined, + /* sourceSpan */ undefined, true); + return new BinaryOperatorExpr(exports.BinaryOperator.And, guardUndefinedOrTrue, expr); } function wrapReference(value) { - const wrapped = new WrappedNodeExpr(value); - return { value: wrapped, type: wrapped }; + const wrapped = new WrappedNodeExpr(value); + return { value: wrapped, type: wrapped }; } function refsToArray(refs, shouldForwardDeclare) { - const values = literalArr(refs.map((ref) => ref.value)); - return shouldForwardDeclare - ? fn([], [new ReturnStatement(values)]) - : values; + const values = literalArr(refs.map(ref => ref.value)); + return shouldForwardDeclare ? fn([], [new ReturnStatement(values)]) : values; } function createMayBeForwardRefExpression(expression, forwardRef) { - return { expression, forwardRef }; + return { expression, forwardRef }; } /** * Convert a `MaybeForwardRefExpression` to an `Expression`, possibly wrapping its expression in a @@ -4566,13 +3669,13 @@ * `packages/compiler/src/jit_compiler_facade.ts` for more information. */ function convertFromMaybeForwardRefExpression({ expression, forwardRef }) { - switch (forwardRef) { - case 0 /* ForwardRefHandling.None */: - case 1 /* ForwardRefHandling.Wrapped */: - return expression; - case 2 /* ForwardRefHandling.Unwrapped */: - return generateForwardRef(expression); - } + switch (forwardRef) { + case 0 /* ForwardRefHandling.None */: + case 1 /* ForwardRefHandling.Wrapped */: + return expression; + case 2 /* ForwardRefHandling.Unwrapped */: + return generateForwardRef(expression); + } } /** * Generate an expression that has the given `expr` wrapped in the following form: @@ -4582,238 +3685,200 @@ * ``` */ function generateForwardRef(expr) { - return importExpr(Identifiers.forwardRef).callFn([ - fn([], [new ReturnStatement(expr)]), - ]); + return importExpr(Identifiers.forwardRef).callFn([fn([], [new ReturnStatement(expr)])]); } var R3FactoryDelegateType; (function (R3FactoryDelegateType) { - R3FactoryDelegateType[(R3FactoryDelegateType['Class'] = 0)] = 'Class'; - R3FactoryDelegateType[(R3FactoryDelegateType['Function'] = 1)] = 'Function'; + R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class"; + R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function"; })(R3FactoryDelegateType || (R3FactoryDelegateType = {})); exports.FactoryTarget = void 0; (function (FactoryTarget) { - FactoryTarget[(FactoryTarget['Directive'] = 0)] = 'Directive'; - FactoryTarget[(FactoryTarget['Component'] = 1)] = 'Component'; - FactoryTarget[(FactoryTarget['Injectable'] = 2)] = 'Injectable'; - FactoryTarget[(FactoryTarget['Pipe'] = 3)] = 'Pipe'; - FactoryTarget[(FactoryTarget['NgModule'] = 4)] = 'NgModule'; + FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive"; + FactoryTarget[FactoryTarget["Component"] = 1] = "Component"; + FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable"; + FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe"; + FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule"; })(exports.FactoryTarget || (exports.FactoryTarget = {})); /** * Construct a factory function expression for the given `R3FactoryMetadata`. */ function compileFactoryFunction(meta) { - const t = variable('t'); - let baseFactoryVar = null; - // The type to instantiate via constructor invocation. If there is no delegated factory, meaning - // this type is always created by constructor invocation, then this is the type-to-create - // parameter provided by the user (t) if specified, or the current type if not. If there is a - // delegated factory (which is used to create the current type) then this is only the type-to- - // create parameter (t). - const typeForCtor = !isDelegatedFactoryMetadata(meta) - ? new BinaryOperatorExpr(exports.BinaryOperator.Or, t, meta.internalType) - : t; - let ctorExpr = null; - if (meta.deps !== null) { - // There is a constructor (either explicitly or implicitly defined). - if (meta.deps !== 'invalid') { - ctorExpr = new InstantiateExpr( - typeForCtor, - injectDependencies(meta.deps, meta.target) - ); - } - } else { - // There is no constructor, use the base class' factory to construct typeForCtor. - baseFactoryVar = variable(`ɵ${meta.name}_BaseFactory`); - ctorExpr = baseFactoryVar.callFn([typeForCtor]); - } - const body = []; - let retExpr = null; - function makeConditionalFactory(nonCtorExpr) { - const r = variable('r'); - body.push(r.set(NULL_EXPR).toDeclStmt()); - const ctorStmt = - ctorExpr !== null - ? r.set(ctorExpr).toStmt() - : importExpr(Identifiers.invalidFactory).callFn([]).toStmt(); - body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()])); - return r; - } - if (isDelegatedFactoryMetadata(meta)) { - // This type is created with a delegated factory. If a type parameter is not specified, call - // the factory instead. - const delegateArgs = injectDependencies(meta.delegateDeps, meta.target); - // Either call `new delegate(...)` or `delegate(...)` depending on meta.delegateType. - const factoryExpr = new ( - meta.delegateType === R3FactoryDelegateType.Class - ? InstantiateExpr - : InvokeFunctionExpr - )(meta.delegate, delegateArgs); - retExpr = makeConditionalFactory(factoryExpr); - } else if (isExpressionFactoryMetadata(meta)) { - // TODO(alxhub): decide whether to lower the value here or in the caller - retExpr = makeConditionalFactory(meta.expression); - } else { - retExpr = ctorExpr; - } - if (retExpr === null) { - // The expression cannot be formed so render an `ɵɵinvalidFactory()` call. - body.push(importExpr(Identifiers.invalidFactory).callFn([]).toStmt()); - } else if (baseFactoryVar !== null) { - // This factory uses a base factory, so call `ɵɵgetInheritedFactory()` to compute it. - const getInheritedFactoryCall = importExpr( - Identifiers.getInheritedFactory - ).callFn([meta.internalType]); - // Memoize the base factoryFn: `baseFactory || (baseFactory = ɵɵgetInheritedFactory(...))` - const baseFactory = new BinaryOperatorExpr( - exports.BinaryOperator.Or, - baseFactoryVar, - baseFactoryVar.set(getInheritedFactoryCall) - ); - body.push(new ReturnStatement(baseFactory.callFn([typeForCtor]))); - } else { - // This is straightforward factory, just return it. - body.push(new ReturnStatement(retExpr)); - } - let factoryFn = fn( - [new FnParam('t', DYNAMIC_TYPE)], - body, - INFERRED_TYPE, - undefined, - `${meta.name}_Factory` - ); - if (baseFactoryVar !== null) { - // There is a base factory variable so wrap its declaration along with the factory function into - // an IIFE. - factoryFn = fn( - [], - [ - new DeclareVarStmt(baseFactoryVar.name), - new ReturnStatement(factoryFn), - ] - ).callFn([], /* sourceSpan */ undefined, /* pure */ true); - } - return { - expression: factoryFn, - statements: [], - type: createFactoryType(meta), - }; + const t = variable('t'); + let baseFactoryVar = null; + // The type to instantiate via constructor invocation. If there is no delegated factory, meaning + // this type is always created by constructor invocation, then this is the type-to-create + // parameter provided by the user (t) if specified, or the current type if not. If there is a + // delegated factory (which is used to create the current type) then this is only the type-to- + // create parameter (t). + const typeForCtor = !isDelegatedFactoryMetadata(meta) ? + new BinaryOperatorExpr(exports.BinaryOperator.Or, t, meta.internalType) : + t; + let ctorExpr = null; + if (meta.deps !== null) { + // There is a constructor (either explicitly or implicitly defined). + if (meta.deps !== 'invalid') { + ctorExpr = new InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.target)); + } + } + else { + // There is no constructor, use the base class' factory to construct typeForCtor. + baseFactoryVar = variable(`ɵ${meta.name}_BaseFactory`); + ctorExpr = baseFactoryVar.callFn([typeForCtor]); + } + const body = []; + let retExpr = null; + function makeConditionalFactory(nonCtorExpr) { + const r = variable('r'); + body.push(r.set(NULL_EXPR).toDeclStmt()); + const ctorStmt = ctorExpr !== null ? r.set(ctorExpr).toStmt() : + importExpr(Identifiers.invalidFactory).callFn([]).toStmt(); + body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()])); + return r; + } + if (isDelegatedFactoryMetadata(meta)) { + // This type is created with a delegated factory. If a type parameter is not specified, call + // the factory instead. + const delegateArgs = injectDependencies(meta.delegateDeps, meta.target); + // Either call `new delegate(...)` or `delegate(...)` depending on meta.delegateType. + const factoryExpr = new (meta.delegateType === R3FactoryDelegateType.Class ? + InstantiateExpr : + InvokeFunctionExpr)(meta.delegate, delegateArgs); + retExpr = makeConditionalFactory(factoryExpr); + } + else if (isExpressionFactoryMetadata(meta)) { + // TODO(alxhub): decide whether to lower the value here or in the caller + retExpr = makeConditionalFactory(meta.expression); + } + else { + retExpr = ctorExpr; + } + if (retExpr === null) { + // The expression cannot be formed so render an `ɵɵinvalidFactory()` call. + body.push(importExpr(Identifiers.invalidFactory).callFn([]).toStmt()); + } + else if (baseFactoryVar !== null) { + // This factory uses a base factory, so call `ɵɵgetInheritedFactory()` to compute it. + const getInheritedFactoryCall = importExpr(Identifiers.getInheritedFactory).callFn([meta.internalType]); + // Memoize the base factoryFn: `baseFactory || (baseFactory = ɵɵgetInheritedFactory(...))` + const baseFactory = new BinaryOperatorExpr(exports.BinaryOperator.Or, baseFactoryVar, baseFactoryVar.set(getInheritedFactoryCall)); + body.push(new ReturnStatement(baseFactory.callFn([typeForCtor]))); + } + else { + // This is straightforward factory, just return it. + body.push(new ReturnStatement(retExpr)); + } + let factoryFn = fn([new FnParam('t', DYNAMIC_TYPE)], body, INFERRED_TYPE, undefined, `${meta.name}_Factory`); + if (baseFactoryVar !== null) { + // There is a base factory variable so wrap its declaration along with the factory function into + // an IIFE. + factoryFn = fn([], [ + new DeclareVarStmt(baseFactoryVar.name), new ReturnStatement(factoryFn) + ]).callFn([], /* sourceSpan */ undefined, /* pure */ true); + } + return { + expression: factoryFn, + statements: [], + type: createFactoryType(meta), + }; } function createFactoryType(meta) { - const ctorDepsType = - meta.deps !== null && meta.deps !== 'invalid' - ? createCtorDepsType(meta.deps) - : NONE_TYPE; - return expressionType( - importExpr(Identifiers.FactoryDeclaration, [ - typeWithParameters(meta.type.type, meta.typeArgumentCount), - ctorDepsType, - ]) - ); + const ctorDepsType = meta.deps !== null && meta.deps !== 'invalid' ? createCtorDepsType(meta.deps) : NONE_TYPE; + return expressionType(importExpr(Identifiers.FactoryDeclaration, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType])); } function injectDependencies(deps, target) { - return deps.map((dep, index) => - compileInjectDependency(dep, target, index) - ); + return deps.map((dep, index) => compileInjectDependency(dep, target, index)); } function compileInjectDependency(dep, target, index) { - // Interpret the dependency according to its resolved type. - if (dep.token === null) { - return importExpr(Identifiers.invalidFactoryDep).callFn([literal(index)]); - } else if (dep.attributeNameType === null) { - // Build up the injection flags according to the metadata. - const flags = - 0 /* InjectFlags.Default */ | - (dep.self ? 2 /* InjectFlags.Self */ : 0) | - (dep.skipSelf ? 4 /* InjectFlags.SkipSelf */ : 0) | - (dep.host ? 1 /* InjectFlags.Host */ : 0) | - (dep.optional ? 8 /* InjectFlags.Optional */ : 0) | - (target === exports.FactoryTarget.Pipe - ? 16 /* InjectFlags.ForPipe */ - : 0); - // If this dependency is optional or otherwise has non-default flags, then additional - // parameters describing how to inject the dependency must be passed to the inject function - // that's being used. - let flagsParam = - flags !== 0 /* InjectFlags.Default */ || dep.optional - ? literal(flags) - : null; - // Build up the arguments to the injectFn call. - const injectArgs = [dep.token]; - if (flagsParam) { - injectArgs.push(flagsParam); - } - const injectFn = getInjectFn(target); - return importExpr(injectFn).callFn(injectArgs); - } else { - // The `dep.attributeTypeName` value is defined, which indicates that this is an `@Attribute()` - // type dependency. For the generated JS we still want to use the `dep.token` value in case the - // name given for the attribute is not a string literal. For example given `@Attribute(foo())`, - // we want to generate `ɵɵinjectAttribute(foo())`. - // - // The `dep.attributeTypeName` is only actually used (in `createCtorDepType()`) to generate - // typings. - return importExpr(Identifiers.injectAttribute).callFn([dep.token]); - } + // Interpret the dependency according to its resolved type. + if (dep.token === null) { + return importExpr(Identifiers.invalidFactoryDep).callFn([literal(index)]); + } + else if (dep.attributeNameType === null) { + // Build up the injection flags according to the metadata. + const flags = 0 /* InjectFlags.Default */ | (dep.self ? 2 /* InjectFlags.Self */ : 0) | + (dep.skipSelf ? 4 /* InjectFlags.SkipSelf */ : 0) | (dep.host ? 1 /* InjectFlags.Host */ : 0) | + (dep.optional ? 8 /* InjectFlags.Optional */ : 0) | + (target === exports.FactoryTarget.Pipe ? 16 /* InjectFlags.ForPipe */ : 0); + // If this dependency is optional or otherwise has non-default flags, then additional + // parameters describing how to inject the dependency must be passed to the inject function + // that's being used. + let flagsParam = (flags !== 0 /* InjectFlags.Default */ || dep.optional) ? literal(flags) : null; + // Build up the arguments to the injectFn call. + const injectArgs = [dep.token]; + if (flagsParam) { + injectArgs.push(flagsParam); + } + const injectFn = getInjectFn(target); + return importExpr(injectFn).callFn(injectArgs); + } + else { + // The `dep.attributeTypeName` value is defined, which indicates that this is an `@Attribute()` + // type dependency. For the generated JS we still want to use the `dep.token` value in case the + // name given for the attribute is not a string literal. For example given `@Attribute(foo())`, + // we want to generate `ɵɵinjectAttribute(foo())`. + // + // The `dep.attributeTypeName` is only actually used (in `createCtorDepType()`) to generate + // typings. + return importExpr(Identifiers.injectAttribute).callFn([dep.token]); + } } function createCtorDepsType(deps) { - let hasTypes = false; - const attributeTypes = deps.map((dep) => { - const type = createCtorDepType(dep); - if (type !== null) { - hasTypes = true; - return type; - } else { - return literal(null); - } - }); - if (hasTypes) { - return expressionType(literalArr(attributeTypes)); - } else { - return NONE_TYPE; - } - } - function createCtorDepType(dep) { - const entries = []; - if (dep.attributeNameType !== null) { - entries.push({ - key: 'attribute', - value: dep.attributeNameType, - quoted: false, + let hasTypes = false; + const attributeTypes = deps.map(dep => { + const type = createCtorDepType(dep); + if (type !== null) { + hasTypes = true; + return type; + } + else { + return literal(null); + } }); - } - if (dep.optional) { - entries.push({ key: 'optional', value: literal(true), quoted: false }); - } - if (dep.host) { - entries.push({ key: 'host', value: literal(true), quoted: false }); - } - if (dep.self) { - entries.push({ key: 'self', value: literal(true), quoted: false }); - } - if (dep.skipSelf) { - entries.push({ key: 'skipSelf', value: literal(true), quoted: false }); - } - return entries.length > 0 ? literalMap(entries) : null; + if (hasTypes) { + return expressionType(literalArr(attributeTypes)); + } + else { + return NONE_TYPE; + } + } + function createCtorDepType(dep) { + const entries = []; + if (dep.attributeNameType !== null) { + entries.push({ key: 'attribute', value: dep.attributeNameType, quoted: false }); + } + if (dep.optional) { + entries.push({ key: 'optional', value: literal(true), quoted: false }); + } + if (dep.host) { + entries.push({ key: 'host', value: literal(true), quoted: false }); + } + if (dep.self) { + entries.push({ key: 'self', value: literal(true), quoted: false }); + } + if (dep.skipSelf) { + entries.push({ key: 'skipSelf', value: literal(true), quoted: false }); + } + return entries.length > 0 ? literalMap(entries) : null; } function isDelegatedFactoryMetadata(meta) { - return meta.delegateType !== undefined; + return meta.delegateType !== undefined; } function isExpressionFactoryMetadata(meta) { - return meta.expression !== undefined; + return meta.expression !== undefined; } function getInjectFn(target) { - switch (target) { - case exports.FactoryTarget.Component: - case exports.FactoryTarget.Directive: - case exports.FactoryTarget.Pipe: - return Identifiers.directiveInject; - case exports.FactoryTarget.NgModule: - case exports.FactoryTarget.Injectable: - default: - return Identifiers.inject; - } + switch (target) { + case exports.FactoryTarget.Component: + case exports.FactoryTarget.Directive: + case exports.FactoryTarget.Pipe: + return Identifiers.directiveInject; + case exports.FactoryTarget.NgModule: + case exports.FactoryTarget.Injectable: + default: + return Identifiers.inject; + } } /** @@ -4830,32 +3895,32 @@ * is true. */ class Comment$1 { - constructor(value, sourceSpan) { - this.value = value; - this.sourceSpan = sourceSpan; - } - visit(_visitor) { - throw new Error('visit() not implemented for Comment'); - } + constructor(value, sourceSpan) { + this.value = value; + this.sourceSpan = sourceSpan; + } + visit(_visitor) { + throw new Error('visit() not implemented for Comment'); + } } class Text$3 { - constructor(value, sourceSpan) { - this.value = value; - this.sourceSpan = sourceSpan; - } - visit(visitor) { - return visitor.visitText(this); - } + constructor(value, sourceSpan) { + this.value = value; + this.sourceSpan = sourceSpan; + } + visit(visitor) { + return visitor.visitText(this); + } } class BoundText { - constructor(value, sourceSpan, i18n) { - this.value = value; - this.sourceSpan = sourceSpan; - this.i18n = i18n; - } - visit(visitor) { - return visitor.visitBoundText(this); - } + constructor(value, sourceSpan, i18n) { + this.value = value; + this.sourceSpan = sourceSpan; + this.i18n = i18n; + } + visit(visitor) { + return visitor.visitBoundText(this); + } } /** * Represents a text attribute in the template. @@ -4864,263 +3929,193 @@ * `keySpan` may also not be present for synthetic attributes from ICU expansions. */ class TextAttribute { - constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) { - this.name = name; - this.value = value; - this.sourceSpan = sourceSpan; - this.keySpan = keySpan; - this.valueSpan = valueSpan; - this.i18n = i18n; - } - visit(visitor) { - return visitor.visitTextAttribute(this); - } + constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) { + this.name = name; + this.value = value; + this.sourceSpan = sourceSpan; + this.keySpan = keySpan; + this.valueSpan = valueSpan; + this.i18n = i18n; + } + visit(visitor) { + return visitor.visitTextAttribute(this); + } } class BoundAttribute { - constructor( - name, - type, - securityContext, - value, - unit, - sourceSpan, - keySpan, - valueSpan, - i18n - ) { - this.name = name; - this.type = type; - this.securityContext = securityContext; - this.value = value; - this.unit = unit; - this.sourceSpan = sourceSpan; - this.keySpan = keySpan; - this.valueSpan = valueSpan; - this.i18n = i18n; - } - static fromBoundElementProperty(prop, i18n) { - if (prop.keySpan === undefined) { - throw new Error( - `Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}` - ); - } - return new BoundAttribute( - prop.name, - prop.type, - prop.securityContext, - prop.value, - prop.unit, - prop.sourceSpan, - prop.keySpan, - prop.valueSpan, - i18n - ); - } - visit(visitor) { - return visitor.visitBoundAttribute(this); - } + constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan, i18n) { + this.name = name; + this.type = type; + this.securityContext = securityContext; + this.value = value; + this.unit = unit; + this.sourceSpan = sourceSpan; + this.keySpan = keySpan; + this.valueSpan = valueSpan; + this.i18n = i18n; + } + static fromBoundElementProperty(prop, i18n) { + if (prop.keySpan === undefined) { + throw new Error(`Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}`); + } + return new BoundAttribute(prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n); + } + visit(visitor) { + return visitor.visitBoundAttribute(this); + } } class BoundEvent { - constructor( - name, - type, - handler, - target, - phase, - sourceSpan, - handlerSpan, - keySpan - ) { - this.name = name; - this.type = type; - this.handler = handler; - this.target = target; - this.phase = phase; - this.sourceSpan = sourceSpan; - this.handlerSpan = handlerSpan; - this.keySpan = keySpan; - } - static fromParsedEvent(event) { - const target = - event.type === 0 /* ParsedEventType.Regular */ - ? event.targetOrPhase - : null; - const phase = - event.type === 1 /* ParsedEventType.Animation */ - ? event.targetOrPhase - : null; - if (event.keySpan === undefined) { - throw new Error( - `Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}` - ); - } - return new BoundEvent( - event.name, - event.type, - event.handler, - target, - phase, - event.sourceSpan, - event.handlerSpan, - event.keySpan - ); - } - visit(visitor) { - return visitor.visitBoundEvent(this); - } + constructor(name, type, handler, target, phase, sourceSpan, handlerSpan, keySpan) { + this.name = name; + this.type = type; + this.handler = handler; + this.target = target; + this.phase = phase; + this.sourceSpan = sourceSpan; + this.handlerSpan = handlerSpan; + this.keySpan = keySpan; + } + static fromParsedEvent(event) { + const target = event.type === 0 /* ParsedEventType.Regular */ ? event.targetOrPhase : null; + const phase = event.type === 1 /* ParsedEventType.Animation */ ? event.targetOrPhase : null; + if (event.keySpan === undefined) { + throw new Error(`Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}`); + } + return new BoundEvent(event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan, event.keySpan); + } + visit(visitor) { + return visitor.visitBoundEvent(this); + } } class Element$1 { - constructor( - name, - attributes, - inputs, - outputs, - children, - references, - sourceSpan, - startSourceSpan, - endSourceSpan, - i18n - ) { - this.name = name; - this.attributes = attributes; - this.inputs = inputs; - this.outputs = outputs; - this.children = children; - this.references = references; - this.sourceSpan = sourceSpan; - this.startSourceSpan = startSourceSpan; - this.endSourceSpan = endSourceSpan; - this.i18n = i18n; - } - visit(visitor) { - return visitor.visitElement(this); - } + constructor(name, attributes, inputs, outputs, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) { + this.name = name; + this.attributes = attributes; + this.inputs = inputs; + this.outputs = outputs; + this.children = children; + this.references = references; + this.sourceSpan = sourceSpan; + this.startSourceSpan = startSourceSpan; + this.endSourceSpan = endSourceSpan; + this.i18n = i18n; + } + visit(visitor) { + return visitor.visitElement(this); + } } class Template { - constructor( + constructor( // tagName is the name of the container element, if applicable. // `null` is a special case for when there is a structural directive on an `ng-template` so // the renderer can differentiate between the synthetic template and the one written in the // file. - tagName, - attributes, - inputs, - outputs, - templateAttrs, - children, - references, - variables, - sourceSpan, - startSourceSpan, - endSourceSpan, - i18n - ) { - this.tagName = tagName; - this.attributes = attributes; - this.inputs = inputs; - this.outputs = outputs; - this.templateAttrs = templateAttrs; - this.children = children; - this.references = references; - this.variables = variables; - this.sourceSpan = sourceSpan; - this.startSourceSpan = startSourceSpan; - this.endSourceSpan = endSourceSpan; - this.i18n = i18n; - } - visit(visitor) { - return visitor.visitTemplate(this); - } + tagName, attributes, inputs, outputs, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) { + this.tagName = tagName; + this.attributes = attributes; + this.inputs = inputs; + this.outputs = outputs; + this.templateAttrs = templateAttrs; + this.children = children; + this.references = references; + this.variables = variables; + this.sourceSpan = sourceSpan; + this.startSourceSpan = startSourceSpan; + this.endSourceSpan = endSourceSpan; + this.i18n = i18n; + } + visit(visitor) { + return visitor.visitTemplate(this); + } } class Content { - constructor(selector, attributes, sourceSpan, i18n) { - this.selector = selector; - this.attributes = attributes; - this.sourceSpan = sourceSpan; - this.i18n = i18n; - this.name = 'ng-content'; - } - visit(visitor) { - return visitor.visitContent(this); - } + constructor(selector, attributes, sourceSpan, i18n) { + this.selector = selector; + this.attributes = attributes; + this.sourceSpan = sourceSpan; + this.i18n = i18n; + this.name = 'ng-content'; + } + visit(visitor) { + return visitor.visitContent(this); + } } class Variable { - constructor(name, value, sourceSpan, keySpan, valueSpan) { - this.name = name; - this.value = value; - this.sourceSpan = sourceSpan; - this.keySpan = keySpan; - this.valueSpan = valueSpan; - } - visit(visitor) { - return visitor.visitVariable(this); - } + constructor(name, value, sourceSpan, keySpan, valueSpan) { + this.name = name; + this.value = value; + this.sourceSpan = sourceSpan; + this.keySpan = keySpan; + this.valueSpan = valueSpan; + } + visit(visitor) { + return visitor.visitVariable(this); + } } class Reference { - constructor(name, value, sourceSpan, keySpan, valueSpan) { - this.name = name; - this.value = value; - this.sourceSpan = sourceSpan; - this.keySpan = keySpan; - this.valueSpan = valueSpan; - } - visit(visitor) { - return visitor.visitReference(this); - } + constructor(name, value, sourceSpan, keySpan, valueSpan) { + this.name = name; + this.value = value; + this.sourceSpan = sourceSpan; + this.keySpan = keySpan; + this.valueSpan = valueSpan; + } + visit(visitor) { + return visitor.visitReference(this); + } } class Icu$1 { - constructor(vars, placeholders, sourceSpan, i18n) { - this.vars = vars; - this.placeholders = placeholders; - this.sourceSpan = sourceSpan; - this.i18n = i18n; - } - visit(visitor) { - return visitor.visitIcu(this); - } + constructor(vars, placeholders, sourceSpan, i18n) { + this.vars = vars; + this.placeholders = placeholders; + this.sourceSpan = sourceSpan; + this.i18n = i18n; + } + visit(visitor) { + return visitor.visitIcu(this); + } } class RecursiveVisitor$1 { - visitElement(element) { - visitAll$1(this, element.attributes); - visitAll$1(this, element.inputs); - visitAll$1(this, element.outputs); - visitAll$1(this, element.children); - visitAll$1(this, element.references); - } - visitTemplate(template) { - visitAll$1(this, template.attributes); - visitAll$1(this, template.inputs); - visitAll$1(this, template.outputs); - visitAll$1(this, template.children); - visitAll$1(this, template.references); - visitAll$1(this, template.variables); - } - visitContent(content) {} - visitVariable(variable) {} - visitReference(reference) {} - visitTextAttribute(attribute) {} - visitBoundAttribute(attribute) {} - visitBoundEvent(attribute) {} - visitText(text) {} - visitBoundText(text) {} - visitIcu(icu) {} + visitElement(element) { + visitAll$1(this, element.attributes); + visitAll$1(this, element.inputs); + visitAll$1(this, element.outputs); + visitAll$1(this, element.children); + visitAll$1(this, element.references); + } + visitTemplate(template) { + visitAll$1(this, template.attributes); + visitAll$1(this, template.inputs); + visitAll$1(this, template.outputs); + visitAll$1(this, template.children); + visitAll$1(this, template.references); + visitAll$1(this, template.variables); + } + visitContent(content) { } + visitVariable(variable) { } + visitReference(reference) { } + visitTextAttribute(attribute) { } + visitBoundAttribute(attribute) { } + visitBoundEvent(attribute) { } + visitText(text) { } + visitBoundText(text) { } + visitIcu(icu) { } } function visitAll$1(visitor, nodes) { - const result = []; - if (visitor.visit) { - for (const node of nodes) { - visitor.visit(node) || node.visit(visitor); - } - } else { - for (const node of nodes) { - const newNode = node.visit(visitor); - if (newNode) { - result.push(newNode); - } + const result = []; + if (visitor.visit) { + for (const node of nodes) { + visitor.visit(node) || node.visit(visitor); + } + } + else { + for (const node of nodes) { + const newNode = node.visit(visitor); + if (newNode) { + result.push(newNode); + } + } } - } - return result; + return result; } /** @@ -5131,213 +4126,180 @@ * found in the LICENSE file at https://angular.io/license */ class Message { - /** - * @param nodes message AST - * @param placeholders maps placeholder names to static content and their source spans - * @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages) - * @param meaning - * @param description - * @param customId - */ - constructor( - nodes, - placeholders, - placeholderToMessage, - meaning, - description, - customId - ) { - this.nodes = nodes; - this.placeholders = placeholders; - this.placeholderToMessage = placeholderToMessage; - this.meaning = meaning; - this.description = description; - this.customId = customId; - this.id = this.customId; - /** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */ - this.legacyIds = []; - this.messageString = serializeMessage(this.nodes); - if (nodes.length) { - this.sources = [ - { - filePath: nodes[0].sourceSpan.start.file.url, - startLine: nodes[0].sourceSpan.start.line + 1, - startCol: nodes[0].sourceSpan.start.col + 1, - endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1, - endCol: nodes[0].sourceSpan.start.col + 1, - }, - ]; - } else { - this.sources = []; + /** + * @param nodes message AST + * @param placeholders maps placeholder names to static content and their source spans + * @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages) + * @param meaning + * @param description + * @param customId + */ + constructor(nodes, placeholders, placeholderToMessage, meaning, description, customId) { + this.nodes = nodes; + this.placeholders = placeholders; + this.placeholderToMessage = placeholderToMessage; + this.meaning = meaning; + this.description = description; + this.customId = customId; + this.id = this.customId; + /** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */ + this.legacyIds = []; + this.messageString = serializeMessage(this.nodes); + if (nodes.length) { + this.sources = [{ + filePath: nodes[0].sourceSpan.start.file.url, + startLine: nodes[0].sourceSpan.start.line + 1, + startCol: nodes[0].sourceSpan.start.col + 1, + endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1, + endCol: nodes[0].sourceSpan.start.col + 1 + }]; + } + else { + this.sources = []; + } } - } } class Text$2 { - constructor(value, sourceSpan) { - this.value = value; - this.sourceSpan = sourceSpan; - } - visit(visitor, context) { - return visitor.visitText(this, context); - } + constructor(value, sourceSpan) { + this.value = value; + this.sourceSpan = sourceSpan; + } + visit(visitor, context) { + return visitor.visitText(this, context); + } } // TODO(vicb): do we really need this node (vs an array) ? class Container { - constructor(children, sourceSpan) { - this.children = children; - this.sourceSpan = sourceSpan; - } - visit(visitor, context) { - return visitor.visitContainer(this, context); - } + constructor(children, sourceSpan) { + this.children = children; + this.sourceSpan = sourceSpan; + } + visit(visitor, context) { + return visitor.visitContainer(this, context); + } } class Icu { - constructor(expression, type, cases, sourceSpan) { - this.expression = expression; - this.type = type; - this.cases = cases; - this.sourceSpan = sourceSpan; - } - visit(visitor, context) { - return visitor.visitIcu(this, context); - } + constructor(expression, type, cases, sourceSpan) { + this.expression = expression; + this.type = type; + this.cases = cases; + this.sourceSpan = sourceSpan; + } + visit(visitor, context) { + return visitor.visitIcu(this, context); + } } class TagPlaceholder { - constructor( - tag, - attrs, - startName, - closeName, - children, - isVoid, + constructor(tag, attrs, startName, closeName, children, isVoid, // TODO sourceSpan should cover all (we need a startSourceSpan and endSourceSpan) - sourceSpan, - startSourceSpan, - endSourceSpan - ) { - this.tag = tag; - this.attrs = attrs; - this.startName = startName; - this.closeName = closeName; - this.children = children; - this.isVoid = isVoid; - this.sourceSpan = sourceSpan; - this.startSourceSpan = startSourceSpan; - this.endSourceSpan = endSourceSpan; - } - visit(visitor, context) { - return visitor.visitTagPlaceholder(this, context); - } + sourceSpan, startSourceSpan, endSourceSpan) { + this.tag = tag; + this.attrs = attrs; + this.startName = startName; + this.closeName = closeName; + this.children = children; + this.isVoid = isVoid; + this.sourceSpan = sourceSpan; + this.startSourceSpan = startSourceSpan; + this.endSourceSpan = endSourceSpan; + } + visit(visitor, context) { + return visitor.visitTagPlaceholder(this, context); + } } class Placeholder { - constructor(value, name, sourceSpan) { - this.value = value; - this.name = name; - this.sourceSpan = sourceSpan; - } - visit(visitor, context) { - return visitor.visitPlaceholder(this, context); - } + constructor(value, name, sourceSpan) { + this.value = value; + this.name = name; + this.sourceSpan = sourceSpan; + } + visit(visitor, context) { + return visitor.visitPlaceholder(this, context); + } } class IcuPlaceholder { - constructor(value, name, sourceSpan) { - this.value = value; - this.name = name; - this.sourceSpan = sourceSpan; - } - visit(visitor, context) { - return visitor.visitIcuPlaceholder(this, context); - } + constructor(value, name, sourceSpan) { + this.value = value; + this.name = name; + this.sourceSpan = sourceSpan; + } + visit(visitor, context) { + return visitor.visitIcuPlaceholder(this, context); + } } // Clone the AST class CloneVisitor { - visitText(text, context) { - return new Text$2(text.value, text.sourceSpan); - } - visitContainer(container, context) { - const children = container.children.map((n) => n.visit(this, context)); - return new Container(children, container.sourceSpan); - } - visitIcu(icu, context) { - const cases = {}; - Object.keys(icu.cases).forEach( - (key) => (cases[key] = icu.cases[key].visit(this, context)) - ); - const msg = new Icu(icu.expression, icu.type, cases, icu.sourceSpan); - msg.expressionPlaceholder = icu.expressionPlaceholder; - return msg; - } - visitTagPlaceholder(ph, context) { - const children = ph.children.map((n) => n.visit(this, context)); - return new TagPlaceholder( - ph.tag, - ph.attrs, - ph.startName, - ph.closeName, - children, - ph.isVoid, - ph.sourceSpan, - ph.startSourceSpan, - ph.endSourceSpan - ); - } - visitPlaceholder(ph, context) { - return new Placeholder(ph.value, ph.name, ph.sourceSpan); - } - visitIcuPlaceholder(ph, context) { - return new IcuPlaceholder(ph.value, ph.name, ph.sourceSpan); - } + visitText(text, context) { + return new Text$2(text.value, text.sourceSpan); + } + visitContainer(container, context) { + const children = container.children.map(n => n.visit(this, context)); + return new Container(children, container.sourceSpan); + } + visitIcu(icu, context) { + const cases = {}; + Object.keys(icu.cases).forEach(key => cases[key] = icu.cases[key].visit(this, context)); + const msg = new Icu(icu.expression, icu.type, cases, icu.sourceSpan); + msg.expressionPlaceholder = icu.expressionPlaceholder; + return msg; + } + visitTagPlaceholder(ph, context) { + const children = ph.children.map(n => n.visit(this, context)); + return new TagPlaceholder(ph.tag, ph.attrs, ph.startName, ph.closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan); + } + visitPlaceholder(ph, context) { + return new Placeholder(ph.value, ph.name, ph.sourceSpan); + } + visitIcuPlaceholder(ph, context) { + return new IcuPlaceholder(ph.value, ph.name, ph.sourceSpan); + } } // Visit all the nodes recursively class RecurseVisitor { - visitText(text, context) {} - visitContainer(container, context) { - container.children.forEach((child) => child.visit(this)); - } - visitIcu(icu, context) { - Object.keys(icu.cases).forEach((k) => { - icu.cases[k].visit(this); - }); - } - visitTagPlaceholder(ph, context) { - ph.children.forEach((child) => child.visit(this)); - } - visitPlaceholder(ph, context) {} - visitIcuPlaceholder(ph, context) {} + visitText(text, context) { } + visitContainer(container, context) { + container.children.forEach(child => child.visit(this)); + } + visitIcu(icu, context) { + Object.keys(icu.cases).forEach(k => { + icu.cases[k].visit(this); + }); + } + visitTagPlaceholder(ph, context) { + ph.children.forEach(child => child.visit(this)); + } + visitPlaceholder(ph, context) { } + visitIcuPlaceholder(ph, context) { } } /** * Serialize the message to the Localize backtick string format that would appear in compiled code. */ function serializeMessage(messageNodes) { - const visitor = new LocalizeMessageStringVisitor(); - const str = messageNodes.map((n) => n.visit(visitor)).join(''); - return str; + const visitor = new LocalizeMessageStringVisitor(); + const str = messageNodes.map(n => n.visit(visitor)).join(''); + return str; } class LocalizeMessageStringVisitor { - visitText(text) { - return text.value; - } - visitContainer(container) { - return container.children.map((child) => child.visit(this)).join(''); - } - visitIcu(icu) { - const strCases = Object.keys(icu.cases).map( - (k) => `${k} {${icu.cases[k].visit(this)}}` - ); - return `{${ - icu.expressionPlaceholder - }, ${icu.type}, ${strCases.join(' ')}}`; - } - visitTagPlaceholder(ph) { - const children = ph.children.map((child) => child.visit(this)).join(''); - return `{$${ph.startName}}${children}{$${ph.closeName}}`; - } - visitPlaceholder(ph) { - return `{$${ph.name}}`; - } - visitIcuPlaceholder(ph) { - return `{$${ph.name}}`; - } + visitText(text) { + return text.value; + } + visitContainer(container) { + return container.children.map(child => child.visit(this)).join(''); + } + visitIcu(icu) { + const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`); + return `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`; + } + visitTagPlaceholder(ph) { + const children = ph.children.map(child => child.visit(this)).join(''); + return `{$${ph.startName}}${children}{$${ph.closeName}}`; + } + visitPlaceholder(ph) { + return `{$${ph.name}}`; + } + visitIcuPlaceholder(ph) { + return `{$${ph.name}}`; + } } /** @@ -5348,66 +4310,66 @@ * found in the LICENSE file at https://angular.io/license */ class Serializer { - // Creates a name mapper, see `PlaceholderMapper` - // Returning `null` means that no name mapping is used. - createNameMapper(message) { - return null; - } + // Creates a name mapper, see `PlaceholderMapper` + // Returning `null` means that no name mapping is used. + createNameMapper(message) { + return null; + } } /** * A simple mapper that take a function to transform an internal name to a public name */ class SimplePlaceholderMapper extends RecurseVisitor { - // create a mapping from the message - constructor(message, mapName) { - super(); - this.mapName = mapName; - this.internalToPublic = {}; - this.publicToNextId = {}; - this.publicToInternal = {}; - message.nodes.forEach((node) => node.visit(this)); - } - toPublicName(internalName) { - return this.internalToPublic.hasOwnProperty(internalName) - ? this.internalToPublic[internalName] - : null; - } - toInternalName(publicName) { - return this.publicToInternal.hasOwnProperty(publicName) - ? this.publicToInternal[publicName] - : null; - } - visitText(text, context) { - return null; - } - visitTagPlaceholder(ph, context) { - this.visitPlaceholderName(ph.startName); - super.visitTagPlaceholder(ph, context); - this.visitPlaceholderName(ph.closeName); - } - visitPlaceholder(ph, context) { - this.visitPlaceholderName(ph.name); - } - visitIcuPlaceholder(ph, context) { - this.visitPlaceholderName(ph.name); - } - // XMB placeholders could only contains A-Z, 0-9 and _ - visitPlaceholderName(internalName) { - if (!internalName || this.internalToPublic.hasOwnProperty(internalName)) { - return; - } - let publicName = this.mapName(internalName); - if (this.publicToInternal.hasOwnProperty(publicName)) { - // Create a new XMB when it has already been used - const nextId = this.publicToNextId[publicName]; - this.publicToNextId[publicName] = nextId + 1; - publicName = `${publicName}_${nextId}`; - } else { - this.publicToNextId[publicName] = 1; - } - this.internalToPublic[internalName] = publicName; - this.publicToInternal[publicName] = internalName; - } + // create a mapping from the message + constructor(message, mapName) { + super(); + this.mapName = mapName; + this.internalToPublic = {}; + this.publicToNextId = {}; + this.publicToInternal = {}; + message.nodes.forEach(node => node.visit(this)); + } + toPublicName(internalName) { + return this.internalToPublic.hasOwnProperty(internalName) ? + this.internalToPublic[internalName] : + null; + } + toInternalName(publicName) { + return this.publicToInternal.hasOwnProperty(publicName) ? this.publicToInternal[publicName] : + null; + } + visitText(text, context) { + return null; + } + visitTagPlaceholder(ph, context) { + this.visitPlaceholderName(ph.startName); + super.visitTagPlaceholder(ph, context); + this.visitPlaceholderName(ph.closeName); + } + visitPlaceholder(ph, context) { + this.visitPlaceholderName(ph.name); + } + visitIcuPlaceholder(ph, context) { + this.visitPlaceholderName(ph.name); + } + // XMB placeholders could only contains A-Z, 0-9 and _ + visitPlaceholderName(internalName) { + if (!internalName || this.internalToPublic.hasOwnProperty(internalName)) { + return; + } + let publicName = this.mapName(internalName); + if (this.publicToInternal.hasOwnProperty(publicName)) { + // Create a new XMB when it has already been used + const nextId = this.publicToNextId[publicName]; + this.publicToNextId[publicName] = nextId + 1; + publicName = `${publicName}_${nextId}`; + } + else { + this.publicToNextId[publicName] = 1; + } + this.internalToPublic[internalName] = publicName; + this.publicToInternal[publicName] = internalName; + } } /** @@ -5418,93 +4380,88 @@ * found in the LICENSE file at https://angular.io/license */ class _Visitor$2 { - visitTag(tag) { - const strAttrs = this._serializeAttributes(tag.attrs); - if (tag.children.length == 0) { - return `<${tag.name}${strAttrs}/>`; - } - const strChildren = tag.children.map((node) => node.visit(this)); - return `<${tag.name}${strAttrs}>${strChildren.join('')}`; - } - visitText(text) { - return text.value; - } - visitDeclaration(decl) { - return ``; - } - _serializeAttributes(attrs) { - const strAttrs = Object.keys(attrs) - .map((name) => `${name}="${attrs[name]}"`) - .join(' '); - return strAttrs.length > 0 ? ' ' + strAttrs : ''; - } - visitDoctype(doctype) { - return ``; - } + visitTag(tag) { + const strAttrs = this._serializeAttributes(tag.attrs); + if (tag.children.length == 0) { + return `<${tag.name}${strAttrs}/>`; + } + const strChildren = tag.children.map(node => node.visit(this)); + return `<${tag.name}${strAttrs}>${strChildren.join('')}`; + } + visitText(text) { + return text.value; + } + visitDeclaration(decl) { + return ``; + } + _serializeAttributes(attrs) { + const strAttrs = Object.keys(attrs).map((name) => `${name}="${attrs[name]}"`).join(' '); + return strAttrs.length > 0 ? ' ' + strAttrs : ''; + } + visitDoctype(doctype) { + return ``; + } } const _visitor = new _Visitor$2(); function serialize(nodes) { - return nodes.map((node) => node.visit(_visitor)).join(''); + return nodes.map((node) => node.visit(_visitor)).join(''); } class Declaration { - constructor(unescapedAttrs) { - this.attrs = {}; - Object.keys(unescapedAttrs).forEach((k) => { - this.attrs[k] = escapeXml(unescapedAttrs[k]); - }); - } - visit(visitor) { - return visitor.visitDeclaration(this); - } + constructor(unescapedAttrs) { + this.attrs = {}; + Object.keys(unescapedAttrs).forEach((k) => { + this.attrs[k] = escapeXml(unescapedAttrs[k]); + }); + } + visit(visitor) { + return visitor.visitDeclaration(this); + } } class Doctype { - constructor(rootTag, dtd) { - this.rootTag = rootTag; - this.dtd = dtd; - } - visit(visitor) { - return visitor.visitDoctype(this); - } + constructor(rootTag, dtd) { + this.rootTag = rootTag; + this.dtd = dtd; + } + visit(visitor) { + return visitor.visitDoctype(this); + } } class Tag { - constructor(name, unescapedAttrs = {}, children = []) { - this.name = name; - this.children = children; - this.attrs = {}; - Object.keys(unescapedAttrs).forEach((k) => { - this.attrs[k] = escapeXml(unescapedAttrs[k]); - }); - } - visit(visitor) { - return visitor.visitTag(this); - } + constructor(name, unescapedAttrs = {}, children = []) { + this.name = name; + this.children = children; + this.attrs = {}; + Object.keys(unescapedAttrs).forEach((k) => { + this.attrs[k] = escapeXml(unescapedAttrs[k]); + }); + } + visit(visitor) { + return visitor.visitTag(this); + } } class Text$1 { - constructor(unescapedValue) { - this.value = escapeXml(unescapedValue); - } - visit(visitor) { - return visitor.visitText(this); - } + constructor(unescapedValue) { + this.value = escapeXml(unescapedValue); + } + visit(visitor) { + return visitor.visitText(this); + } } class CR extends Text$1 { - constructor(ws = 0) { - super(`\n${new Array(ws + 1).join(' ')}`); - } + constructor(ws = 0) { + super(`\n${new Array(ws + 1).join(' ')}`); + } } const _ESCAPED_CHARS = [ - [/&/g, '&'], - [/"/g, '"'], - [/'/g, '''], - [//g, '>'], + [/&/g, '&'], + [/"/g, '"'], + [/'/g, '''], + [//g, '>'], ]; // Escape `_ESCAPED_CHARS` characters in the given text with encoded entities function escapeXml(text) { - return _ESCAPED_CHARS.reduce( - (text, entry) => text.replace(entry[0], entry[1]), - text - ); + return _ESCAPED_CHARS.reduce((text, entry) => text.replace(entry[0], entry[1]), text); } /** @@ -5539,161 +4496,127 @@ `; class Xmb extends Serializer { - write(messages, locale) { - const exampleVisitor = new ExampleVisitor(); - const visitor = new _Visitor$1(); - let rootNode = new Tag(_MESSAGES_TAG); - messages.forEach((message) => { - const attrs = { id: message.id }; - if (message.description) { - attrs['desc'] = message.description; - } - if (message.meaning) { - attrs['meaning'] = message.meaning; - } - let sourceTags = []; - message.sources.forEach((source) => { - sourceTags.push( - new Tag(_SOURCE_TAG$2, {}, [ - new Text$1( - `${source.filePath}:${source.startLine}${ - source.endLine !== source.startLine - ? ',' + source.endLine - : '' - }` - ), - ]) - ); - }); - rootNode.children.push( - new CR(2), - new Tag(_MESSAGE_TAG, attrs, [ - ...sourceTags, - ...visitor.serialize(message.nodes), - ]) - ); - }); - rootNode.children.push(new CR()); - return serialize([ - new Declaration({ version: '1.0', encoding: 'UTF-8' }), - new CR(), - new Doctype(_MESSAGES_TAG, _DOCTYPE), - new CR(), - exampleVisitor.addDefaultExamples(rootNode), - new CR(), - ]); - } - load(content, url) { - throw new Error('Unsupported'); - } - digest(message) { - return digest(message); - } - createNameMapper(message) { - return new SimplePlaceholderMapper(message, toPublicName); - } + write(messages, locale) { + const exampleVisitor = new ExampleVisitor(); + const visitor = new _Visitor$1(); + let rootNode = new Tag(_MESSAGES_TAG); + messages.forEach(message => { + const attrs = { id: message.id }; + if (message.description) { + attrs['desc'] = message.description; + } + if (message.meaning) { + attrs['meaning'] = message.meaning; + } + let sourceTags = []; + message.sources.forEach((source) => { + sourceTags.push(new Tag(_SOURCE_TAG$2, {}, [new Text$1(`${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`)])); + }); + rootNode.children.push(new CR(2), new Tag(_MESSAGE_TAG, attrs, [...sourceTags, ...visitor.serialize(message.nodes)])); + }); + rootNode.children.push(new CR()); + return serialize([ + new Declaration({ version: '1.0', encoding: 'UTF-8' }), + new CR(), + new Doctype(_MESSAGES_TAG, _DOCTYPE), + new CR(), + exampleVisitor.addDefaultExamples(rootNode), + new CR(), + ]); + } + load(content, url) { + throw new Error('Unsupported'); + } + digest(message) { + return digest(message); + } + createNameMapper(message) { + return new SimplePlaceholderMapper(message, toPublicName); + } } class _Visitor$1 { - visitText(text, context) { - return [new Text$1(text.value)]; - } - visitContainer(container, context) { - const nodes = []; - container.children.forEach((node) => nodes.push(...node.visit(this))); - return nodes; - } - visitIcu(icu, context) { - const nodes = [ - new Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `), - ]; - Object.keys(icu.cases).forEach((c) => { - nodes.push( - new Text$1(`${c} {`), - ...icu.cases[c].visit(this), - new Text$1(`} `) - ); - }); - nodes.push(new Text$1(`}`)); - return nodes; - } - visitTagPlaceholder(ph, context) { - const startTagAsText = new Text$1(`<${ph.tag}>`); - const startEx = new Tag(_EXAMPLE_TAG, {}, [startTagAsText]); - // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. - const startTagPh = new Tag(_PLACEHOLDER_TAG$3, { name: ph.startName }, [ - startEx, - startTagAsText, - ]); - if (ph.isVoid) { - // void tags have no children nor closing tags - return [startTagPh]; - } - const closeTagAsText = new Text$1(``); - const closeEx = new Tag(_EXAMPLE_TAG, {}, [closeTagAsText]); - // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. - const closeTagPh = new Tag(_PLACEHOLDER_TAG$3, { name: ph.closeName }, [ - closeEx, - closeTagAsText, - ]); - return [startTagPh, ...this.serialize(ph.children), closeTagPh]; - } - visitPlaceholder(ph, context) { - const interpolationAsText = new Text$1(`{{${ph.value}}}`); - // Example tag needs to be not-empty for TC. - const exTag = new Tag(_EXAMPLE_TAG, {}, [interpolationAsText]); - return [ - // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. - new Tag(_PLACEHOLDER_TAG$3, { name: ph.name }, [ - exTag, - interpolationAsText, - ]), - ]; - } - visitIcuPlaceholder(ph, context) { - const icuExpression = ph.value.expression; - const icuType = ph.value.type; - const icuCases = Object.keys(ph.value.cases) - .map((value) => value + ' {...}') - .join(' '); - const icuAsText = new Text$1( - `{${icuExpression}, ${icuType}, ${icuCases}}` - ); - const exTag = new Tag(_EXAMPLE_TAG, {}, [icuAsText]); - return [ - // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. - new Tag(_PLACEHOLDER_TAG$3, { name: ph.name }, [exTag, icuAsText]), - ]; - } - serialize(nodes) { - return [].concat(...nodes.map((node) => node.visit(this))); - } + visitText(text, context) { + return [new Text$1(text.value)]; + } + visitContainer(container, context) { + const nodes = []; + container.children.forEach((node) => nodes.push(...node.visit(this))); + return nodes; + } + visitIcu(icu, context) { + const nodes = [new Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)]; + Object.keys(icu.cases).forEach((c) => { + nodes.push(new Text$1(`${c} {`), ...icu.cases[c].visit(this), new Text$1(`} `)); + }); + nodes.push(new Text$1(`}`)); + return nodes; + } + visitTagPlaceholder(ph, context) { + const startTagAsText = new Text$1(`<${ph.tag}>`); + const startEx = new Tag(_EXAMPLE_TAG, {}, [startTagAsText]); + // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. + const startTagPh = new Tag(_PLACEHOLDER_TAG$3, { name: ph.startName }, [startEx, startTagAsText]); + if (ph.isVoid) { + // void tags have no children nor closing tags + return [startTagPh]; + } + const closeTagAsText = new Text$1(``); + const closeEx = new Tag(_EXAMPLE_TAG, {}, [closeTagAsText]); + // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. + const closeTagPh = new Tag(_PLACEHOLDER_TAG$3, { name: ph.closeName }, [closeEx, closeTagAsText]); + return [startTagPh, ...this.serialize(ph.children), closeTagPh]; + } + visitPlaceholder(ph, context) { + const interpolationAsText = new Text$1(`{{${ph.value}}}`); + // Example tag needs to be not-empty for TC. + const exTag = new Tag(_EXAMPLE_TAG, {}, [interpolationAsText]); + return [ + // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. + new Tag(_PLACEHOLDER_TAG$3, { name: ph.name }, [exTag, interpolationAsText]) + ]; + } + visitIcuPlaceholder(ph, context) { + const icuExpression = ph.value.expression; + const icuType = ph.value.type; + const icuCases = Object.keys(ph.value.cases).map((value) => value + ' {...}').join(' '); + const icuAsText = new Text$1(`{${icuExpression}, ${icuType}, ${icuCases}}`); + const exTag = new Tag(_EXAMPLE_TAG, {}, [icuAsText]); + return [ + // TC requires PH to have a non empty EX, and uses the text node to show the "original" value. + new Tag(_PLACEHOLDER_TAG$3, { name: ph.name }, [exTag, icuAsText]) + ]; + } + serialize(nodes) { + return [].concat(...nodes.map(node => node.visit(this))); + } } function digest(message) { - return decimalDigest(message); + return decimalDigest(message); } // TC requires at least one non-empty example on placeholders class ExampleVisitor { - addDefaultExamples(node) { - node.visit(this); - return node; - } - visitTag(tag) { - if (tag.name === _PLACEHOLDER_TAG$3) { - if (!tag.children || tag.children.length == 0) { - const exText = new Text$1(tag.attrs['name'] || '...'); - tag.children = [new Tag(_EXAMPLE_TAG, {}, [exText])]; - } - } else if (tag.children) { - tag.children.forEach((node) => node.visit(this)); + addDefaultExamples(node) { + node.visit(this); + return node; + } + visitTag(tag) { + if (tag.name === _PLACEHOLDER_TAG$3) { + if (!tag.children || tag.children.length == 0) { + const exText = new Text$1(tag.attrs['name'] || '...'); + tag.children = [new Tag(_EXAMPLE_TAG, {}, [exText])]; + } + } + else if (tag.children) { + tag.children.forEach(node => node.visit(this)); + } } - } - visitText(text) {} - visitDeclaration(decl) {} - visitDoctype(doctype) {} + visitText(text) { } + visitDeclaration(decl) { } + visitDoctype(doctype) { } } // XMB/XTB placeholders can only contain A-Z, 0-9 and _ function toPublicName(internalName) { - return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_'); + return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_'); } /** @@ -5721,86 +4644,68 @@ /** Placeholder wrapper for i18n expressions **/ const I18N_PLACEHOLDER_SYMBOL = '�'; function isI18nAttribute(name) { - return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX); + return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX); } function isI18nRootNode(meta) { - return meta instanceof Message; + return meta instanceof Message; } function isSingleI18nIcu(meta) { - return ( - isI18nRootNode(meta) && - meta.nodes.length === 1 && - meta.nodes[0] instanceof Icu - ); + return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof Icu; } function hasI18nMeta(node) { - return !!node.i18n; + return !!node.i18n; } function hasI18nAttrs(element) { - return element.attrs.some((attr) => isI18nAttribute(attr.name)); + return element.attrs.some((attr) => isI18nAttribute(attr.name)); } function icuFromI18nMessage(message) { - return message.nodes[0]; + return message.nodes[0]; } function wrapI18nPlaceholder(content, contextId = 0) { - const blockId = contextId > 0 ? `:${contextId}` : ''; - return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`; - } - function assembleI18nBoundString( - strings, - bindingStartIndex = 0, - contextId = 0 - ) { - if (!strings.length) return ''; - let acc = ''; - const lastIdx = strings.length - 1; - for (let i = 0; i < lastIdx; i++) { - acc += `${strings[i]}${wrapI18nPlaceholder( - bindingStartIndex + i, - contextId - )}`; - } - acc += strings[lastIdx]; - return acc; + const blockId = contextId > 0 ? `:${contextId}` : ''; + return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`; + } + function assembleI18nBoundString(strings, bindingStartIndex = 0, contextId = 0) { + if (!strings.length) + return ''; + let acc = ''; + const lastIdx = strings.length - 1; + for (let i = 0; i < lastIdx; i++) { + acc += `${strings[i]}${wrapI18nPlaceholder(bindingStartIndex + i, contextId)}`; + } + acc += strings[lastIdx]; + return acc; } function getSeqNumberGenerator(startsAt = 0) { - let current = startsAt; - return () => current++; + let current = startsAt; + return () => current++; } function placeholdersToParams(placeholders) { - const params = {}; - placeholders.forEach((values, key) => { - params[key] = literal( - values.length > 1 ? `[${values.join('|')}]` : values[0] - ); - }); - return params; + const params = {}; + placeholders.forEach((values, key) => { + params[key] = literal(values.length > 1 ? `[${values.join('|')}]` : values[0]); + }); + return params; } function updatePlaceholderMap(map, name, ...values) { - const current = map.get(name) || []; - current.push(...values); - map.set(name, current); - } - function assembleBoundTextPlaceholders( - meta, - bindingStartIndex = 0, - contextId = 0 - ) { - const startIdx = bindingStartIndex; - const placeholders = new Map(); - const node = - meta instanceof Message - ? meta.nodes.find((node) => node instanceof Container) - : meta; - if (node) { - node.children - .filter((child) => child instanceof Placeholder) - .forEach((child, idx) => { - const content = wrapI18nPlaceholder(startIdx + idx, contextId); - updatePlaceholderMap(placeholders, child.name, content); - }); - } - return placeholders; + const current = map.get(name) || []; + current.push(...values); + map.set(name, current); + } + function assembleBoundTextPlaceholders(meta, bindingStartIndex = 0, contextId = 0) { + const startIdx = bindingStartIndex; + const placeholders = new Map(); + const node = meta instanceof Message ? meta.nodes.find(node => node instanceof Container) : meta; + if (node) { + node + .children + .filter((child) => child instanceof Placeholder) + .forEach((child, idx) => { + const content = wrapI18nPlaceholder(startIdx + idx, contextId); + updatePlaceholderMap(placeholders, child.name, content); + }); + } + return placeholders; } /** * Format the placeholder names in a map of placeholders to expressions. @@ -5813,14 +4718,11 @@ * @returns A new map of formatted placeholder names to expressions. */ function formatI18nPlaceholderNamesInMap(params = {}, useCamelCase) { - const _params = {}; - if (params && Object.keys(params).length) { - Object.keys(params).forEach( - (key) => - (_params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]) - ); - } - return _params; + const _params = {}; + if (params && Object.keys(params).length) { + Object.keys(params).forEach(key => _params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]); + } + return _params; } /** * Converts internal placeholder names to public-facing format @@ -5831,27 +4733,25 @@ * @returns Formatted placeholder name */ function formatI18nPlaceholderName(name, useCamelCase = true) { - const publicName = toPublicName(name); - if (!useCamelCase) { - return publicName; - } - const chunks = publicName.split('_'); - if (chunks.length === 1) { - // if no "_" found - just lowercase the value - return name.toLowerCase(); - } - let postfix; - // eject last element if it's a number - if (/^\d+$/.test(chunks[chunks.length - 1])) { - postfix = chunks.pop(); - } - let raw = chunks.shift().toLowerCase(); - if (chunks.length) { - raw += chunks - .map((c) => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()) - .join(''); - } - return postfix ? `${raw}_${postfix}` : raw; + const publicName = toPublicName(name); + if (!useCamelCase) { + return publicName; + } + const chunks = publicName.split('_'); + if (chunks.length === 1) { + // if no "_" found - just lowercase the value + return name.toLowerCase(); + } + let postfix; + // eject last element if it's a number + if (/^\d+$/.test(chunks[chunks.length - 1])) { + postfix = chunks.pop(); + } + let raw = chunks.shift().toLowerCase(); + if (chunks.length) { + raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join(''); + } + return postfix ? `${raw}_${postfix}` : raw; } /** * Generates a prefix for translation const name. @@ -5860,20 +4760,14 @@ * @returns Complete translation const prefix */ function getTranslationConstPrefix(extra) { - return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase(); + return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase(); } /** * Generate AST to declare a variable. E.g. `var I18N_1;`. * @param variable the name of the variable to declare. */ function declareI18nVariable(variable) { - return new DeclareVarStmt( - variable.name, - undefined, - INFERRED_TYPE, - undefined, - variable.sourceSpan - ); + return new DeclareVarStmt(variable.name, undefined, INFERRED_TYPE, undefined, variable.sourceSpan); } /** @@ -5914,62 +4808,62 @@ const MAX_CHAIN_LENGTH = 500; /** Instructions that support chaining. */ const CHAINABLE_INSTRUCTIONS = new Set([ - Identifiers.element, - Identifiers.elementStart, - Identifiers.elementEnd, - Identifiers.elementContainer, - Identifiers.elementContainerStart, - Identifiers.elementContainerEnd, - Identifiers.i18nExp, - Identifiers.listener, - Identifiers.classProp, - Identifiers.syntheticHostListener, - Identifiers.hostProperty, - Identifiers.syntheticHostProperty, - Identifiers.property, - Identifiers.propertyInterpolate1, - Identifiers.propertyInterpolate2, - Identifiers.propertyInterpolate3, - Identifiers.propertyInterpolate4, - Identifiers.propertyInterpolate5, - Identifiers.propertyInterpolate6, - Identifiers.propertyInterpolate7, - Identifiers.propertyInterpolate8, - Identifiers.propertyInterpolateV, - Identifiers.attribute, - Identifiers.attributeInterpolate1, - Identifiers.attributeInterpolate2, - Identifiers.attributeInterpolate3, - Identifiers.attributeInterpolate4, - Identifiers.attributeInterpolate5, - Identifiers.attributeInterpolate6, - Identifiers.attributeInterpolate7, - Identifiers.attributeInterpolate8, - Identifiers.attributeInterpolateV, - Identifiers.styleProp, - Identifiers.stylePropInterpolate1, - Identifiers.stylePropInterpolate2, - Identifiers.stylePropInterpolate3, - Identifiers.stylePropInterpolate4, - Identifiers.stylePropInterpolate5, - Identifiers.stylePropInterpolate6, - Identifiers.stylePropInterpolate7, - Identifiers.stylePropInterpolate8, - Identifiers.stylePropInterpolateV, - Identifiers.textInterpolate, - Identifiers.textInterpolate1, - Identifiers.textInterpolate2, - Identifiers.textInterpolate3, - Identifiers.textInterpolate4, - Identifiers.textInterpolate5, - Identifiers.textInterpolate6, - Identifiers.textInterpolate7, - Identifiers.textInterpolate8, - Identifiers.textInterpolateV, + Identifiers.element, + Identifiers.elementStart, + Identifiers.elementEnd, + Identifiers.elementContainer, + Identifiers.elementContainerStart, + Identifiers.elementContainerEnd, + Identifiers.i18nExp, + Identifiers.listener, + Identifiers.classProp, + Identifiers.syntheticHostListener, + Identifiers.hostProperty, + Identifiers.syntheticHostProperty, + Identifiers.property, + Identifiers.propertyInterpolate1, + Identifiers.propertyInterpolate2, + Identifiers.propertyInterpolate3, + Identifiers.propertyInterpolate4, + Identifiers.propertyInterpolate5, + Identifiers.propertyInterpolate6, + Identifiers.propertyInterpolate7, + Identifiers.propertyInterpolate8, + Identifiers.propertyInterpolateV, + Identifiers.attribute, + Identifiers.attributeInterpolate1, + Identifiers.attributeInterpolate2, + Identifiers.attributeInterpolate3, + Identifiers.attributeInterpolate4, + Identifiers.attributeInterpolate5, + Identifiers.attributeInterpolate6, + Identifiers.attributeInterpolate7, + Identifiers.attributeInterpolate8, + Identifiers.attributeInterpolateV, + Identifiers.styleProp, + Identifiers.stylePropInterpolate1, + Identifiers.stylePropInterpolate2, + Identifiers.stylePropInterpolate3, + Identifiers.stylePropInterpolate4, + Identifiers.stylePropInterpolate5, + Identifiers.stylePropInterpolate6, + Identifiers.stylePropInterpolate7, + Identifiers.stylePropInterpolate8, + Identifiers.stylePropInterpolateV, + Identifiers.textInterpolate, + Identifiers.textInterpolate1, + Identifiers.textInterpolate2, + Identifiers.textInterpolate3, + Identifiers.textInterpolate4, + Identifiers.textInterpolate5, + Identifiers.textInterpolate6, + Identifiers.textInterpolate7, + Identifiers.textInterpolate8, + Identifiers.textInterpolateV, ]); /** Generates a call to a single instruction. */ function invokeInstruction(span, reference, params) { - return importExpr(reference, null, span).callFn(params, span); + return importExpr(reference, null, span).callFn(params, span); } /** * Creates an allocator for a temporary variable. @@ -5977,98 +4871,89 @@ * A variable declaration is added to the statements the first time the allocator is invoked. */ function temporaryAllocator(statements, name) { - let temp = null; - return () => { - if (!temp) { - statements.push( - new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE) - ); - temp = variable(name); - } - return temp; - }; + let temp = null; + return () => { + if (!temp) { + statements.push(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE)); + temp = variable(name); + } + return temp; + }; } function invalid(arg) { - throw new Error( - `Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}` - ); + throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`); } function asLiteral(value) { - if (Array.isArray(value)) { - return literalArr(value.map(asLiteral)); - } - return literal(value, INFERRED_TYPE); + if (Array.isArray(value)) { + return literalArr(value.map(asLiteral)); + } + return literal(value, INFERRED_TYPE); } function conditionallyCreateMapObjectLiteral(keys, keepDeclared) { - if (Object.getOwnPropertyNames(keys).length > 0) { - return mapToExpression(keys, keepDeclared); - } - return null; + if (Object.getOwnPropertyNames(keys).length > 0) { + return mapToExpression(keys, keepDeclared); + } + return null; } function mapToExpression(map, keepDeclared) { - return literalMap( - Object.getOwnPropertyNames(map).map((key) => { - // canonical syntax: `dirProp: publicProp` - const value = map[key]; - let declaredName; - let publicName; - let minifiedName; - let needsDeclaredName; - if (Array.isArray(value)) { - [publicName, declaredName] = value; - minifiedName = key; - needsDeclaredName = publicName !== declaredName; - } else { - minifiedName = declaredName = key; - publicName = value; - needsDeclaredName = false; - } - return { - key: minifiedName, - // put quotes around keys that contain potentially unsafe characters - quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName), - value: - keepDeclared && needsDeclaredName - ? literalArr([asLiteral(publicName), asLiteral(declaredName)]) - : asLiteral(publicName), - }; - }) - ); + return literalMap(Object.getOwnPropertyNames(map).map(key => { + // canonical syntax: `dirProp: publicProp` + const value = map[key]; + let declaredName; + let publicName; + let minifiedName; + let needsDeclaredName; + if (Array.isArray(value)) { + [publicName, declaredName] = value; + minifiedName = key; + needsDeclaredName = publicName !== declaredName; + } + else { + minifiedName = declaredName = key; + publicName = value; + needsDeclaredName = false; + } + return { + key: minifiedName, + // put quotes around keys that contain potentially unsafe characters + quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName), + value: (keepDeclared && needsDeclaredName) ? + literalArr([asLiteral(publicName), asLiteral(declaredName)]) : + asLiteral(publicName) + }; + })); } /** * Remove trailing null nodes as they are implied. */ function trimTrailingNulls(parameters) { - while (isNull(parameters[parameters.length - 1])) { - parameters.pop(); - } - return parameters; + while (isNull(parameters[parameters.length - 1])) { + parameters.pop(); + } + return parameters; } function getQueryPredicate(query, constantPool) { - if (Array.isArray(query.predicate)) { - let predicate = []; - query.predicate.forEach((selector) => { - // Each item in predicates array may contain strings with comma-separated refs - // (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them - // as separate array entities - const selectors = selector - .split(',') - .map((token) => literal(token.trim())); - predicate.push(...selectors); - }); - return constantPool.getConstLiteral(literalArr(predicate), true); - } else { - // The original predicate may have been wrapped in a `forwardRef()` call. - switch (query.predicate.forwardRef) { - case 0 /* ForwardRefHandling.None */: - case 2 /* ForwardRefHandling.Unwrapped */: - return query.predicate.expression; - case 1 /* ForwardRefHandling.Wrapped */: - return importExpr(Identifiers.resolveForwardRef).callFn([ - query.predicate.expression, - ]); + if (Array.isArray(query.predicate)) { + let predicate = []; + query.predicate.forEach((selector) => { + // Each item in predicates array may contain strings with comma-separated refs + // (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them + // as separate array entities + const selectors = selector.split(',').map(token => literal(token.trim())); + predicate.push(...selectors); + }); + return constantPool.getConstLiteral(literalArr(predicate), true); + } + else { + // The original predicate may have been wrapped in a `forwardRef()` call. + switch (query.predicate.forwardRef) { + case 0 /* ForwardRefHandling.None */: + case 2 /* ForwardRefHandling.Unwrapped */: + return query.predicate.expression; + case 1 /* ForwardRefHandling.Wrapped */: + return importExpr(Identifiers.resolveForwardRef).callFn([query.predicate.expression]); + } } - } } /** * A representation for an object literal used during codegen of definition objects. The generic @@ -6076,17 +4961,17 @@ * property names that are set can be resolved to their documented declaration. */ class DefinitionMap { - constructor() { - this.values = []; - } - set(key, value) { - if (value) { - this.values.push({ key: key, value, quoted: false }); - } - } - toLiteralMap() { - return literalMap(this.values); - } + constructor() { + this.values = []; + } + set(key, value) { + if (value) { + this.values.push({ key: key, value, quoted: false }); + } + } + toLiteralMap() { + return literalMap(this.values); + } } /** * Extract a map of properties to values for a given element or template node, which can be used @@ -6098,23 +4983,24 @@ * property name to an empty string. */ function getAttrsForDirectiveMatching(elOrTpl) { - const attributesMap = {}; - if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') { - elOrTpl.templateAttrs.forEach((a) => (attributesMap[a.name] = '')); - } else { - elOrTpl.attributes.forEach((a) => { - if (!isI18nAttribute(a.name)) { - attributesMap[a.name] = a.value; - } - }); - elOrTpl.inputs.forEach((i) => { - attributesMap[i.name] = ''; - }); - elOrTpl.outputs.forEach((o) => { - attributesMap[o.name] = ''; - }); - } - return attributesMap; + const attributesMap = {}; + if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') { + elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = ''); + } + else { + elOrTpl.attributes.forEach(a => { + if (!isI18nAttribute(a.name)) { + attributesMap[a.name] = a.value; + } + }); + elOrTpl.inputs.forEach(i => { + attributesMap[i.name] = ''; + }); + elOrTpl.outputs.forEach(o => { + attributesMap[o.name] = ''; + }); + } + return attributesMap; } /** * Gets the number of arguments expected to be passed to a generated instruction in the case of @@ -6122,74 +5008,53 @@ * @param interpolation An interpolation ast */ function getInterpolationArgsLength(interpolation) { - const { expressions, strings } = interpolation; - if ( - expressions.length === 1 && - strings.length === 2 && - strings[0] === '' && - strings[1] === '' - ) { - // If the interpolation has one interpolated value, but the prefix and suffix are both empty - // strings, we only pass one argument, to a special instruction like `propertyInterpolate` or - // `textInterpolate`. - return 1; - } else { - return expressions.length + strings.length; - } + const { expressions, strings } = interpolation; + if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') { + // If the interpolation has one interpolated value, but the prefix and suffix are both empty + // strings, we only pass one argument, to a special instruction like `propertyInterpolate` or + // `textInterpolate`. + return 1; + } + else { + return expressions.length + strings.length; + } } /** * Generates the final instruction call statements based on the passed in configuration. * Will try to chain instructions as much as possible, if chaining is supported. */ function getInstructionStatements(instructions) { - var _a; - const statements = []; - let pendingExpression = null; - let pendingExpressionType = null; - let chainLength = 0; - for (const current of instructions) { - const resolvedParams = - (_a = - typeof current.paramsOrFn === 'function' - ? current.paramsOrFn() - : current.paramsOrFn) !== null && _a !== void 0 - ? _a - : []; - const params = Array.isArray(resolvedParams) - ? resolvedParams - : [resolvedParams]; - // If the current instruction is the same as the previous one - // and it can be chained, add another call to the chain. - if ( - chainLength < MAX_CHAIN_LENGTH && - pendingExpressionType === current.reference && - CHAINABLE_INSTRUCTIONS.has(pendingExpressionType) - ) { - // We'll always have a pending expression when there's a pending expression type. - pendingExpression = pendingExpression.callFn( - params, - pendingExpression.sourceSpan - ); - chainLength++; - } else { - if (pendingExpression !== null) { + var _a; + const statements = []; + let pendingExpression = null; + let pendingExpressionType = null; + let chainLength = 0; + for (const current of instructions) { + const resolvedParams = (_a = (typeof current.paramsOrFn === 'function' ? current.paramsOrFn() : current.paramsOrFn)) !== null && _a !== void 0 ? _a : []; + const params = Array.isArray(resolvedParams) ? resolvedParams : [resolvedParams]; + // If the current instruction is the same as the previous one + // and it can be chained, add another call to the chain. + if (chainLength < MAX_CHAIN_LENGTH && pendingExpressionType === current.reference && + CHAINABLE_INSTRUCTIONS.has(pendingExpressionType)) { + // We'll always have a pending expression when there's a pending expression type. + pendingExpression = pendingExpression.callFn(params, pendingExpression.sourceSpan); + chainLength++; + } + else { + if (pendingExpression !== null) { + statements.push(pendingExpression.toStmt()); + } + pendingExpression = invokeInstruction(current.span, current.reference, params); + pendingExpressionType = current.reference; + chainLength = 0; + } + } + // Since the current instruction adds the previous one to the statements, + // we may be left with the final one at the end that is still pending. + if (pendingExpression !== null) { statements.push(pendingExpression.toStmt()); - } - pendingExpression = invokeInstruction( - current.span, - current.reference, - params - ); - pendingExpressionType = current.reference; - chainLength = 0; - } - } - // Since the current instruction adds the previous one to the statements, - // we may be left with the final one at the end that is still pending. - if (pendingExpression !== null) { - statements.push(pendingExpression.toStmt()); - } - return statements; + } + return statements; } /** @@ -6200,153 +5065,113 @@ * found in the LICENSE file at https://angular.io/license */ function compileInjectable(meta, resolveForwardRefs) { - let result = null; - const factoryMeta = { - name: meta.name, - type: meta.type, - internalType: meta.internalType, - typeArgumentCount: meta.typeArgumentCount, - deps: [], - target: exports.FactoryTarget.Injectable, - }; - if (meta.useClass !== undefined) { - // meta.useClass has two modes of operation. Either deps are specified, in which case `new` is - // used to instantiate the class with dependencies injected, or deps are not specified and - // the factory of the class is used to instantiate it. - // - // A special case exists for useClass: Type where Type is the injectable type itself and no - // deps are specified, in which case 'useClass' is effectively ignored. - const useClassOnSelf = meta.useClass.expression.isEquivalent( - meta.internalType - ); - let deps = undefined; - if (meta.deps !== undefined) { - deps = meta.deps; - } - if (deps !== undefined) { - // factory: () => new meta.useClass(...deps) - result = compileFactoryFunction( - Object.assign(Object.assign({}, factoryMeta), { - delegate: meta.useClass.expression, - delegateDeps: deps, - delegateType: R3FactoryDelegateType.Class, - }) - ); - } else if (useClassOnSelf) { - result = compileFactoryFunction(factoryMeta); - } else { - result = { - statements: [], - expression: delegateToFactory( - meta.type.value, - meta.useClass.expression, - resolveForwardRefs - ), - }; - } - } else if (meta.useFactory !== undefined) { - if (meta.deps !== undefined) { - result = compileFactoryFunction( - Object.assign(Object.assign({}, factoryMeta), { - delegate: meta.useFactory, - delegateDeps: meta.deps || [], - delegateType: R3FactoryDelegateType.Function, - }) - ); - } else { - result = { - statements: [], - expression: fn([], [new ReturnStatement(meta.useFactory.callFn([]))]), - }; - } - } else if (meta.useValue !== undefined) { - // Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for - // client code because meta.useValue is an Expression which will be defined even if the actual - // value is undefined. - result = compileFactoryFunction( - Object.assign(Object.assign({}, factoryMeta), { - expression: meta.useValue.expression, - }) - ); - } else if (meta.useExisting !== undefined) { - // useExisting is an `inject` call on the existing token. - result = compileFactoryFunction( - Object.assign(Object.assign({}, factoryMeta), { - expression: importExpr(Identifiers.inject).callFn([ - meta.useExisting.expression, - ]), - }) - ); - } else { - result = { - statements: [], - expression: delegateToFactory( - meta.type.value, - meta.internalType, - resolveForwardRefs - ), + let result = null; + const factoryMeta = { + name: meta.name, + type: meta.type, + internalType: meta.internalType, + typeArgumentCount: meta.typeArgumentCount, + deps: [], + target: exports.FactoryTarget.Injectable, + }; + if (meta.useClass !== undefined) { + // meta.useClass has two modes of operation. Either deps are specified, in which case `new` is + // used to instantiate the class with dependencies injected, or deps are not specified and + // the factory of the class is used to instantiate it. + // + // A special case exists for useClass: Type where Type is the injectable type itself and no + // deps are specified, in which case 'useClass' is effectively ignored. + const useClassOnSelf = meta.useClass.expression.isEquivalent(meta.internalType); + let deps = undefined; + if (meta.deps !== undefined) { + deps = meta.deps; + } + if (deps !== undefined) { + // factory: () => new meta.useClass(...deps) + result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useClass.expression, delegateDeps: deps, delegateType: R3FactoryDelegateType.Class })); + } + else if (useClassOnSelf) { + result = compileFactoryFunction(factoryMeta); + } + else { + result = { + statements: [], + expression: delegateToFactory(meta.type.value, meta.useClass.expression, resolveForwardRefs) + }; + } + } + else if (meta.useFactory !== undefined) { + if (meta.deps !== undefined) { + result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useFactory, delegateDeps: meta.deps || [], delegateType: R3FactoryDelegateType.Function })); + } + else { + result = { + statements: [], + expression: fn([], [new ReturnStatement(meta.useFactory.callFn([]))]) + }; + } + } + else if (meta.useValue !== undefined) { + // Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for + // client code because meta.useValue is an Expression which will be defined even if the actual + // value is undefined. + result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: meta.useValue.expression })); + } + else if (meta.useExisting !== undefined) { + // useExisting is an `inject` call on the existing token. + result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: importExpr(Identifiers.inject).callFn([meta.useExisting.expression]) })); + } + else { + result = { + statements: [], + expression: delegateToFactory(meta.type.value, meta.internalType, resolveForwardRefs) + }; + } + const token = meta.internalType; + const injectableProps = new DefinitionMap(); + injectableProps.set('token', token); + injectableProps.set('factory', result.expression); + // Only generate providedIn property if it has a non-null value + if (meta.providedIn.expression.value !== null) { + injectableProps.set('providedIn', convertFromMaybeForwardRefExpression(meta.providedIn)); + } + const expression = importExpr(Identifiers.ɵɵdefineInjectable) + .callFn([injectableProps.toLiteralMap()], undefined, true); + return { + expression, + type: createInjectableType(meta), + statements: result.statements, }; - } - const token = meta.internalType; - const injectableProps = new DefinitionMap(); - injectableProps.set('token', token); - injectableProps.set('factory', result.expression); - // Only generate providedIn property if it has a non-null value - if (meta.providedIn.expression.value !== null) { - injectableProps.set( - 'providedIn', - convertFromMaybeForwardRefExpression(meta.providedIn) - ); - } - const expression = importExpr(Identifiers.ɵɵdefineInjectable).callFn( - [injectableProps.toLiteralMap()], - undefined, - true - ); - return { - expression, - type: createInjectableType(meta), - statements: result.statements, - }; } function createInjectableType(meta) { - return new ExpressionType( - importExpr(Identifiers.InjectableDeclaration, [ - typeWithParameters(meta.type.type, meta.typeArgumentCount), - ]) - ); + return new ExpressionType(importExpr(Identifiers.InjectableDeclaration, [typeWithParameters(meta.type.type, meta.typeArgumentCount)])); } function delegateToFactory(type, internalType, unwrapForwardRefs) { - if (type.node === internalType.node) { - // The types are the same, so we can simply delegate directly to the type's factory. - // ``` - // factory: type.ɵfac - // ``` - return internalType.prop('ɵfac'); - } - if (!unwrapForwardRefs) { - // The type is not wrapped in a `forwardRef()`, so we create a simple factory function that - // accepts a sub-type as an argument. + if (type.node === internalType.node) { + // The types are the same, so we can simply delegate directly to the type's factory. + // ``` + // factory: type.ɵfac + // ``` + return internalType.prop('ɵfac'); + } + if (!unwrapForwardRefs) { + // The type is not wrapped in a `forwardRef()`, so we create a simple factory function that + // accepts a sub-type as an argument. + // ``` + // factory: function(t) { return internalType.ɵfac(t); } + // ``` + return createFactoryFunction(internalType); + } + // The internalType is actually wrapped in a `forwardRef()` so we need to resolve that before + // calling its factory. // ``` - // factory: function(t) { return internalType.ɵfac(t); } + // factory: function(t) { return core.resolveForwardRef(type).ɵfac(t); } // ``` - return createFactoryFunction(internalType); - } - // The internalType is actually wrapped in a `forwardRef()` so we need to resolve that before - // calling its factory. - // ``` - // factory: function(t) { return core.resolveForwardRef(type).ɵfac(t); } - // ``` - const unwrappedType = importExpr(Identifiers.resolveForwardRef).callFn([ - internalType, - ]); - return createFactoryFunction(unwrappedType); + const unwrappedType = importExpr(Identifiers.resolveForwardRef).callFn([internalType]); + return createFactoryFunction(unwrappedType); } function createFactoryFunction(type) { - return fn( - [new FnParam('t', DYNAMIC_TYPE)], - [new ReturnStatement(type.prop('ɵfac').callFn([variable('t')]))] - ); + return fn([new FnParam('t', DYNAMIC_TYPE)], [new ReturnStatement(type.prop('ɵfac').callFn([variable('t')]))]); } /** @@ -6357,27 +5182,26 @@ * found in the LICENSE file at https://angular.io/license */ const UNUSABLE_INTERPOLATION_REGEXPS = [ - /^\s*$/, - /[<>]/, - /^[{}]$/, - /&(#|[a-z])/i, - /^\/\//, // comment + /^\s*$/, + /[<>]/, + /^[{}]$/, + /&(#|[a-z])/i, + /^\/\//, // comment ]; function assertInterpolationSymbols(identifier, value) { - if (value != null && !(Array.isArray(value) && value.length == 2)) { - throw new Error(`Expected '${identifier}' to be an array, [start, end].`); - } else if (value != null) { - const start = value[0]; - const end = value[1]; - // Check for unusable interpolation symbols - UNUSABLE_INTERPOLATION_REGEXPS.forEach((regexp) => { - if (regexp.test(start) || regexp.test(end)) { - throw new Error( - `['${start}', '${end}'] contains unusable interpolation symbol.` - ); - } - }); - } + if (value != null && !(Array.isArray(value) && value.length == 2)) { + throw new Error(`Expected '${identifier}' to be an array, [start, end].`); + } + else if (value != null) { + const start = value[0]; + const end = value[1]; + // Check for unusable interpolation symbols + UNUSABLE_INTERPOLATION_REGEXPS.forEach(regexp => { + if (regexp.test(start) || regexp.test(end)) { + throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`); + } + }); + } } /** @@ -6388,17 +5212,17 @@ * found in the LICENSE file at https://angular.io/license */ class InterpolationConfig { - constructor(start, end) { - this.start = start; - this.end = end; - } - static fromArray(markers) { - if (!markers) { - return DEFAULT_INTERPOLATION_CONFIG; - } - assertInterpolationSymbols('interpolation', markers); - return new InterpolationConfig(markers[0], markers[1]); - } + constructor(start, end) { + this.start = start; + this.end = end; + } + static fromArray(markers) { + if (!markers) { + return DEFAULT_INTERPOLATION_CONFIG; + } + assertInterpolationSymbols('interpolation', markers); + return new InterpolationConfig(markers[0], markers[1]); + } } const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}'); @@ -6468,27 +5292,25 @@ const $NBSP = 160; const $BT = 96; function isWhitespace(code) { - return (code >= $TAB && code <= $SPACE) || code == $NBSP; + return (code >= $TAB && code <= $SPACE) || (code == $NBSP); } function isDigit(code) { - return $0 <= code && code <= $9; + return $0 <= code && code <= $9; } function isAsciiLetter(code) { - return (code >= $a && code <= $z) || (code >= $A && code <= $Z); + return code >= $a && code <= $z || code >= $A && code <= $Z; } function isAsciiHexDigit(code) { - return ( - (code >= $a && code <= $f) || (code >= $A && code <= $F) || isDigit(code) - ); + return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code); } function isNewLine(code) { - return code === $LF || code === $CR; + return code === $LF || code === $CR; } function isOctalDigit(code) { - return $0 <= code && code <= $7; + return $0 <= code && code <= $7; } function isQuote(code) { - return code === $SQ || code === $DQ || code === $BT; + return code === $SQ || code === $DQ || code === $BT; } /** @@ -6499,156 +5321,148 @@ * found in the LICENSE file at https://angular.io/license */ class ParseLocation { - constructor(file, offset, line, col) { - this.file = file; - this.offset = offset; - this.line = line; - this.col = col; - } - toString() { - return this.offset != null - ? `${this.file.url}@${this.line}:${this.col}` - : this.file.url; - } - moveBy(delta) { - const source = this.file.content; - const len = source.length; - let offset = this.offset; - let line = this.line; - let col = this.col; - while (offset > 0 && delta < 0) { - offset--; - delta++; - const ch = source.charCodeAt(offset); - if (ch == $LF) { - line--; - const priorLine = source - .substring(0, offset - 1) - .lastIndexOf(String.fromCharCode($LF)); - col = priorLine > 0 ? offset - priorLine : offset; - } else { - col--; - } - } - while (offset < len && delta > 0) { - const ch = source.charCodeAt(offset); - offset++; - delta--; - if (ch == $LF) { - line++; - col = 0; - } else { - col++; - } - } - return new ParseLocation(this.file, offset, line, col); - } - // Return the source around the location - // Up to `maxChars` or `maxLines` on each side of the location - getContext(maxChars, maxLines) { - const content = this.file.content; - let startOffset = this.offset; - if (startOffset != null) { - if (startOffset > content.length - 1) { - startOffset = content.length - 1; - } - let endOffset = startOffset; - let ctxChars = 0; - let ctxLines = 0; - while (ctxChars < maxChars && startOffset > 0) { - startOffset--; - ctxChars++; - if (content[startOffset] == '\n') { - if (++ctxLines == maxLines) { - break; - } + constructor(file, offset, line, col) { + this.file = file; + this.offset = offset; + this.line = line; + this.col = col; + } + toString() { + return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url; + } + moveBy(delta) { + const source = this.file.content; + const len = source.length; + let offset = this.offset; + let line = this.line; + let col = this.col; + while (offset > 0 && delta < 0) { + offset--; + delta++; + const ch = source.charCodeAt(offset); + if (ch == $LF) { + line--; + const priorLine = source.substring(0, offset - 1).lastIndexOf(String.fromCharCode($LF)); + col = priorLine > 0 ? offset - priorLine : offset; + } + else { + col--; + } } - } - ctxChars = 0; - ctxLines = 0; - while (ctxChars < maxChars && endOffset < content.length - 1) { - endOffset++; - ctxChars++; - if (content[endOffset] == '\n') { - if (++ctxLines == maxLines) { - break; - } + while (offset < len && delta > 0) { + const ch = source.charCodeAt(offset); + offset++; + delta--; + if (ch == $LF) { + line++; + col = 0; + } + else { + col++; + } } - } - return { - before: content.substring(startOffset, this.offset), - after: content.substring(this.offset, endOffset + 1), - }; - } - return null; - } - } - class ParseSourceFile { - constructor(content, url) { - this.content = content; - this.url = url; - } + return new ParseLocation(this.file, offset, line, col); + } + // Return the source around the location + // Up to `maxChars` or `maxLines` on each side of the location + getContext(maxChars, maxLines) { + const content = this.file.content; + let startOffset = this.offset; + if (startOffset != null) { + if (startOffset > content.length - 1) { + startOffset = content.length - 1; + } + let endOffset = startOffset; + let ctxChars = 0; + let ctxLines = 0; + while (ctxChars < maxChars && startOffset > 0) { + startOffset--; + ctxChars++; + if (content[startOffset] == '\n') { + if (++ctxLines == maxLines) { + break; + } + } + } + ctxChars = 0; + ctxLines = 0; + while (ctxChars < maxChars && endOffset < content.length - 1) { + endOffset++; + ctxChars++; + if (content[endOffset] == '\n') { + if (++ctxLines == maxLines) { + break; + } + } + } + return { + before: content.substring(startOffset, this.offset), + after: content.substring(this.offset, endOffset + 1), + }; + } + return null; + } + } + class ParseSourceFile { + constructor(content, url) { + this.content = content; + this.url = url; + } } class ParseSourceSpan { - /** - * Create an object that holds information about spans of tokens/nodes captured during - * lexing/parsing of text. - * - * @param start - * The location of the start of the span (having skipped leading trivia). - * Skipping leading trivia makes source-spans more "user friendly", since things like HTML - * elements will appear to begin at the start of the opening tag, rather than at the start of any - * leading trivia, which could include newlines. - * - * @param end - * The location of the end of the span. - * - * @param fullStart - * The start of the token without skipping the leading trivia. - * This is used by tooling that splits tokens further, such as extracting Angular interpolations - * from text tokens. Such tooling creates new source-spans relative to the original token's - * source-span. If leading trivia characters have been skipped then the new source-spans may be - * incorrectly offset. - * - * @param details - * Additional information (such as identifier names) that should be associated with the span. - */ - constructor(start, end, fullStart = start, details = null) { - this.start = start; - this.end = end; - this.fullStart = fullStart; - this.details = details; - } - toString() { - return this.start.file.content.substring( - this.start.offset, - this.end.offset - ); - } + /** + * Create an object that holds information about spans of tokens/nodes captured during + * lexing/parsing of text. + * + * @param start + * The location of the start of the span (having skipped leading trivia). + * Skipping leading trivia makes source-spans more "user friendly", since things like HTML + * elements will appear to begin at the start of the opening tag, rather than at the start of any + * leading trivia, which could include newlines. + * + * @param end + * The location of the end of the span. + * + * @param fullStart + * The start of the token without skipping the leading trivia. + * This is used by tooling that splits tokens further, such as extracting Angular interpolations + * from text tokens. Such tooling creates new source-spans relative to the original token's + * source-span. If leading trivia characters have been skipped then the new source-spans may be + * incorrectly offset. + * + * @param details + * Additional information (such as identifier names) that should be associated with the span. + */ + constructor(start, end, fullStart = start, details = null) { + this.start = start; + this.end = end; + this.fullStart = fullStart; + this.details = details; + } + toString() { + return this.start.file.content.substring(this.start.offset, this.end.offset); + } } exports.ParseErrorLevel = void 0; (function (ParseErrorLevel) { - ParseErrorLevel[(ParseErrorLevel['WARNING'] = 0)] = 'WARNING'; - ParseErrorLevel[(ParseErrorLevel['ERROR'] = 1)] = 'ERROR'; + ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING"; + ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR"; })(exports.ParseErrorLevel || (exports.ParseErrorLevel = {})); class ParseError { - constructor(span, msg, level = exports.ParseErrorLevel.ERROR) { - this.span = span; - this.msg = msg; - this.level = level; - } - contextualMessage() { - const ctx = this.span.start.getContext(100, 3); - return ctx - ? `${this.msg} ("${ctx.before}[${ - exports.ParseErrorLevel[this.level] - } ->]${ctx.after}")` - : this.msg; - } - toString() { - const details = this.span.details ? `, ${this.span.details}` : ''; - return `${this.contextualMessage()}: ${this.span.start}${details}`; - } + constructor(span, msg, level = exports.ParseErrorLevel.ERROR) { + this.span = span; + this.msg = msg; + this.level = level; + } + contextualMessage() { + const ctx = this.span.start.getContext(100, 3); + return ctx ? `${this.msg} ("${ctx.before}[${exports.ParseErrorLevel[this.level]} ->]${ctx.after}")` : + this.msg; + } + toString() { + const details = this.span.details ? `, ${this.span.details}` : ''; + return `${this.contextualMessage()}: ${this.span.start}${details}`; + } } /** * Generates Source Span object for a given R3 Type for JIT mode. @@ -6659,39 +5473,37 @@ * @returns instance of ParseSourceSpan that represent a given Component or Directive. */ function r3JitTypeSourceSpan(kind, typeName, sourceUrl) { - const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`; - const sourceFile = new ParseSourceFile('', sourceFileName); - return new ParseSourceSpan( - new ParseLocation(sourceFile, -1, -1, -1), - new ParseLocation(sourceFile, -1, -1, -1) - ); + const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`; + const sourceFile = new ParseSourceFile('', sourceFileName); + return new ParseSourceSpan(new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1)); } let _anonymousTypeIndex = 0; function identifierName(compileIdentifier) { - if (!compileIdentifier || !compileIdentifier.reference) { - return null; - } - const ref = compileIdentifier.reference; - if (ref['__anonymousType']) { - return ref['__anonymousType']; - } - if (ref['__forward_ref__']) { - // We do not want to try to stringify a `forwardRef()` function because that would cause the - // inner function to be evaluated too early, defeating the whole point of the `forwardRef`. - return '__forward_ref__'; - } - let identifier = stringify(ref); - if (identifier.indexOf('(') >= 0) { - // case: anonymous functions! - identifier = `anonymous_${_anonymousTypeIndex++}`; - ref['__anonymousType'] = identifier; - } else { - identifier = sanitizeIdentifier(identifier); - } - return identifier; + if (!compileIdentifier || !compileIdentifier.reference) { + return null; + } + const ref = compileIdentifier.reference; + if (ref['__anonymousType']) { + return ref['__anonymousType']; + } + if (ref['__forward_ref__']) { + // We do not want to try to stringify a `forwardRef()` function because that would cause the + // inner function to be evaluated too early, defeating the whole point of the `forwardRef`. + return '__forward_ref__'; + } + let identifier = stringify(ref); + if (identifier.indexOf('(') >= 0) { + // case: anonymous functions! + identifier = `anonymous_${_anonymousTypeIndex++}`; + ref['__anonymousType'] = identifier; + } + else { + identifier = sanitizeIdentifier(identifier); + } + return identifier; } function sanitizeIdentifier(name) { - return name.replace(/\W/g, '_'); + return name.replace(/\W/g, '_'); } /** @@ -6713,116 +5525,90 @@ * In the inline function, if `Object.defineProperty` is available we use that to attach the `raw` * array. */ - const makeTemplateObjectPolyfill = - '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})'; + const makeTemplateObjectPolyfill = '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})'; class AbstractJsEmitterVisitor extends AbstractEmitterVisitor { - constructor() { - super(false); - } - visitWrappedNodeExpr(ast, ctx) { - throw new Error('Cannot emit a WrappedNodeExpr in Javascript.'); - } - visitDeclareVarStmt(stmt, ctx) { - ctx.print(stmt, `var ${stmt.name}`); - if (stmt.value) { - ctx.print(stmt, ' = '); - stmt.value.visitExpression(this, ctx); - } - ctx.println(stmt, `;`); - return null; - } - visitTaggedTemplateExpr(ast, ctx) { - // The following convoluted piece of code is effectively the downlevelled equivalent of - // ``` - // tag`...` - // ``` - // which is effectively like: - // ``` - // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...); - // ``` - const elements = ast.template.elements; - ast.tag.visitExpression(this, ctx); - ctx.print(ast, `(${makeTemplateObjectPolyfill}(`); - ctx.print( - ast, - `[${elements - .map((part) => escapeIdentifier(part.text, false)) - .join(', ')}], ` - ); - ctx.print( - ast, - `[${elements - .map((part) => escapeIdentifier(part.rawText, false)) - .join(', ')}])` - ); - ast.template.expressions.forEach((expression) => { - ctx.print(ast, ', '); - expression.visitExpression(this, ctx); - }); - ctx.print(ast, ')'); - return null; - } - visitFunctionExpr(ast, ctx) { - ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`); - this._visitParams(ast.params, ctx); - ctx.println(ast, `) {`); - ctx.incIndent(); - this.visitAllStatements(ast.statements, ctx); - ctx.decIndent(); - ctx.print(ast, `}`); - return null; - } - visitDeclareFunctionStmt(stmt, ctx) { - ctx.print(stmt, `function ${stmt.name}(`); - this._visitParams(stmt.params, ctx); - ctx.println(stmt, `) {`); - ctx.incIndent(); - this.visitAllStatements(stmt.statements, ctx); - ctx.decIndent(); - ctx.println(stmt, `}`); - return null; - } - visitLocalizedString(ast, ctx) { - // The following convoluted piece of code is effectively the downlevelled equivalent of - // ``` - // $localize `...` - // ``` - // which is effectively like: - // ``` - // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...); - // ``` - ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`); - const parts = [ast.serializeI18nHead()]; - for (let i = 1; i < ast.messageParts.length; i++) { - parts.push(ast.serializeI18nTemplatePart(i)); - } - ctx.print( - ast, - `[${parts - .map((part) => escapeIdentifier(part.cooked, false)) - .join(', ')}], ` - ); - ctx.print( - ast, - `[${parts - .map((part) => escapeIdentifier(part.raw, false)) - .join(', ')}])` - ); - ast.expressions.forEach((expression) => { - ctx.print(ast, ', '); - expression.visitExpression(this, ctx); - }); - ctx.print(ast, ')'); - return null; - } - _visitParams(params, ctx) { - this.visitAllObjects( - (param) => ctx.print(null, param.name), - params, - ctx, - ',' - ); - } + constructor() { + super(false); + } + visitWrappedNodeExpr(ast, ctx) { + throw new Error('Cannot emit a WrappedNodeExpr in Javascript.'); + } + visitDeclareVarStmt(stmt, ctx) { + ctx.print(stmt, `var ${stmt.name}`); + if (stmt.value) { + ctx.print(stmt, ' = '); + stmt.value.visitExpression(this, ctx); + } + ctx.println(stmt, `;`); + return null; + } + visitTaggedTemplateExpr(ast, ctx) { + // The following convoluted piece of code is effectively the downlevelled equivalent of + // ``` + // tag`...` + // ``` + // which is effectively like: + // ``` + // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...); + // ``` + const elements = ast.template.elements; + ast.tag.visitExpression(this, ctx); + ctx.print(ast, `(${makeTemplateObjectPolyfill}(`); + ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `); + ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`); + ast.template.expressions.forEach(expression => { + ctx.print(ast, ', '); + expression.visitExpression(this, ctx); + }); + ctx.print(ast, ')'); + return null; + } + visitFunctionExpr(ast, ctx) { + ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`); + this._visitParams(ast.params, ctx); + ctx.println(ast, `) {`); + ctx.incIndent(); + this.visitAllStatements(ast.statements, ctx); + ctx.decIndent(); + ctx.print(ast, `}`); + return null; + } + visitDeclareFunctionStmt(stmt, ctx) { + ctx.print(stmt, `function ${stmt.name}(`); + this._visitParams(stmt.params, ctx); + ctx.println(stmt, `) {`); + ctx.incIndent(); + this.visitAllStatements(stmt.statements, ctx); + ctx.decIndent(); + ctx.println(stmt, `}`); + return null; + } + visitLocalizedString(ast, ctx) { + // The following convoluted piece of code is effectively the downlevelled equivalent of + // ``` + // $localize `...` + // ``` + // which is effectively like: + // ``` + // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...); + // ``` + ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`); + const parts = [ast.serializeI18nHead()]; + for (let i = 1; i < ast.messageParts.length; i++) { + parts.push(ast.serializeI18nTemplatePart(i)); + } + ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `); + ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`); + ast.expressions.forEach(expression => { + ctx.print(ast, ', '); + expression.visitExpression(this, ctx); + }); + ctx.print(ast, ')'); + return null; + } + _visitParams(params, ctx) { + this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ','); + } } /** @@ -6842,22 +5628,24 @@ * enabled/supported. The first call to this function will create the policy. */ function getPolicy() { - if (policy === undefined) { - policy = null; - if (_global.trustedTypes) { - try { - policy = _global.trustedTypes.createPolicy('angular#unsafe-jit', { - createScript: (s) => s, - }); - } catch (_a) { - // trustedTypes.createPolicy throws if called with a name that is - // already registered, even in report-only mode. Until the API changes, - // catch the error not to break the applications functionally. In such - // cases, the code will fall back to using strings. - } + if (policy === undefined) { + policy = null; + if (_global.trustedTypes) { + try { + policy = + _global.trustedTypes.createPolicy('angular#unsafe-jit', { + createScript: (s) => s, + }); + } + catch (_a) { + // trustedTypes.createPolicy throws if called with a name that is + // already registered, even in report-only mode. Until the API changes, + // catch the error not to break the applications functionally. In such + // cases, the code will fall back to using strings. + } + } } - } - return policy; + return policy; } /** * Unsafely promote a string to a TrustedScript, falling back to strings when @@ -6867,12 +5655,8 @@ * interpreted and executed as a script by a browser, e.g. when calling eval. */ function trustedScriptFromString(script) { - var _a; - return ( - ((_a = getPolicy()) === null || _a === void 0 - ? void 0 - : _a.createScript(script)) || script - ); + var _a; + return ((_a = getPolicy()) === null || _a === void 0 ? void 0 : _a.createScript(script)) || script; } /** * Unsafely call the Function constructor with the given string arguments. @@ -6882,40 +5666,40 @@ * vulnerabilities. */ function newTrustedFunctionForJIT(...args) { - if (!_global.trustedTypes) { - // In environments that don't support Trusted Types, fall back to the most - // straightforward implementation: - return new Function(...args); - } - // Chrome currently does not support passing TrustedScript to the Function - // constructor. The following implements the workaround proposed on the page - // below, where the Chromium bug is also referenced: - // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor - const fnArgs = args.slice(0, -1).join(','); - const fnBody = args[args.length - 1]; - const body = `(function anonymous(${fnArgs} + if (!_global.trustedTypes) { + // In environments that don't support Trusted Types, fall back to the most + // straightforward implementation: + return new Function(...args); + } + // Chrome currently does not support passing TrustedScript to the Function + // constructor. The following implements the workaround proposed on the page + // below, where the Chromium bug is also referenced: + // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor + const fnArgs = args.slice(0, -1).join(','); + const fnBody = args[args.length - 1]; + const body = `(function anonymous(${fnArgs} ) { ${fnBody} })`; - // Using eval directly confuses the compiler and prevents this module from - // being stripped out of JS binaries even if not used. The global['eval'] - // indirection fixes that. - const fn = _global['eval'](trustedScriptFromString(body)); - if (fn.bind === undefined) { - // Workaround for a browser bug that only exists in Chrome 83, where passing - // a TrustedScript to eval just returns the TrustedScript back without - // evaluating it. In that case, fall back to the most straightforward - // implementation: - return new Function(...args); - } - // To completely mimic the behavior of calling "new Function", two more - // things need to happen: - // 1. Stringifying the resulting function should return its source code - fn.toString = () => body; - // 2. When calling the resulting function, `this` should refer to `global` - return fn.bind(_global); - // When Trusted Types support in Function constructors is widely available, - // the implementation of this function can be simplified to: - // return new Function(...args.map(a => trustedScriptFromString(a))); + // Using eval directly confuses the compiler and prevents this module from + // being stripped out of JS binaries even if not used. The global['eval'] + // indirection fixes that. + const fn = _global['eval'](trustedScriptFromString(body)); + if (fn.bind === undefined) { + // Workaround for a browser bug that only exists in Chrome 83, where passing + // a TrustedScript to eval just returns the TrustedScript back without + // evaluating it. In that case, fall back to the most straightforward + // implementation: + return new Function(...args); + } + // To completely mimic the behavior of calling "new Function", two more + // things need to happen: + // 1. Stringifying the resulting function should return its source code + fn.toString = () => body; + // 2. When calling the resulting function, `this` should refer to `global` + return fn.bind(_global); + // When Trusted Types support in Function constructors is widely available, + // the implementation of this function can be simplified to: + // return new Function(...args.map(a => trustedScriptFromString(a))); } /** @@ -6929,148 +5713,129 @@ * A helper class to manage the evaluation of JIT generated code. */ class JitEvaluator { - /** - * - * @param sourceUrl The URL of the generated code. - * @param statements An array of Angular statement AST nodes to be evaluated. - * @param refResolver Resolves `o.ExternalReference`s into values. - * @param createSourceMaps If true then create a source-map for the generated code and include it - * inline as a source-map comment. - * @returns A map of all the variables in the generated code. - */ - evaluateStatements(sourceUrl, statements, refResolver, createSourceMaps) { - const converter = new JitEmitterVisitor(refResolver); - const ctx = EmitterVisitorContext.createRoot(); - // Ensure generated code is in strict mode - if (statements.length > 0 && !isUseStrictStatement(statements[0])) { - statements = [literal('use strict').toStmt(), ...statements]; - } - converter.visitAllStatements(statements, ctx); - converter.createReturnStmt(ctx); - return this.evaluateCode( - sourceUrl, - ctx, - converter.getArgs(), - createSourceMaps - ); - } - /** - * Evaluate a piece of JIT generated code. - * @param sourceUrl The URL of this generated code. - * @param ctx A context object that contains an AST of the code to be evaluated. - * @param vars A map containing the names and values of variables that the evaluated code might - * reference. - * @param createSourceMap If true then create a source-map for the generated code and include it - * inline as a source-map comment. - * @returns The result of evaluating the code. - */ - evaluateCode(sourceUrl, ctx, vars, createSourceMap) { - let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`; - const fnArgNames = []; - const fnArgValues = []; - for (const argName in vars) { - fnArgValues.push(vars[argName]); - fnArgNames.push(argName); - } - if (createSourceMap) { - // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise - // E.g. ``` - // function anonymous(a,b,c - // /**/) { ... }``` - // We don't want to hard code this fact, so we auto detect it via an empty function first. - const emptyFn = newTrustedFunctionForJIT( - ...fnArgNames.concat('return null;') - ).toString(); - const headerLines = - emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - - 1; - fnBody += `\n${ctx - .toSourceMapGenerator(sourceUrl, headerLines) - .toJsComment()}`; - } - const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody)); - return this.executeFunction(fn, fnArgValues); - } - /** - * Execute a JIT generated function by calling it. - * - * This method can be overridden in tests to capture the functions that are generated - * by this `JitEvaluator` class. - * - * @param fn A function to execute. - * @param args The arguments to pass to the function being executed. - * @returns The return value of the executed function. - */ - executeFunction(fn, args) { - return fn(...args); - } + /** + * + * @param sourceUrl The URL of the generated code. + * @param statements An array of Angular statement AST nodes to be evaluated. + * @param refResolver Resolves `o.ExternalReference`s into values. + * @param createSourceMaps If true then create a source-map for the generated code and include it + * inline as a source-map comment. + * @returns A map of all the variables in the generated code. + */ + evaluateStatements(sourceUrl, statements, refResolver, createSourceMaps) { + const converter = new JitEmitterVisitor(refResolver); + const ctx = EmitterVisitorContext.createRoot(); + // Ensure generated code is in strict mode + if (statements.length > 0 && !isUseStrictStatement(statements[0])) { + statements = [ + literal('use strict').toStmt(), + ...statements, + ]; + } + converter.visitAllStatements(statements, ctx); + converter.createReturnStmt(ctx); + return this.evaluateCode(sourceUrl, ctx, converter.getArgs(), createSourceMaps); + } + /** + * Evaluate a piece of JIT generated code. + * @param sourceUrl The URL of this generated code. + * @param ctx A context object that contains an AST of the code to be evaluated. + * @param vars A map containing the names and values of variables that the evaluated code might + * reference. + * @param createSourceMap If true then create a source-map for the generated code and include it + * inline as a source-map comment. + * @returns The result of evaluating the code. + */ + evaluateCode(sourceUrl, ctx, vars, createSourceMap) { + let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`; + const fnArgNames = []; + const fnArgValues = []; + for (const argName in vars) { + fnArgValues.push(vars[argName]); + fnArgNames.push(argName); + } + if (createSourceMap) { + // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise + // E.g. ``` + // function anonymous(a,b,c + // /**/) { ... }``` + // We don't want to hard code this fact, so we auto detect it via an empty function first. + const emptyFn = newTrustedFunctionForJIT(...fnArgNames.concat('return null;')).toString(); + const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1; + fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`; + } + const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody)); + return this.executeFunction(fn, fnArgValues); + } + /** + * Execute a JIT generated function by calling it. + * + * This method can be overridden in tests to capture the functions that are generated + * by this `JitEvaluator` class. + * + * @param fn A function to execute. + * @param args The arguments to pass to the function being executed. + * @returns The return value of the executed function. + */ + executeFunction(fn, args) { + return fn(...args); + } } /** * An Angular AST visitor that converts AST nodes into executable JavaScript code. */ class JitEmitterVisitor extends AbstractJsEmitterVisitor { - constructor(refResolver) { - super(); - this.refResolver = refResolver; - this._evalArgNames = []; - this._evalArgValues = []; - this._evalExportedVars = []; - } - createReturnStmt(ctx) { - const stmt = new ReturnStatement( - new LiteralMapExpr( - this._evalExportedVars.map( - (resultVar) => - new LiteralMapEntry(resultVar, variable(resultVar), false) - ) - ) - ); - stmt.visitStatement(this, ctx); - } - getArgs() { - const result = {}; - for (let i = 0; i < this._evalArgNames.length; i++) { - result[this._evalArgNames[i]] = this._evalArgValues[i]; + constructor(refResolver) { + super(); + this.refResolver = refResolver; + this._evalArgNames = []; + this._evalArgValues = []; + this._evalExportedVars = []; + } + createReturnStmt(ctx) { + const stmt = new ReturnStatement(new LiteralMapExpr(this._evalExportedVars.map(resultVar => new LiteralMapEntry(resultVar, variable(resultVar), false)))); + stmt.visitStatement(this, ctx); + } + getArgs() { + const result = {}; + for (let i = 0; i < this._evalArgNames.length; i++) { + result[this._evalArgNames[i]] = this._evalArgValues[i]; + } + return result; + } + visitExternalExpr(ast, ctx) { + this._emitReferenceToExternal(ast, this.refResolver.resolveExternalReference(ast.value), ctx); + return null; + } + visitWrappedNodeExpr(ast, ctx) { + this._emitReferenceToExternal(ast, ast.node, ctx); + return null; + } + visitDeclareVarStmt(stmt, ctx) { + if (stmt.hasModifier(exports.StmtModifier.Exported)) { + this._evalExportedVars.push(stmt.name); + } + return super.visitDeclareVarStmt(stmt, ctx); + } + visitDeclareFunctionStmt(stmt, ctx) { + if (stmt.hasModifier(exports.StmtModifier.Exported)) { + this._evalExportedVars.push(stmt.name); + } + return super.visitDeclareFunctionStmt(stmt, ctx); + } + _emitReferenceToExternal(ast, value, ctx) { + let id = this._evalArgValues.indexOf(value); + if (id === -1) { + id = this._evalArgValues.length; + this._evalArgValues.push(value); + const name = identifierName({ reference: value }) || 'val'; + this._evalArgNames.push(`jit_${name}_${id}`); + } + ctx.print(ast, this._evalArgNames[id]); } - return result; - } - visitExternalExpr(ast, ctx) { - this._emitReferenceToExternal( - ast, - this.refResolver.resolveExternalReference(ast.value), - ctx - ); - return null; - } - visitWrappedNodeExpr(ast, ctx) { - this._emitReferenceToExternal(ast, ast.node, ctx); - return null; - } - visitDeclareVarStmt(stmt, ctx) { - if (stmt.hasModifier(exports.StmtModifier.Exported)) { - this._evalExportedVars.push(stmt.name); - } - return super.visitDeclareVarStmt(stmt, ctx); - } - visitDeclareFunctionStmt(stmt, ctx) { - if (stmt.hasModifier(exports.StmtModifier.Exported)) { - this._evalExportedVars.push(stmt.name); - } - return super.visitDeclareFunctionStmt(stmt, ctx); - } - _emitReferenceToExternal(ast, value, ctx) { - let id = this._evalArgValues.indexOf(value); - if (id === -1) { - id = this._evalArgValues.length; - this._evalArgValues.push(value); - const name = identifierName({ reference: value }) || 'val'; - this._evalArgNames.push(`jit_${name}_${id}`); - } - ctx.print(ast, this._evalArgNames[id]); - } } function isUseStrictStatement(statement) { - return statement.isEquivalent(literal('use strict').toStmt()); + return statement.isEquivalent(literal('use strict').toStmt()); } /** @@ -7081,27 +5846,19 @@ * found in the LICENSE file at https://angular.io/license */ function compileInjector(meta) { - const definitionMap = new DefinitionMap(); - if (meta.providers !== null) { - definitionMap.set('providers', meta.providers); - } - if (meta.imports.length > 0) { - definitionMap.set('imports', literalArr(meta.imports)); - } - const expression = importExpr(Identifiers.defineInjector).callFn( - [definitionMap.toLiteralMap()], - undefined, - true - ); - const type = createInjectorType(meta); - return { expression, type, statements: [] }; + const definitionMap = new DefinitionMap(); + if (meta.providers !== null) { + definitionMap.set('providers', meta.providers); + } + if (meta.imports.length > 0) { + definitionMap.set('imports', literalArr(meta.imports)); + } + const expression = importExpr(Identifiers.defineInjector).callFn([definitionMap.toLiteralMap()], undefined, true); + const type = createInjectorType(meta); + return { expression, type, statements: [] }; } function createInjectorType(meta) { - return new ExpressionType( - importExpr(Identifiers.InjectorDeclaration, [ - new ExpressionType(meta.type.type), - ]) - ); + return new ExpressionType(importExpr(Identifiers.InjectorDeclaration, [new ExpressionType(meta.type.type)])); } /** @@ -7118,23 +5875,19 @@ * Only supports `resolveExternalReference`, all other methods throw. */ class R3JitReflector { - constructor(context) { - this.context = context; - } - resolveExternalReference(ref) { - // This reflector only handles @angular/core imports. - if (ref.moduleName !== '@angular/core') { - throw new Error( - `Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.` - ); - } - if (!this.context.hasOwnProperty(ref.name)) { - throw new Error( - `No value provided for @angular/core symbol '${ref.name}'.` - ); - } - return this.context[ref.name]; - } + constructor(context) { + this.context = context; + } + resolveExternalReference(ref) { + // This reflector only handles @angular/core imports. + if (ref.moduleName !== '@angular/core') { + throw new Error(`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`); + } + if (!this.context.hasOwnProperty(ref.name)) { + throw new Error(`No value provided for @angular/core symbol '${ref.name}'.`); + } + return this.context[ref.name]; + } } /** @@ -7150,153 +5903,112 @@ */ exports.R3SelectorScopeMode = void 0; (function (R3SelectorScopeMode) { - /** - * Emit the declarations inline into the module definition. - * - * This option is useful in certain contexts where it's known that JIT support is required. The - * tradeoff here is that this emit style prevents directives and pipes from being tree-shaken if - * they are unused, but the NgModule is used. - */ - R3SelectorScopeMode[(R3SelectorScopeMode['Inline'] = 0)] = 'Inline'; - /** - * Emit the declarations using a side effectful function call, `ɵɵsetNgModuleScope`, that is - * guarded with the `ngJitMode` flag. - * - * This form of emit supports JIT and can be optimized away if the `ngJitMode` flag is set to - * false, which allows unused directives and pipes to be tree-shaken. - */ - R3SelectorScopeMode[(R3SelectorScopeMode['SideEffect'] = 1)] = 'SideEffect'; - /** - * Don't generate selector scopes at all. - * - * This is useful for contexts where JIT support is known to be unnecessary. - */ - R3SelectorScopeMode[(R3SelectorScopeMode['Omit'] = 2)] = 'Omit'; + /** + * Emit the declarations inline into the module definition. + * + * This option is useful in certain contexts where it's known that JIT support is required. The + * tradeoff here is that this emit style prevents directives and pipes from being tree-shaken if + * they are unused, but the NgModule is used. + */ + R3SelectorScopeMode[R3SelectorScopeMode["Inline"] = 0] = "Inline"; + /** + * Emit the declarations using a side effectful function call, `ɵɵsetNgModuleScope`, that is + * guarded with the `ngJitMode` flag. + * + * This form of emit supports JIT and can be optimized away if the `ngJitMode` flag is set to + * false, which allows unused directives and pipes to be tree-shaken. + */ + R3SelectorScopeMode[R3SelectorScopeMode["SideEffect"] = 1] = "SideEffect"; + /** + * Don't generate selector scopes at all. + * + * This is useful for contexts where JIT support is known to be unnecessary. + */ + R3SelectorScopeMode[R3SelectorScopeMode["Omit"] = 2] = "Omit"; })(exports.R3SelectorScopeMode || (exports.R3SelectorScopeMode = {})); /** * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`. */ function compileNgModule(meta) { - const { - adjacentType, - internalType, - bootstrap, - declarations, - imports, - exports: exports$1, - schemas, - containsForwardDecls, - selectorScopeMode, - id, - } = meta; - const statements = []; - const definitionMap = new DefinitionMap(); - definitionMap.set('type', internalType); - if (bootstrap.length > 0) { - definitionMap.set( - 'bootstrap', - refsToArray(bootstrap, containsForwardDecls) - ); - } - if (selectorScopeMode === exports.R3SelectorScopeMode.Inline) { - // If requested to emit scope information inline, pass the `declarations`, `imports` and - // `exports` to the `ɵɵdefineNgModule()` call directly. - if (declarations.length > 0) { - definitionMap.set( - 'declarations', - refsToArray(declarations, containsForwardDecls) - ); + const { adjacentType, internalType, bootstrap, declarations, imports, exports: exports$1, schemas, containsForwardDecls, selectorScopeMode, id } = meta; + const statements = []; + const definitionMap = new DefinitionMap(); + definitionMap.set('type', internalType); + if (bootstrap.length > 0) { + definitionMap.set('bootstrap', refsToArray(bootstrap, containsForwardDecls)); + } + if (selectorScopeMode === exports.R3SelectorScopeMode.Inline) { + // If requested to emit scope information inline, pass the `declarations`, `imports` and + // `exports` to the `ɵɵdefineNgModule()` call directly. + if (declarations.length > 0) { + definitionMap.set('declarations', refsToArray(declarations, containsForwardDecls)); + } + if (imports.length > 0) { + definitionMap.set('imports', refsToArray(imports, containsForwardDecls)); + } + if (exports$1.length > 0) { + definitionMap.set('exports', refsToArray(exports$1, containsForwardDecls)); + } } - if (imports.length > 0) { - definitionMap.set( - 'imports', - refsToArray(imports, containsForwardDecls) - ); - } - if (exports$1.length > 0) { - definitionMap.set( - 'exports', - refsToArray(exports$1, containsForwardDecls) - ); - } - } else if (selectorScopeMode === exports.R3SelectorScopeMode.SideEffect) { - // In this mode, scope information is not passed into `ɵɵdefineNgModule` as it - // would prevent tree-shaking of the declarations, imports and exports references. Instead, it's - // patched onto the NgModule definition with a `ɵɵsetNgModuleScope` call that's guarded by the - // `ngJitMode` flag. - const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta); - if (setNgModuleScopeCall !== null) { - statements.push(setNgModuleScopeCall); - } - } else; - if (schemas !== null && schemas.length > 0) { - definitionMap.set('schemas', literalArr(schemas.map((ref) => ref.value))); - } - if (id !== null) { - definitionMap.set('id', id); - // Generate a side-effectful call to register this NgModule by its id, as per the semantics of - // NgModule ids. - statements.push( - importExpr(Identifiers.registerNgModuleType) - .callFn([adjacentType, id]) - .toStmt() - ); - } - const expression = importExpr(Identifiers.defineNgModule).callFn( - [definitionMap.toLiteralMap()], - undefined, - true - ); - const type = createNgModuleType(meta); - return { expression, type, statements }; + else if (selectorScopeMode === exports.R3SelectorScopeMode.SideEffect) { + // In this mode, scope information is not passed into `ɵɵdefineNgModule` as it + // would prevent tree-shaking of the declarations, imports and exports references. Instead, it's + // patched onto the NgModule definition with a `ɵɵsetNgModuleScope` call that's guarded by the + // `ngJitMode` flag. + const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta); + if (setNgModuleScopeCall !== null) { + statements.push(setNgModuleScopeCall); + } + } + else ; + if (schemas !== null && schemas.length > 0) { + definitionMap.set('schemas', literalArr(schemas.map(ref => ref.value))); + } + if (id !== null) { + definitionMap.set('id', id); + // Generate a side-effectful call to register this NgModule by its id, as per the semantics of + // NgModule ids. + statements.push(importExpr(Identifiers.registerNgModuleType).callFn([adjacentType, id]).toStmt()); + } + const expression = importExpr(Identifiers.defineNgModule).callFn([definitionMap.toLiteralMap()], undefined, true); + const type = createNgModuleType(meta); + return { expression, type, statements }; } /** * This function is used in JIT mode to generate the call to `ɵɵdefineNgModule()` from a call to * `ɵɵngDeclareNgModule()`. */ function compileNgModuleDeclarationExpression(meta) { - const definitionMap = new DefinitionMap(); - definitionMap.set('type', new WrappedNodeExpr(meta.type)); - if (meta.bootstrap !== undefined) { - definitionMap.set('bootstrap', new WrappedNodeExpr(meta.bootstrap)); - } - if (meta.declarations !== undefined) { - definitionMap.set('declarations', new WrappedNodeExpr(meta.declarations)); - } - if (meta.imports !== undefined) { - definitionMap.set('imports', new WrappedNodeExpr(meta.imports)); - } - if (meta.exports !== undefined) { - definitionMap.set('exports', new WrappedNodeExpr(meta.exports)); - } - if (meta.schemas !== undefined) { - definitionMap.set('schemas', new WrappedNodeExpr(meta.schemas)); - } - if (meta.id !== undefined) { - definitionMap.set('id', new WrappedNodeExpr(meta.id)); - } - return importExpr(Identifiers.defineNgModule).callFn([ - definitionMap.toLiteralMap(), - ]); - } - function createNgModuleType({ - type: moduleType, - declarations, - exports, - imports, - includeImportTypes, - publicDeclarationTypes, - }) { - return new ExpressionType( - importExpr(Identifiers.NgModuleDeclaration, [ - new ExpressionType(moduleType.type), - publicDeclarationTypes === null - ? tupleTypeOf(declarations) - : tupleOfTypes(publicDeclarationTypes), - includeImportTypes ? tupleTypeOf(imports) : NONE_TYPE, - tupleTypeOf(exports), - ]) - ); + const definitionMap = new DefinitionMap(); + definitionMap.set('type', new WrappedNodeExpr(meta.type)); + if (meta.bootstrap !== undefined) { + definitionMap.set('bootstrap', new WrappedNodeExpr(meta.bootstrap)); + } + if (meta.declarations !== undefined) { + definitionMap.set('declarations', new WrappedNodeExpr(meta.declarations)); + } + if (meta.imports !== undefined) { + definitionMap.set('imports', new WrappedNodeExpr(meta.imports)); + } + if (meta.exports !== undefined) { + definitionMap.set('exports', new WrappedNodeExpr(meta.exports)); + } + if (meta.schemas !== undefined) { + definitionMap.set('schemas', new WrappedNodeExpr(meta.schemas)); + } + if (meta.id !== undefined) { + definitionMap.set('id', new WrappedNodeExpr(meta.id)); + } + return importExpr(Identifiers.defineNgModule).callFn([definitionMap.toLiteralMap()]); + } + function createNgModuleType({ type: moduleType, declarations, exports, imports, includeImportTypes, publicDeclarationTypes }) { + return new ExpressionType(importExpr(Identifiers.NgModuleDeclaration, [ + new ExpressionType(moduleType.type), + publicDeclarationTypes === null ? tupleTypeOf(declarations) : + tupleOfTypes(publicDeclarationTypes), + includeImportTypes ? tupleTypeOf(imports) : NONE_TYPE, + tupleTypeOf(exports), + ])); } /** * Generates a function call to `ɵɵsetNgModuleScope` with all necessary information so that the @@ -7305,54 +6017,43 @@ * symbols to become tree-shakeable. */ function generateSetNgModuleScopeCall(meta) { - const { - adjacentType: moduleType, - declarations, - imports, - exports, - containsForwardDecls, - } = meta; - const scopeMap = new DefinitionMap(); - if (declarations.length > 0) { - scopeMap.set( - 'declarations', - refsToArray(declarations, containsForwardDecls) - ); - } - if (imports.length > 0) { - scopeMap.set('imports', refsToArray(imports, containsForwardDecls)); - } - if (exports.length > 0) { - scopeMap.set('exports', refsToArray(exports, containsForwardDecls)); - } - if (Object.keys(scopeMap.values).length === 0) { - return null; - } - // setNgModuleScope(...) - const fnCall = new InvokeFunctionExpr( + const { adjacentType: moduleType, declarations, imports, exports, containsForwardDecls } = meta; + const scopeMap = new DefinitionMap(); + if (declarations.length > 0) { + scopeMap.set('declarations', refsToArray(declarations, containsForwardDecls)); + } + if (imports.length > 0) { + scopeMap.set('imports', refsToArray(imports, containsForwardDecls)); + } + if (exports.length > 0) { + scopeMap.set('exports', refsToArray(exports, containsForwardDecls)); + } + if (Object.keys(scopeMap.values).length === 0) { + return null; + } + // setNgModuleScope(...) + const fnCall = new InvokeFunctionExpr( /* fn */ importExpr(Identifiers.setNgModuleScope), - /* args */ [moduleType, scopeMap.toLiteralMap()] - ); - // (ngJitMode guard) && setNgModuleScope(...) - const guardedCall = jitOnlyGuardedExpression(fnCall); - // function() { (ngJitMode guard) && setNgModuleScope(...); } - const iife = new FunctionExpr( + /* args */ [moduleType, scopeMap.toLiteralMap()]); + // (ngJitMode guard) && setNgModuleScope(...) + const guardedCall = jitOnlyGuardedExpression(fnCall); + // function() { (ngJitMode guard) && setNgModuleScope(...); } + const iife = new FunctionExpr( /* params */ [], - /* statements */ [guardedCall.toStmt()] - ); - // (function() { (ngJitMode guard) && setNgModuleScope(...); })() - const iifeCall = new InvokeFunctionExpr(/* fn */ iife, /* args */ []); - return iifeCall.toStmt(); + /* statements */ [guardedCall.toStmt()]); + // (function() { (ngJitMode guard) && setNgModuleScope(...); })() + const iifeCall = new InvokeFunctionExpr( + /* fn */ iife, + /* args */ []); + return iifeCall.toStmt(); } function tupleTypeOf(exp) { - const types = exp.map((ref) => typeofExpr(ref.type)); - return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE; + const types = exp.map(ref => typeofExpr(ref.type)); + return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE; } function tupleOfTypes(types) { - const typeofTypes = types.map((type) => typeofExpr(type)); - return types.length > 0 - ? expressionType(literalArr(typeofTypes)) - : NONE_TYPE; + const typeofTypes = types.map(type => typeofExpr(type)); + return types.length > 0 ? expressionType(literalArr(typeofTypes)) : NONE_TYPE; } /** @@ -7363,48 +6064,26 @@ * found in the LICENSE file at https://angular.io/license */ function compilePipeFromMetadata(metadata) { - const definitionMapValues = []; - // e.g. `name: 'myPipe'` - definitionMapValues.push({ - key: 'name', - value: literal(metadata.pipeName), - quoted: false, - }); - // e.g. `type: MyPipe` - definitionMapValues.push({ - key: 'type', - value: metadata.type.value, - quoted: false, - }); - // e.g. `pure: true` - definitionMapValues.push({ - key: 'pure', - value: literal(metadata.pure), - quoted: false, - }); - if (metadata.isStandalone) { - definitionMapValues.push({ - key: 'standalone', - value: literal(true), - quoted: false, - }); - } - const expression = importExpr(Identifiers.definePipe).callFn( - [literalMap(definitionMapValues)], - undefined, - true - ); - const type = createPipeType(metadata); - return { expression, type, statements: [] }; + const definitionMapValues = []; + // e.g. `name: 'myPipe'` + definitionMapValues.push({ key: 'name', value: literal(metadata.pipeName), quoted: false }); + // e.g. `type: MyPipe` + definitionMapValues.push({ key: 'type', value: metadata.type.value, quoted: false }); + // e.g. `pure: true` + definitionMapValues.push({ key: 'pure', value: literal(metadata.pure), quoted: false }); + if (metadata.isStandalone) { + definitionMapValues.push({ key: 'standalone', value: literal(true), quoted: false }); + } + const expression = importExpr(Identifiers.definePipe).callFn([literalMap(definitionMapValues)], undefined, true); + const type = createPipeType(metadata); + return { expression, type, statements: [] }; } function createPipeType(metadata) { - return new ExpressionType( - importExpr(Identifiers.PipeDeclaration, [ - typeWithParameters(metadata.type.type, metadata.typeArgumentCount), - new ExpressionType(new LiteralExpr(metadata.pipeName)), - new ExpressionType(new LiteralExpr(metadata.isStandalone)), - ]) - ); + return new ExpressionType(importExpr(Identifiers.PipeDeclaration, [ + typeWithParameters(metadata.type.type, metadata.typeArgumentCount), + new ExpressionType(new LiteralExpr(metadata.pipeName)), + new ExpressionType(new LiteralExpr(metadata.isStandalone)), + ])); } /** @@ -7416,14 +6095,10 @@ */ exports.R3TemplateDependencyKind = void 0; (function (R3TemplateDependencyKind) { - R3TemplateDependencyKind[(R3TemplateDependencyKind['Directive'] = 0)] = - 'Directive'; - R3TemplateDependencyKind[(R3TemplateDependencyKind['Pipe'] = 1)] = 'Pipe'; - R3TemplateDependencyKind[(R3TemplateDependencyKind['NgModule'] = 2)] = - 'NgModule'; - })( - exports.R3TemplateDependencyKind || (exports.R3TemplateDependencyKind = {}) - ); + R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive"; + R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe"; + R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule"; + })(exports.R3TemplateDependencyKind || (exports.R3TemplateDependencyKind = {})); /** * @license @@ -7433,55 +6108,50 @@ * found in the LICENSE file at https://angular.io/license */ class ParserError { - constructor(message, input, errLocation, ctxLocation) { - this.input = input; - this.errLocation = errLocation; - this.ctxLocation = ctxLocation; - this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`; - } + constructor(message, input, errLocation, ctxLocation) { + this.input = input; + this.errLocation = errLocation; + this.ctxLocation = ctxLocation; + this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`; + } } class ParseSpan { - constructor(start, end) { - this.start = start; - this.end = end; - } - toAbsolute(absoluteOffset) { - return new AbsoluteSourceSpan( - absoluteOffset + this.start, - absoluteOffset + this.end - ); - } + constructor(start, end) { + this.start = start; + this.end = end; + } + toAbsolute(absoluteOffset) { + return new AbsoluteSourceSpan(absoluteOffset + this.start, absoluteOffset + this.end); + } } class AST { - constructor( - span, + constructor(span, /** * Absolute location of the expression AST in a source code file. */ - sourceSpan - ) { - this.span = span; - this.sourceSpan = sourceSpan; - } - toString() { - return 'AST'; - } + sourceSpan) { + this.span = span; + this.sourceSpan = sourceSpan; + } + toString() { + return 'AST'; + } } class ASTWithName extends AST { - constructor(span, sourceSpan, nameSpan) { - super(span, sourceSpan); - this.nameSpan = nameSpan; - } + constructor(span, sourceSpan, nameSpan) { + super(span, sourceSpan); + this.nameSpan = nameSpan; + } } class EmptyExpr extends AST { - visit(visitor, context = null) { - // do nothing - } + visit(visitor, context = null) { + // do nothing + } } class ImplicitReceiver extends AST { - visit(visitor, context = null) { - return visitor.visitImplicitReceiver(this, context); - } + visit(visitor, context = null) { + return visitor.visitImplicitReceiver(this, context); + } } /** * Receiver when something is accessed through `this` (e.g. `this.foo`). Note that this class @@ -7492,157 +6162,155 @@ * TODO: we should find a way for this class not to extend from `ImplicitReceiver` in the future. */ class ThisReceiver extends ImplicitReceiver { - visit(visitor, context = null) { - var _a; - return (_a = visitor.visitThisReceiver) === null || _a === void 0 - ? void 0 - : _a.call(visitor, this, context); - } + visit(visitor, context = null) { + var _a; + return (_a = visitor.visitThisReceiver) === null || _a === void 0 ? void 0 : _a.call(visitor, this, context); + } } /** * Multiple expressions separated by a semicolon. */ class Chain extends AST { - constructor(span, sourceSpan, expressions) { - super(span, sourceSpan); - this.expressions = expressions; - } - visit(visitor, context = null) { - return visitor.visitChain(this, context); - } + constructor(span, sourceSpan, expressions) { + super(span, sourceSpan); + this.expressions = expressions; + } + visit(visitor, context = null) { + return visitor.visitChain(this, context); + } } class Conditional extends AST { - constructor(span, sourceSpan, condition, trueExp, falseExp) { - super(span, sourceSpan); - this.condition = condition; - this.trueExp = trueExp; - this.falseExp = falseExp; - } - visit(visitor, context = null) { - return visitor.visitConditional(this, context); - } + constructor(span, sourceSpan, condition, trueExp, falseExp) { + super(span, sourceSpan); + this.condition = condition; + this.trueExp = trueExp; + this.falseExp = falseExp; + } + visit(visitor, context = null) { + return visitor.visitConditional(this, context); + } } class PropertyRead extends ASTWithName { - constructor(span, sourceSpan, nameSpan, receiver, name) { - super(span, sourceSpan, nameSpan); - this.receiver = receiver; - this.name = name; - } - visit(visitor, context = null) { - return visitor.visitPropertyRead(this, context); - } + constructor(span, sourceSpan, nameSpan, receiver, name) { + super(span, sourceSpan, nameSpan); + this.receiver = receiver; + this.name = name; + } + visit(visitor, context = null) { + return visitor.visitPropertyRead(this, context); + } } class PropertyWrite extends ASTWithName { - constructor(span, sourceSpan, nameSpan, receiver, name, value) { - super(span, sourceSpan, nameSpan); - this.receiver = receiver; - this.name = name; - this.value = value; - } - visit(visitor, context = null) { - return visitor.visitPropertyWrite(this, context); - } + constructor(span, sourceSpan, nameSpan, receiver, name, value) { + super(span, sourceSpan, nameSpan); + this.receiver = receiver; + this.name = name; + this.value = value; + } + visit(visitor, context = null) { + return visitor.visitPropertyWrite(this, context); + } } class SafePropertyRead extends ASTWithName { - constructor(span, sourceSpan, nameSpan, receiver, name) { - super(span, sourceSpan, nameSpan); - this.receiver = receiver; - this.name = name; - } - visit(visitor, context = null) { - return visitor.visitSafePropertyRead(this, context); - } + constructor(span, sourceSpan, nameSpan, receiver, name) { + super(span, sourceSpan, nameSpan); + this.receiver = receiver; + this.name = name; + } + visit(visitor, context = null) { + return visitor.visitSafePropertyRead(this, context); + } } class KeyedRead extends AST { - constructor(span, sourceSpan, receiver, key) { - super(span, sourceSpan); - this.receiver = receiver; - this.key = key; - } - visit(visitor, context = null) { - return visitor.visitKeyedRead(this, context); - } + constructor(span, sourceSpan, receiver, key) { + super(span, sourceSpan); + this.receiver = receiver; + this.key = key; + } + visit(visitor, context = null) { + return visitor.visitKeyedRead(this, context); + } } class SafeKeyedRead extends AST { - constructor(span, sourceSpan, receiver, key) { - super(span, sourceSpan); - this.receiver = receiver; - this.key = key; - } - visit(visitor, context = null) { - return visitor.visitSafeKeyedRead(this, context); - } + constructor(span, sourceSpan, receiver, key) { + super(span, sourceSpan); + this.receiver = receiver; + this.key = key; + } + visit(visitor, context = null) { + return visitor.visitSafeKeyedRead(this, context); + } } class KeyedWrite extends AST { - constructor(span, sourceSpan, receiver, key, value) { - super(span, sourceSpan); - this.receiver = receiver; - this.key = key; - this.value = value; - } - visit(visitor, context = null) { - return visitor.visitKeyedWrite(this, context); - } + constructor(span, sourceSpan, receiver, key, value) { + super(span, sourceSpan); + this.receiver = receiver; + this.key = key; + this.value = value; + } + visit(visitor, context = null) { + return visitor.visitKeyedWrite(this, context); + } } class BindingPipe extends ASTWithName { - constructor(span, sourceSpan, exp, name, args, nameSpan) { - super(span, sourceSpan, nameSpan); - this.exp = exp; - this.name = name; - this.args = args; - } - visit(visitor, context = null) { - return visitor.visitPipe(this, context); - } + constructor(span, sourceSpan, exp, name, args, nameSpan) { + super(span, sourceSpan, nameSpan); + this.exp = exp; + this.name = name; + this.args = args; + } + visit(visitor, context = null) { + return visitor.visitPipe(this, context); + } } class LiteralPrimitive extends AST { - constructor(span, sourceSpan, value) { - super(span, sourceSpan); - this.value = value; - } - visit(visitor, context = null) { - return visitor.visitLiteralPrimitive(this, context); - } + constructor(span, sourceSpan, value) { + super(span, sourceSpan); + this.value = value; + } + visit(visitor, context = null) { + return visitor.visitLiteralPrimitive(this, context); + } } class LiteralArray extends AST { - constructor(span, sourceSpan, expressions) { - super(span, sourceSpan); - this.expressions = expressions; - } - visit(visitor, context = null) { - return visitor.visitLiteralArray(this, context); - } + constructor(span, sourceSpan, expressions) { + super(span, sourceSpan); + this.expressions = expressions; + } + visit(visitor, context = null) { + return visitor.visitLiteralArray(this, context); + } } class LiteralMap extends AST { - constructor(span, sourceSpan, keys, values) { - super(span, sourceSpan); - this.keys = keys; - this.values = values; - } - visit(visitor, context = null) { - return visitor.visitLiteralMap(this, context); - } + constructor(span, sourceSpan, keys, values) { + super(span, sourceSpan); + this.keys = keys; + this.values = values; + } + visit(visitor, context = null) { + return visitor.visitLiteralMap(this, context); + } } class Interpolation extends AST { - constructor(span, sourceSpan, strings, expressions) { - super(span, sourceSpan); - this.strings = strings; - this.expressions = expressions; - } - visit(visitor, context = null) { - return visitor.visitInterpolation(this, context); - } + constructor(span, sourceSpan, strings, expressions) { + super(span, sourceSpan); + this.strings = strings; + this.expressions = expressions; + } + visit(visitor, context = null) { + return visitor.visitInterpolation(this, context); + } } class Binary extends AST { - constructor(span, sourceSpan, operation, left, right) { - super(span, sourceSpan); - this.operation = operation; - this.left = left; - this.right = right; - } - visit(visitor, context = null) { - return visitor.visitBinary(this, context); - } + constructor(span, sourceSpan, operation, left, right) { + super(span, sourceSpan); + this.operation = operation; + this.left = left; + this.right = right; + } + visit(visitor, context = null) { + return visitor.visitBinary(this, context); + } } /** * For backwards compatibility reasons, `Unary` inherits from `Binary` and mimics the binary AST @@ -7650,720 +6318,519 @@ * after consumers have been given a chance to fully support Unary. */ class Unary extends Binary { - /** - * During the deprecation period this constructor is private, to avoid consumers from creating - * a `Unary` with the fallback properties for `Binary`. - */ - constructor( - span, - sourceSpan, - operator, - expr, - binaryOp, - binaryLeft, - binaryRight - ) { - super(span, sourceSpan, binaryOp, binaryLeft, binaryRight); - this.operator = operator; - this.expr = expr; - // Redeclare the properties that are inherited from `Binary` as `never`, as consumers should not - // depend on these fields when operating on `Unary`. - this.left = null; - this.right = null; - this.operation = null; - } - /** - * Creates a unary minus expression "-x", represented as `Binary` using "0 - x". - */ - static createMinus(span, sourceSpan, expr) { - return new Unary( - span, - sourceSpan, - '-', - expr, - '-', - new LiteralPrimitive(span, sourceSpan, 0), - expr - ); - } - /** - * Creates a unary plus expression "+x", represented as `Binary` using "x - 0". - */ - static createPlus(span, sourceSpan, expr) { - return new Unary( - span, - sourceSpan, - '+', - expr, - '-', - expr, - new LiteralPrimitive(span, sourceSpan, 0) - ); - } - visit(visitor, context = null) { - if (visitor.visitUnary !== undefined) { - return visitor.visitUnary(this, context); - } - return visitor.visitBinary(this, context); - } + /** + * During the deprecation period this constructor is private, to avoid consumers from creating + * a `Unary` with the fallback properties for `Binary`. + */ + constructor(span, sourceSpan, operator, expr, binaryOp, binaryLeft, binaryRight) { + super(span, sourceSpan, binaryOp, binaryLeft, binaryRight); + this.operator = operator; + this.expr = expr; + // Redeclare the properties that are inherited from `Binary` as `never`, as consumers should not + // depend on these fields when operating on `Unary`. + this.left = null; + this.right = null; + this.operation = null; + } + /** + * Creates a unary minus expression "-x", represented as `Binary` using "0 - x". + */ + static createMinus(span, sourceSpan, expr) { + return new Unary(span, sourceSpan, '-', expr, '-', new LiteralPrimitive(span, sourceSpan, 0), expr); + } + /** + * Creates a unary plus expression "+x", represented as `Binary` using "x - 0". + */ + static createPlus(span, sourceSpan, expr) { + return new Unary(span, sourceSpan, '+', expr, '-', expr, new LiteralPrimitive(span, sourceSpan, 0)); + } + visit(visitor, context = null) { + if (visitor.visitUnary !== undefined) { + return visitor.visitUnary(this, context); + } + return visitor.visitBinary(this, context); + } } class PrefixNot extends AST { - constructor(span, sourceSpan, expression) { - super(span, sourceSpan); - this.expression = expression; - } - visit(visitor, context = null) { - return visitor.visitPrefixNot(this, context); - } + constructor(span, sourceSpan, expression) { + super(span, sourceSpan); + this.expression = expression; + } + visit(visitor, context = null) { + return visitor.visitPrefixNot(this, context); + } } class NonNullAssert extends AST { - constructor(span, sourceSpan, expression) { - super(span, sourceSpan); - this.expression = expression; - } - visit(visitor, context = null) { - return visitor.visitNonNullAssert(this, context); - } + constructor(span, sourceSpan, expression) { + super(span, sourceSpan); + this.expression = expression; + } + visit(visitor, context = null) { + return visitor.visitNonNullAssert(this, context); + } } class Call extends AST { - constructor(span, sourceSpan, receiver, args, argumentSpan) { - super(span, sourceSpan); - this.receiver = receiver; - this.args = args; - this.argumentSpan = argumentSpan; - } - visit(visitor, context = null) { - return visitor.visitCall(this, context); - } + constructor(span, sourceSpan, receiver, args, argumentSpan) { + super(span, sourceSpan); + this.receiver = receiver; + this.args = args; + this.argumentSpan = argumentSpan; + } + visit(visitor, context = null) { + return visitor.visitCall(this, context); + } } class SafeCall extends AST { - constructor(span, sourceSpan, receiver, args, argumentSpan) { - super(span, sourceSpan); - this.receiver = receiver; - this.args = args; - this.argumentSpan = argumentSpan; - } - visit(visitor, context = null) { - return visitor.visitSafeCall(this, context); - } + constructor(span, sourceSpan, receiver, args, argumentSpan) { + super(span, sourceSpan); + this.receiver = receiver; + this.args = args; + this.argumentSpan = argumentSpan; + } + visit(visitor, context = null) { + return visitor.visitSafeCall(this, context); + } } /** * Records the absolute position of a text span in a source file, where `start` and `end` are the * starting and ending byte offsets, respectively, of the text span in a source file. */ class AbsoluteSourceSpan { - constructor(start, end) { - this.start = start; - this.end = end; - } + constructor(start, end) { + this.start = start; + this.end = end; + } } class ASTWithSource extends AST { - constructor(ast, source, location, absoluteOffset, errors) { - super( - new ParseSpan(0, source === null ? 0 : source.length), - new AbsoluteSourceSpan( - absoluteOffset, - source === null ? absoluteOffset : absoluteOffset + source.length - ) - ); - this.ast = ast; - this.source = source; - this.location = location; - this.errors = errors; - } - visit(visitor, context = null) { - if (visitor.visitASTWithSource) { - return visitor.visitASTWithSource(this, context); - } - return this.ast.visit(visitor, context); - } - toString() { - return `${this.source} in ${this.location}`; - } + constructor(ast, source, location, absoluteOffset, errors) { + super(new ParseSpan(0, source === null ? 0 : source.length), new AbsoluteSourceSpan(absoluteOffset, source === null ? absoluteOffset : absoluteOffset + source.length)); + this.ast = ast; + this.source = source; + this.location = location; + this.errors = errors; + } + visit(visitor, context = null) { + if (visitor.visitASTWithSource) { + return visitor.visitASTWithSource(this, context); + } + return this.ast.visit(visitor, context); + } + toString() { + return `${this.source} in ${this.location}`; + } } class VariableBinding { - /** - * @param sourceSpan entire span of the binding. - * @param key name of the LHS along with its span. - * @param value optional value for the RHS along with its span. - */ - constructor(sourceSpan, key, value) { - this.sourceSpan = sourceSpan; - this.key = key; - this.value = value; - } + /** + * @param sourceSpan entire span of the binding. + * @param key name of the LHS along with its span. + * @param value optional value for the RHS along with its span. + */ + constructor(sourceSpan, key, value) { + this.sourceSpan = sourceSpan; + this.key = key; + this.value = value; + } } class ExpressionBinding { - /** - * @param sourceSpan entire span of the binding. - * @param key binding name, like ngForOf, ngForTrackBy, ngIf, along with its - * span. Note that the length of the span may not be the same as - * `key.source.length`. For example, - * 1. key.source = ngFor, key.span is for "ngFor" - * 2. key.source = ngForOf, key.span is for "of" - * 3. key.source = ngForTrackBy, key.span is for "trackBy" - * @param value optional expression for the RHS. - */ - constructor(sourceSpan, key, value) { - this.sourceSpan = sourceSpan; - this.key = key; - this.value = value; - } + /** + * @param sourceSpan entire span of the binding. + * @param key binding name, like ngForOf, ngForTrackBy, ngIf, along with its + * span. Note that the length of the span may not be the same as + * `key.source.length`. For example, + * 1. key.source = ngFor, key.span is for "ngFor" + * 2. key.source = ngForOf, key.span is for "of" + * 3. key.source = ngForTrackBy, key.span is for "trackBy" + * @param value optional expression for the RHS. + */ + constructor(sourceSpan, key, value) { + this.sourceSpan = sourceSpan; + this.key = key; + this.value = value; + } } class RecursiveAstVisitor { - visit(ast, context) { - // The default implementation just visits every node. - // Classes that extend RecursiveAstVisitor should override this function - // to selectively visit the specified node. - ast.visit(this, context); - } - visitUnary(ast, context) { - this.visit(ast.expr, context); - } - visitBinary(ast, context) { - this.visit(ast.left, context); - this.visit(ast.right, context); - } - visitChain(ast, context) { - this.visitAll(ast.expressions, context); - } - visitConditional(ast, context) { - this.visit(ast.condition, context); - this.visit(ast.trueExp, context); - this.visit(ast.falseExp, context); - } - visitPipe(ast, context) { - this.visit(ast.exp, context); - this.visitAll(ast.args, context); - } - visitImplicitReceiver(ast, context) {} - visitThisReceiver(ast, context) {} - visitInterpolation(ast, context) { - this.visitAll(ast.expressions, context); - } - visitKeyedRead(ast, context) { - this.visit(ast.receiver, context); - this.visit(ast.key, context); - } - visitKeyedWrite(ast, context) { - this.visit(ast.receiver, context); - this.visit(ast.key, context); - this.visit(ast.value, context); - } - visitLiteralArray(ast, context) { - this.visitAll(ast.expressions, context); - } - visitLiteralMap(ast, context) { - this.visitAll(ast.values, context); - } - visitLiteralPrimitive(ast, context) {} - visitPrefixNot(ast, context) { - this.visit(ast.expression, context); - } - visitNonNullAssert(ast, context) { - this.visit(ast.expression, context); - } - visitPropertyRead(ast, context) { - this.visit(ast.receiver, context); - } - visitPropertyWrite(ast, context) { - this.visit(ast.receiver, context); - this.visit(ast.value, context); - } - visitSafePropertyRead(ast, context) { - this.visit(ast.receiver, context); - } - visitSafeKeyedRead(ast, context) { - this.visit(ast.receiver, context); - this.visit(ast.key, context); - } - visitCall(ast, context) { - this.visit(ast.receiver, context); - this.visitAll(ast.args, context); - } - visitSafeCall(ast, context) { - this.visit(ast.receiver, context); - this.visitAll(ast.args, context); - } - // This is not part of the AstVisitor interface, just a helper method - visitAll(asts, context) { - for (const ast of asts) { - this.visit(ast, context); - } - } + visit(ast, context) { + // The default implementation just visits every node. + // Classes that extend RecursiveAstVisitor should override this function + // to selectively visit the specified node. + ast.visit(this, context); + } + visitUnary(ast, context) { + this.visit(ast.expr, context); + } + visitBinary(ast, context) { + this.visit(ast.left, context); + this.visit(ast.right, context); + } + visitChain(ast, context) { + this.visitAll(ast.expressions, context); + } + visitConditional(ast, context) { + this.visit(ast.condition, context); + this.visit(ast.trueExp, context); + this.visit(ast.falseExp, context); + } + visitPipe(ast, context) { + this.visit(ast.exp, context); + this.visitAll(ast.args, context); + } + visitImplicitReceiver(ast, context) { } + visitThisReceiver(ast, context) { } + visitInterpolation(ast, context) { + this.visitAll(ast.expressions, context); + } + visitKeyedRead(ast, context) { + this.visit(ast.receiver, context); + this.visit(ast.key, context); + } + visitKeyedWrite(ast, context) { + this.visit(ast.receiver, context); + this.visit(ast.key, context); + this.visit(ast.value, context); + } + visitLiteralArray(ast, context) { + this.visitAll(ast.expressions, context); + } + visitLiteralMap(ast, context) { + this.visitAll(ast.values, context); + } + visitLiteralPrimitive(ast, context) { } + visitPrefixNot(ast, context) { + this.visit(ast.expression, context); + } + visitNonNullAssert(ast, context) { + this.visit(ast.expression, context); + } + visitPropertyRead(ast, context) { + this.visit(ast.receiver, context); + } + visitPropertyWrite(ast, context) { + this.visit(ast.receiver, context); + this.visit(ast.value, context); + } + visitSafePropertyRead(ast, context) { + this.visit(ast.receiver, context); + } + visitSafeKeyedRead(ast, context) { + this.visit(ast.receiver, context); + this.visit(ast.key, context); + } + visitCall(ast, context) { + this.visit(ast.receiver, context); + this.visitAll(ast.args, context); + } + visitSafeCall(ast, context) { + this.visit(ast.receiver, context); + this.visitAll(ast.args, context); + } + // This is not part of the AstVisitor interface, just a helper method + visitAll(asts, context) { + for (const ast of asts) { + this.visit(ast, context); + } + } } class AstTransformer { - visitImplicitReceiver(ast, context) { - return ast; - } - visitThisReceiver(ast, context) { - return ast; - } - visitInterpolation(ast, context) { - return new Interpolation( - ast.span, - ast.sourceSpan, - ast.strings, - this.visitAll(ast.expressions) - ); - } - visitLiteralPrimitive(ast, context) { - return new LiteralPrimitive(ast.span, ast.sourceSpan, ast.value); - } - visitPropertyRead(ast, context) { - return new PropertyRead( - ast.span, - ast.sourceSpan, - ast.nameSpan, - ast.receiver.visit(this), - ast.name - ); - } - visitPropertyWrite(ast, context) { - return new PropertyWrite( - ast.span, - ast.sourceSpan, - ast.nameSpan, - ast.receiver.visit(this), - ast.name, - ast.value.visit(this) - ); - } - visitSafePropertyRead(ast, context) { - return new SafePropertyRead( - ast.span, - ast.sourceSpan, - ast.nameSpan, - ast.receiver.visit(this), - ast.name - ); - } - visitLiteralArray(ast, context) { - return new LiteralArray( - ast.span, - ast.sourceSpan, - this.visitAll(ast.expressions) - ); - } - visitLiteralMap(ast, context) { - return new LiteralMap( - ast.span, - ast.sourceSpan, - ast.keys, - this.visitAll(ast.values) - ); - } - visitUnary(ast, context) { - switch (ast.operator) { - case '+': - return Unary.createPlus( - ast.span, - ast.sourceSpan, - ast.expr.visit(this) - ); - case '-': - return Unary.createMinus( - ast.span, - ast.sourceSpan, - ast.expr.visit(this) - ); - default: - throw new Error(`Unknown unary operator ${ast.operator}`); - } - } - visitBinary(ast, context) { - return new Binary( - ast.span, - ast.sourceSpan, - ast.operation, - ast.left.visit(this), - ast.right.visit(this) - ); - } - visitPrefixNot(ast, context) { - return new PrefixNot( - ast.span, - ast.sourceSpan, - ast.expression.visit(this) - ); - } - visitNonNullAssert(ast, context) { - return new NonNullAssert( - ast.span, - ast.sourceSpan, - ast.expression.visit(this) - ); - } - visitConditional(ast, context) { - return new Conditional( - ast.span, - ast.sourceSpan, - ast.condition.visit(this), - ast.trueExp.visit(this), - ast.falseExp.visit(this) - ); - } - visitPipe(ast, context) { - return new BindingPipe( - ast.span, - ast.sourceSpan, - ast.exp.visit(this), - ast.name, - this.visitAll(ast.args), - ast.nameSpan - ); - } - visitKeyedRead(ast, context) { - return new KeyedRead( - ast.span, - ast.sourceSpan, - ast.receiver.visit(this), - ast.key.visit(this) - ); - } - visitKeyedWrite(ast, context) { - return new KeyedWrite( - ast.span, - ast.sourceSpan, - ast.receiver.visit(this), - ast.key.visit(this), - ast.value.visit(this) - ); - } - visitCall(ast, context) { - return new Call( - ast.span, - ast.sourceSpan, - ast.receiver.visit(this), - this.visitAll(ast.args), - ast.argumentSpan - ); - } - visitSafeCall(ast, context) { - return new SafeCall( - ast.span, - ast.sourceSpan, - ast.receiver.visit(this), - this.visitAll(ast.args), - ast.argumentSpan - ); - } - visitAll(asts) { - const res = []; - for (let i = 0; i < asts.length; ++i) { - res[i] = asts[i].visit(this); + visitImplicitReceiver(ast, context) { + return ast; } - return res; - } - visitChain(ast, context) { - return new Chain( - ast.span, - ast.sourceSpan, - this.visitAll(ast.expressions) - ); - } - visitSafeKeyedRead(ast, context) { - return new SafeKeyedRead( - ast.span, - ast.sourceSpan, - ast.receiver.visit(this), - ast.key.visit(this) - ); - } - } - // A transformer that only creates new nodes if the transformer makes a change or - // a change is made a child node. - class AstMemoryEfficientTransformer { - visitImplicitReceiver(ast, context) { - return ast; - } - visitThisReceiver(ast, context) { - return ast; - } - visitInterpolation(ast, context) { - const expressions = this.visitAll(ast.expressions); - if (expressions !== ast.expressions) - return new Interpolation( - ast.span, - ast.sourceSpan, - ast.strings, - expressions - ); - return ast; - } - visitLiteralPrimitive(ast, context) { - return ast; - } - visitPropertyRead(ast, context) { - const receiver = ast.receiver.visit(this); - if (receiver !== ast.receiver) { - return new PropertyRead( - ast.span, - ast.sourceSpan, - ast.nameSpan, - receiver, - ast.name - ); - } - return ast; - } - visitPropertyWrite(ast, context) { - const receiver = ast.receiver.visit(this); - const value = ast.value.visit(this); - if (receiver !== ast.receiver || value !== ast.value) { - return new PropertyWrite( - ast.span, - ast.sourceSpan, - ast.nameSpan, - receiver, - ast.name, - value - ); - } - return ast; - } - visitSafePropertyRead(ast, context) { - const receiver = ast.receiver.visit(this); - if (receiver !== ast.receiver) { - return new SafePropertyRead( - ast.span, - ast.sourceSpan, - ast.nameSpan, - receiver, - ast.name - ); - } - return ast; - } - visitLiteralArray(ast, context) { - const expressions = this.visitAll(ast.expressions); - if (expressions !== ast.expressions) { - return new LiteralArray(ast.span, ast.sourceSpan, expressions); - } - return ast; - } - visitLiteralMap(ast, context) { - const values = this.visitAll(ast.values); - if (values !== ast.values) { - return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, values); - } - return ast; - } - visitUnary(ast, context) { - const expr = ast.expr.visit(this); - if (expr !== ast.expr) { - switch (ast.operator) { - case '+': - return Unary.createPlus(ast.span, ast.sourceSpan, expr); - case '-': - return Unary.createMinus(ast.span, ast.sourceSpan, expr); - default: - throw new Error(`Unknown unary operator ${ast.operator}`); - } + visitThisReceiver(ast, context) { + return ast; } - return ast; - } - visitBinary(ast, context) { - const left = ast.left.visit(this); - const right = ast.right.visit(this); - if (left !== ast.left || right !== ast.right) { - return new Binary(ast.span, ast.sourceSpan, ast.operation, left, right); - } - return ast; - } - visitPrefixNot(ast, context) { - const expression = ast.expression.visit(this); - if (expression !== ast.expression) { - return new PrefixNot(ast.span, ast.sourceSpan, expression); - } - return ast; - } - visitNonNullAssert(ast, context) { - const expression = ast.expression.visit(this); - if (expression !== ast.expression) { - return new NonNullAssert(ast.span, ast.sourceSpan, expression); - } - return ast; - } - visitConditional(ast, context) { - const condition = ast.condition.visit(this); - const trueExp = ast.trueExp.visit(this); - const falseExp = ast.falseExp.visit(this); - if ( - condition !== ast.condition || - trueExp !== ast.trueExp || - falseExp !== ast.falseExp - ) { - return new Conditional( - ast.span, - ast.sourceSpan, - condition, - trueExp, - falseExp - ); - } - return ast; - } - visitPipe(ast, context) { - const exp = ast.exp.visit(this); - const args = this.visitAll(ast.args); - if (exp !== ast.exp || args !== ast.args) { - return new BindingPipe( - ast.span, - ast.sourceSpan, - exp, - ast.name, - args, - ast.nameSpan - ); - } - return ast; - } - visitKeyedRead(ast, context) { - const obj = ast.receiver.visit(this); - const key = ast.key.visit(this); - if (obj !== ast.receiver || key !== ast.key) { - return new KeyedRead(ast.span, ast.sourceSpan, obj, key); - } - return ast; - } - visitKeyedWrite(ast, context) { - const obj = ast.receiver.visit(this); - const key = ast.key.visit(this); - const value = ast.value.visit(this); - if (obj !== ast.receiver || key !== ast.key || value !== ast.value) { - return new KeyedWrite(ast.span, ast.sourceSpan, obj, key, value); - } - return ast; - } - visitAll(asts) { - const res = []; - let modified = false; - for (let i = 0; i < asts.length; ++i) { - const original = asts[i]; - const value = original.visit(this); - res[i] = value; - modified = modified || value !== original; - } - return modified ? res : asts; - } - visitChain(ast, context) { - const expressions = this.visitAll(ast.expressions); - if (expressions !== ast.expressions) { - return new Chain(ast.span, ast.sourceSpan, expressions); - } - return ast; - } - visitCall(ast, context) { - const receiver = ast.receiver.visit(this); - const args = this.visitAll(ast.args); - if (receiver !== ast.receiver || args !== ast.args) { - return new Call( - ast.span, - ast.sourceSpan, - receiver, - args, - ast.argumentSpan - ); - } - return ast; - } - visitSafeCall(ast, context) { - const receiver = ast.receiver.visit(this); - const args = this.visitAll(ast.args); - if (receiver !== ast.receiver || args !== ast.args) { - return new SafeCall( - ast.span, - ast.sourceSpan, - receiver, - args, - ast.argumentSpan - ); - } - return ast; - } - visitSafeKeyedRead(ast, context) { - const obj = ast.receiver.visit(this); - const key = ast.key.visit(this); - if (obj !== ast.receiver || key !== ast.key) { - return new SafeKeyedRead(ast.span, ast.sourceSpan, obj, key); - } - return ast; - } - } - // Bindings - class ParsedProperty { - constructor(name, expression, type, sourceSpan, keySpan, valueSpan) { - this.name = name; - this.expression = expression; - this.type = type; - this.sourceSpan = sourceSpan; - this.keySpan = keySpan; - this.valueSpan = valueSpan; - this.isLiteral = this.type === exports.ParsedPropertyType.LITERAL_ATTR; - this.isAnimation = this.type === exports.ParsedPropertyType.ANIMATION; - } - } - exports.ParsedPropertyType = void 0; - (function (ParsedPropertyType) { - ParsedPropertyType[(ParsedPropertyType['DEFAULT'] = 0)] = 'DEFAULT'; - ParsedPropertyType[(ParsedPropertyType['LITERAL_ATTR'] = 1)] = - 'LITERAL_ATTR'; - ParsedPropertyType[(ParsedPropertyType['ANIMATION'] = 2)] = 'ANIMATION'; - })(exports.ParsedPropertyType || (exports.ParsedPropertyType = {})); - class ParsedEvent { - // Regular events have a target - // Animation events have a phase - constructor( - name, - targetOrPhase, - type, - handler, - sourceSpan, - handlerSpan, - keySpan - ) { - this.name = name; - this.targetOrPhase = targetOrPhase; - this.type = type; - this.handler = handler; - this.sourceSpan = sourceSpan; - this.handlerSpan = handlerSpan; - this.keySpan = keySpan; - } - } - /** - * ParsedVariable represents a variable declaration in a microsyntax expression. - */ + visitInterpolation(ast, context) { + return new Interpolation(ast.span, ast.sourceSpan, ast.strings, this.visitAll(ast.expressions)); + } + visitLiteralPrimitive(ast, context) { + return new LiteralPrimitive(ast.span, ast.sourceSpan, ast.value); + } + visitPropertyRead(ast, context) { + return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name); + } + visitPropertyWrite(ast, context) { + return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, ast.value.visit(this)); + } + visitSafePropertyRead(ast, context) { + return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name); + } + visitLiteralArray(ast, context) { + return new LiteralArray(ast.span, ast.sourceSpan, this.visitAll(ast.expressions)); + } + visitLiteralMap(ast, context) { + return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, this.visitAll(ast.values)); + } + visitUnary(ast, context) { + switch (ast.operator) { + case '+': + return Unary.createPlus(ast.span, ast.sourceSpan, ast.expr.visit(this)); + case '-': + return Unary.createMinus(ast.span, ast.sourceSpan, ast.expr.visit(this)); + default: + throw new Error(`Unknown unary operator ${ast.operator}`); + } + } + visitBinary(ast, context) { + return new Binary(ast.span, ast.sourceSpan, ast.operation, ast.left.visit(this), ast.right.visit(this)); + } + visitPrefixNot(ast, context) { + return new PrefixNot(ast.span, ast.sourceSpan, ast.expression.visit(this)); + } + visitNonNullAssert(ast, context) { + return new NonNullAssert(ast.span, ast.sourceSpan, ast.expression.visit(this)); + } + visitConditional(ast, context) { + return new Conditional(ast.span, ast.sourceSpan, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this)); + } + visitPipe(ast, context) { + return new BindingPipe(ast.span, ast.sourceSpan, ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.nameSpan); + } + visitKeyedRead(ast, context) { + return new KeyedRead(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this)); + } + visitKeyedWrite(ast, context) { + return new KeyedWrite(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this), ast.value.visit(this)); + } + visitCall(ast, context) { + return new Call(ast.span, ast.sourceSpan, ast.receiver.visit(this), this.visitAll(ast.args), ast.argumentSpan); + } + visitSafeCall(ast, context) { + return new SafeCall(ast.span, ast.sourceSpan, ast.receiver.visit(this), this.visitAll(ast.args), ast.argumentSpan); + } + visitAll(asts) { + const res = []; + for (let i = 0; i < asts.length; ++i) { + res[i] = asts[i].visit(this); + } + return res; + } + visitChain(ast, context) { + return new Chain(ast.span, ast.sourceSpan, this.visitAll(ast.expressions)); + } + visitSafeKeyedRead(ast, context) { + return new SafeKeyedRead(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this)); + } + } + // A transformer that only creates new nodes if the transformer makes a change or + // a change is made a child node. + class AstMemoryEfficientTransformer { + visitImplicitReceiver(ast, context) { + return ast; + } + visitThisReceiver(ast, context) { + return ast; + } + visitInterpolation(ast, context) { + const expressions = this.visitAll(ast.expressions); + if (expressions !== ast.expressions) + return new Interpolation(ast.span, ast.sourceSpan, ast.strings, expressions); + return ast; + } + visitLiteralPrimitive(ast, context) { + return ast; + } + visitPropertyRead(ast, context) { + const receiver = ast.receiver.visit(this); + if (receiver !== ast.receiver) { + return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name); + } + return ast; + } + visitPropertyWrite(ast, context) { + const receiver = ast.receiver.visit(this); + const value = ast.value.visit(this); + if (receiver !== ast.receiver || value !== ast.value) { + return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, value); + } + return ast; + } + visitSafePropertyRead(ast, context) { + const receiver = ast.receiver.visit(this); + if (receiver !== ast.receiver) { + return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name); + } + return ast; + } + visitLiteralArray(ast, context) { + const expressions = this.visitAll(ast.expressions); + if (expressions !== ast.expressions) { + return new LiteralArray(ast.span, ast.sourceSpan, expressions); + } + return ast; + } + visitLiteralMap(ast, context) { + const values = this.visitAll(ast.values); + if (values !== ast.values) { + return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, values); + } + return ast; + } + visitUnary(ast, context) { + const expr = ast.expr.visit(this); + if (expr !== ast.expr) { + switch (ast.operator) { + case '+': + return Unary.createPlus(ast.span, ast.sourceSpan, expr); + case '-': + return Unary.createMinus(ast.span, ast.sourceSpan, expr); + default: + throw new Error(`Unknown unary operator ${ast.operator}`); + } + } + return ast; + } + visitBinary(ast, context) { + const left = ast.left.visit(this); + const right = ast.right.visit(this); + if (left !== ast.left || right !== ast.right) { + return new Binary(ast.span, ast.sourceSpan, ast.operation, left, right); + } + return ast; + } + visitPrefixNot(ast, context) { + const expression = ast.expression.visit(this); + if (expression !== ast.expression) { + return new PrefixNot(ast.span, ast.sourceSpan, expression); + } + return ast; + } + visitNonNullAssert(ast, context) { + const expression = ast.expression.visit(this); + if (expression !== ast.expression) { + return new NonNullAssert(ast.span, ast.sourceSpan, expression); + } + return ast; + } + visitConditional(ast, context) { + const condition = ast.condition.visit(this); + const trueExp = ast.trueExp.visit(this); + const falseExp = ast.falseExp.visit(this); + if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== ast.falseExp) { + return new Conditional(ast.span, ast.sourceSpan, condition, trueExp, falseExp); + } + return ast; + } + visitPipe(ast, context) { + const exp = ast.exp.visit(this); + const args = this.visitAll(ast.args); + if (exp !== ast.exp || args !== ast.args) { + return new BindingPipe(ast.span, ast.sourceSpan, exp, ast.name, args, ast.nameSpan); + } + return ast; + } + visitKeyedRead(ast, context) { + const obj = ast.receiver.visit(this); + const key = ast.key.visit(this); + if (obj !== ast.receiver || key !== ast.key) { + return new KeyedRead(ast.span, ast.sourceSpan, obj, key); + } + return ast; + } + visitKeyedWrite(ast, context) { + const obj = ast.receiver.visit(this); + const key = ast.key.visit(this); + const value = ast.value.visit(this); + if (obj !== ast.receiver || key !== ast.key || value !== ast.value) { + return new KeyedWrite(ast.span, ast.sourceSpan, obj, key, value); + } + return ast; + } + visitAll(asts) { + const res = []; + let modified = false; + for (let i = 0; i < asts.length; ++i) { + const original = asts[i]; + const value = original.visit(this); + res[i] = value; + modified = modified || value !== original; + } + return modified ? res : asts; + } + visitChain(ast, context) { + const expressions = this.visitAll(ast.expressions); + if (expressions !== ast.expressions) { + return new Chain(ast.span, ast.sourceSpan, expressions); + } + return ast; + } + visitCall(ast, context) { + const receiver = ast.receiver.visit(this); + const args = this.visitAll(ast.args); + if (receiver !== ast.receiver || args !== ast.args) { + return new Call(ast.span, ast.sourceSpan, receiver, args, ast.argumentSpan); + } + return ast; + } + visitSafeCall(ast, context) { + const receiver = ast.receiver.visit(this); + const args = this.visitAll(ast.args); + if (receiver !== ast.receiver || args !== ast.args) { + return new SafeCall(ast.span, ast.sourceSpan, receiver, args, ast.argumentSpan); + } + return ast; + } + visitSafeKeyedRead(ast, context) { + const obj = ast.receiver.visit(this); + const key = ast.key.visit(this); + if (obj !== ast.receiver || key !== ast.key) { + return new SafeKeyedRead(ast.span, ast.sourceSpan, obj, key); + } + return ast; + } + } + // Bindings + class ParsedProperty { + constructor(name, expression, type, sourceSpan, keySpan, valueSpan) { + this.name = name; + this.expression = expression; + this.type = type; + this.sourceSpan = sourceSpan; + this.keySpan = keySpan; + this.valueSpan = valueSpan; + this.isLiteral = this.type === exports.ParsedPropertyType.LITERAL_ATTR; + this.isAnimation = this.type === exports.ParsedPropertyType.ANIMATION; + } + } + exports.ParsedPropertyType = void 0; + (function (ParsedPropertyType) { + ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT"; + ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR"; + ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION"; + })(exports.ParsedPropertyType || (exports.ParsedPropertyType = {})); + class ParsedEvent { + // Regular events have a target + // Animation events have a phase + constructor(name, targetOrPhase, type, handler, sourceSpan, handlerSpan, keySpan) { + this.name = name; + this.targetOrPhase = targetOrPhase; + this.type = type; + this.handler = handler; + this.sourceSpan = sourceSpan; + this.handlerSpan = handlerSpan; + this.keySpan = keySpan; + } + } + /** + * ParsedVariable represents a variable declaration in a microsyntax expression. + */ class ParsedVariable { - constructor(name, value, sourceSpan, keySpan, valueSpan) { - this.name = name; - this.value = value; - this.sourceSpan = sourceSpan; - this.keySpan = keySpan; - this.valueSpan = valueSpan; - } + constructor(name, value, sourceSpan, keySpan, valueSpan) { + this.name = name; + this.value = value; + this.sourceSpan = sourceSpan; + this.keySpan = keySpan; + this.valueSpan = valueSpan; + } } class BoundElementProperty { - constructor( - name, - type, - securityContext, - value, - unit, - sourceSpan, - keySpan, - valueSpan - ) { - this.name = name; - this.type = type; - this.securityContext = securityContext; - this.value = value; - this.unit = unit; - this.sourceSpan = sourceSpan; - this.keySpan = keySpan; - this.valueSpan = valueSpan; - } + constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan) { + this.name = name; + this.type = type; + this.securityContext = securityContext; + this.value = value; + this.unit = unit; + this.sourceSpan = sourceSpan; + this.keySpan = keySpan; + this.valueSpan = valueSpan; + } } /** @@ -8373,114 +6840,79 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ - class EventHandlerVars {} + class EventHandlerVars { + } EventHandlerVars.event = variable('$event'); /** * Converts the given expression AST into an executable output AST, assuming the expression is * used in an action binding (e.g. an event handler). */ - function convertActionBinding( - localResolver, - implicitReceiver, - action, - bindingId, - baseSourceSpan, - implicitReceiverAccesses, - globals - ) { - if (!localResolver) { - localResolver = new DefaultLocalResolver(globals); - } - const actionWithoutBuiltins = convertPropertyBindingBuiltins( - { - createLiteralArrayConverter: (argCount) => { - // Note: no caching for literal arrays in actions. - return (args) => literalArr(args); - }, - createLiteralMapConverter: (keys) => { - // Note: no caching for literal maps in actions. - return (values) => { - const entries = keys.map((k, i) => ({ - key: k.key, - value: values[i], - quoted: k.quoted, - })); - return literalMap(entries); - }; - }, - createPipeConverter: (name) => { - throw new Error( - `Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}` - ); - }, - }, - action - ); - const visitor = new _AstToIrVisitor( - localResolver, - implicitReceiver, - bindingId, - /* supportsInterpolation */ false, - baseSourceSpan, - implicitReceiverAccesses - ); - const actionStmts = []; - flattenStatements( - actionWithoutBuiltins.visit(visitor, _Mode.Statement), - actionStmts - ); - prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts); - if (visitor.usesImplicitReceiver) { - localResolver.notifyImplicitReceiverUse(); - } - const lastIndex = actionStmts.length - 1; - if (lastIndex >= 0) { - const lastStatement = actionStmts[lastIndex]; - // Ensure that the value of the last expression statement is returned - if (lastStatement instanceof ExpressionStatement) { - actionStmts[lastIndex] = new ReturnStatement(lastStatement.expr); - } - } - return actionStmts; + function convertActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) { + if (!localResolver) { + localResolver = new DefaultLocalResolver(globals); + } + const actionWithoutBuiltins = convertPropertyBindingBuiltins({ + createLiteralArrayConverter: (argCount) => { + // Note: no caching for literal arrays in actions. + return (args) => literalArr(args); + }, + createLiteralMapConverter: (keys) => { + // Note: no caching for literal maps in actions. + return (values) => { + const entries = keys.map((k, i) => ({ + key: k.key, + value: values[i], + quoted: k.quoted, + })); + return literalMap(entries); + }; + }, + createPipeConverter: (name) => { + throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`); + } + }, action); + const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses); + const actionStmts = []; + flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts); + prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts); + if (visitor.usesImplicitReceiver) { + localResolver.notifyImplicitReceiverUse(); + } + const lastIndex = actionStmts.length - 1; + if (lastIndex >= 0) { + const lastStatement = actionStmts[lastIndex]; + // Ensure that the value of the last expression statement is returned + if (lastStatement instanceof ExpressionStatement) { + actionStmts[lastIndex] = new ReturnStatement(lastStatement.expr); + } + } + return actionStmts; } function convertPropertyBindingBuiltins(converterFactory, ast) { - return convertBuiltins(converterFactory, ast); + return convertBuiltins(converterFactory, ast); } class ConvertPropertyBindingResult { - constructor(stmts, currValExpr) { - this.stmts = stmts; - this.currValExpr = currValExpr; - } + constructor(stmts, currValExpr) { + this.stmts = stmts; + this.currValExpr = currValExpr; + } } /** * Converts the given expression AST into an executable output AST, assuming the expression * is used in property binding. The expression has to be preprocessed via * `convertPropertyBindingBuiltins`. */ - function convertPropertyBinding( - localResolver, - implicitReceiver, - expressionWithoutBuiltins, - bindingId - ) { - if (!localResolver) { - localResolver = new DefaultLocalResolver(); - } - const visitor = new _AstToIrVisitor( - localResolver, - implicitReceiver, - bindingId, - /* supportsInterpolation */ false - ); - const outputExpr = expressionWithoutBuiltins.visit( - visitor, - _Mode.Expression - ); - const stmts = getStatementsFromVisitor(visitor, bindingId); - if (visitor.usesImplicitReceiver) { - localResolver.notifyImplicitReceiverUse(); - } - return new ConvertPropertyBindingResult(stmts, outputExpr); + function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId) { + if (!localResolver) { + localResolver = new DefaultLocalResolver(); + } + const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false); + const outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression); + const stmts = getStatementsFromVisitor(visitor, bindingId); + if (visitor.usesImplicitReceiver) { + localResolver.notifyImplicitReceiverUse(); + } + return new ConvertPropertyBindingResult(stmts, outputExpr); } /** * Given some expression, such as a binding or interpolation expression, and a context expression to @@ -8498,805 +6930,663 @@ * @returns An array of expressions that can be passed as arguments to instruction expressions like * `o.importExpr(R3.propertyInterpolate).callFn(result)` */ - function convertUpdateArguments( - localResolver, - contextVariableExpression, - expressionWithArgumentsToExtract, - bindingId - ) { - const visitor = new _AstToIrVisitor( - localResolver, - contextVariableExpression, - bindingId, - /* supportsInterpolation */ true - ); - const outputExpr = visitor.visitInterpolation( - expressionWithArgumentsToExtract, - _Mode.Expression - ); - if (visitor.usesImplicitReceiver) { - localResolver.notifyImplicitReceiverUse(); - } - const stmts = getStatementsFromVisitor(visitor, bindingId); - const args = outputExpr.args; - return { stmts, args }; + function convertUpdateArguments(localResolver, contextVariableExpression, expressionWithArgumentsToExtract, bindingId) { + const visitor = new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, /* supportsInterpolation */ true); + const outputExpr = visitor.visitInterpolation(expressionWithArgumentsToExtract, _Mode.Expression); + if (visitor.usesImplicitReceiver) { + localResolver.notifyImplicitReceiverUse(); + } + const stmts = getStatementsFromVisitor(visitor, bindingId); + const args = outputExpr.args; + return { stmts, args }; } function getStatementsFromVisitor(visitor, bindingId) { - const stmts = []; - for (let i = 0; i < visitor.temporaryCount; i++) { - stmts.push(temporaryDeclaration(bindingId, i)); - } - return stmts; + const stmts = []; + for (let i = 0; i < visitor.temporaryCount; i++) { + stmts.push(temporaryDeclaration(bindingId, i)); + } + return stmts; } function convertBuiltins(converterFactory, ast) { - const visitor = new _BuiltinAstConverter(converterFactory); - return ast.visit(visitor); + const visitor = new _BuiltinAstConverter(converterFactory); + return ast.visit(visitor); } function temporaryName(bindingId, temporaryNumber) { - return `tmp_${bindingId}_${temporaryNumber}`; + return `tmp_${bindingId}_${temporaryNumber}`; } function temporaryDeclaration(bindingId, temporaryNumber) { - return new DeclareVarStmt(temporaryName(bindingId, temporaryNumber)); + return new DeclareVarStmt(temporaryName(bindingId, temporaryNumber)); } function prependTemporaryDecls(temporaryCount, bindingId, statements) { - for (let i = temporaryCount - 1; i >= 0; i--) { - statements.unshift(temporaryDeclaration(bindingId, i)); - } + for (let i = temporaryCount - 1; i >= 0; i--) { + statements.unshift(temporaryDeclaration(bindingId, i)); + } } var _Mode; (function (_Mode) { - _Mode[(_Mode['Statement'] = 0)] = 'Statement'; - _Mode[(_Mode['Expression'] = 1)] = 'Expression'; + _Mode[_Mode["Statement"] = 0] = "Statement"; + _Mode[_Mode["Expression"] = 1] = "Expression"; })(_Mode || (_Mode = {})); function ensureStatementMode(mode, ast) { - if (mode !== _Mode.Statement) { - throw new Error(`Expected a statement, but saw ${ast}`); - } + if (mode !== _Mode.Statement) { + throw new Error(`Expected a statement, but saw ${ast}`); + } } function ensureExpressionMode(mode, ast) { - if (mode !== _Mode.Expression) { - throw new Error(`Expected an expression, but saw ${ast}`); - } + if (mode !== _Mode.Expression) { + throw new Error(`Expected an expression, but saw ${ast}`); + } } function convertToStatementIfNeeded(mode, expr) { - if (mode === _Mode.Statement) { - return expr.toStmt(); - } else { - return expr; - } + if (mode === _Mode.Statement) { + return expr.toStmt(); + } + else { + return expr; + } } class _BuiltinAstConverter extends AstTransformer { - constructor(_converterFactory) { - super(); - this._converterFactory = _converterFactory; - } - visitPipe(ast, context) { - const args = [ast.exp, ...ast.args].map((ast) => - ast.visit(this, context) - ); - return new BuiltinFunctionCall( - ast.span, - ast.sourceSpan, - args, - this._converterFactory.createPipeConverter(ast.name, args.length) - ); - } - visitLiteralArray(ast, context) { - const args = ast.expressions.map((ast) => ast.visit(this, context)); - return new BuiltinFunctionCall( - ast.span, - ast.sourceSpan, - args, - this._converterFactory.createLiteralArrayConverter( - ast.expressions.length - ) - ); - } - visitLiteralMap(ast, context) { - const args = ast.values.map((ast) => ast.visit(this, context)); - return new BuiltinFunctionCall( - ast.span, - ast.sourceSpan, - args, - this._converterFactory.createLiteralMapConverter(ast.keys) - ); - } + constructor(_converterFactory) { + super(); + this._converterFactory = _converterFactory; + } + visitPipe(ast, context) { + const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context)); + return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createPipeConverter(ast.name, args.length)); + } + visitLiteralArray(ast, context) { + const args = ast.expressions.map(ast => ast.visit(this, context)); + return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length)); + } + visitLiteralMap(ast, context) { + const args = ast.values.map(ast => ast.visit(this, context)); + return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralMapConverter(ast.keys)); + } } class _AstToIrVisitor { - constructor( - _localResolver, - _implicitReceiver, - bindingId, - supportsInterpolation, - baseSourceSpan, - implicitReceiverAccesses - ) { - this._localResolver = _localResolver; - this._implicitReceiver = _implicitReceiver; - this.bindingId = bindingId; - this.supportsInterpolation = supportsInterpolation; - this.baseSourceSpan = baseSourceSpan; - this.implicitReceiverAccesses = implicitReceiverAccesses; - this._nodeMap = new Map(); - this._resultMap = new Map(); - this._currentTemporary = 0; - this.temporaryCount = 0; - this.usesImplicitReceiver = false; - } - visitUnary(ast, mode) { - let op; - switch (ast.operator) { - case '+': - op = exports.UnaryOperator.Plus; - break; - case '-': - op = exports.UnaryOperator.Minus; - break; - default: - throw new Error(`Unsupported operator ${ast.operator}`); - } - return convertToStatementIfNeeded( - mode, - new UnaryOperatorExpr( - op, - this._visit(ast.expr, _Mode.Expression), - undefined, - this.convertSourceSpan(ast.span) - ) - ); - } - visitBinary(ast, mode) { - let op; - switch (ast.operation) { - case '+': - op = exports.BinaryOperator.Plus; - break; - case '-': - op = exports.BinaryOperator.Minus; - break; - case '*': - op = exports.BinaryOperator.Multiply; - break; - case '/': - op = exports.BinaryOperator.Divide; - break; - case '%': - op = exports.BinaryOperator.Modulo; - break; - case '&&': - op = exports.BinaryOperator.And; - break; - case '||': - op = exports.BinaryOperator.Or; - break; - case '==': - op = exports.BinaryOperator.Equals; - break; - case '!=': - op = exports.BinaryOperator.NotEquals; - break; - case '===': - op = exports.BinaryOperator.Identical; - break; - case '!==': - op = exports.BinaryOperator.NotIdentical; - break; - case '<': - op = exports.BinaryOperator.Lower; - break; - case '>': - op = exports.BinaryOperator.Bigger; - break; - case '<=': - op = exports.BinaryOperator.LowerEquals; - break; - case '>=': - op = exports.BinaryOperator.BiggerEquals; - break; - case '??': - return this.convertNullishCoalesce(ast, mode); - default: - throw new Error(`Unsupported operation ${ast.operation}`); - } - return convertToStatementIfNeeded( - mode, - new BinaryOperatorExpr( - op, - this._visit(ast.left, _Mode.Expression), - this._visit(ast.right, _Mode.Expression), - undefined, - this.convertSourceSpan(ast.span) - ) - ); - } - visitChain(ast, mode) { - ensureStatementMode(mode, ast); - return this.visitAll(ast.expressions, mode); - } - visitConditional(ast, mode) { - const value = this._visit(ast.condition, _Mode.Expression); - return convertToStatementIfNeeded( - mode, - value.conditional( - this._visit(ast.trueExp, _Mode.Expression), - this._visit(ast.falseExp, _Mode.Expression), - this.convertSourceSpan(ast.span) - ) - ); - } - visitPipe(ast, mode) { - throw new Error( - `Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}` - ); - } - visitImplicitReceiver(ast, mode) { - ensureExpressionMode(mode, ast); - this.usesImplicitReceiver = true; - return this._implicitReceiver; - } - visitThisReceiver(ast, mode) { - return this.visitImplicitReceiver(ast, mode); - } - visitInterpolation(ast, mode) { - if (!this.supportsInterpolation) { - throw new Error('Unexpected interpolation'); - } - ensureExpressionMode(mode, ast); - let args = []; - for (let i = 0; i < ast.strings.length - 1; i++) { - args.push(literal(ast.strings[i])); - args.push(this._visit(ast.expressions[i], _Mode.Expression)); - } - args.push(literal(ast.strings[ast.strings.length - 1])); - // If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the - // args returned to just the value, because we're going to pass it to a special instruction. - const strings = ast.strings; - if (strings.length === 2 && strings[0] === '' && strings[1] === '') { - // Single argument interpolate instructions. - args = [args[1]]; - } else if (ast.expressions.length >= 9) { - // 9 or more arguments must be passed to the `interpolateV`-style instructions, which accept - // an array of arguments - args = [literalArr(args)]; - } - return new InterpolationExpression(args); - } - visitKeyedRead(ast, mode) { - const leftMostSafe = this.leftMostSafeNode(ast); - if (leftMostSafe) { - return this.convertSafeAccess(ast, leftMostSafe, mode); - } else { - return convertToStatementIfNeeded( - mode, - this._visit(ast.receiver, _Mode.Expression).key( - this._visit(ast.key, _Mode.Expression) - ) - ); - } - } - visitKeyedWrite(ast, mode) { - const obj = this._visit(ast.receiver, _Mode.Expression); - const key = this._visit(ast.key, _Mode.Expression); - const value = this._visit(ast.value, _Mode.Expression); - if (obj === this._implicitReceiver) { - this._localResolver.maybeRestoreView(); - } - return convertToStatementIfNeeded(mode, obj.key(key).set(value)); - } - visitLiteralArray(ast, mode) { - throw new Error( - `Illegal State: literal arrays should have been converted into functions` - ); - } - visitLiteralMap(ast, mode) { - throw new Error( - `Illegal State: literal maps should have been converted into functions` - ); - } - visitLiteralPrimitive(ast, mode) { - // For literal values of null, undefined, true, or false allow type interference - // to infer the type. - const type = - ast.value === null || - ast.value === undefined || - ast.value === true || - ast.value === true - ? INFERRED_TYPE - : undefined; - return convertToStatementIfNeeded( - mode, - literal(ast.value, type, this.convertSourceSpan(ast.span)) - ); - } - _getLocal(name, receiver) { - var _a; - if ( - ((_a = this._localResolver.globals) === null || _a === void 0 - ? void 0 - : _a.has(name)) && - receiver instanceof ThisReceiver - ) { - return null; - } - return this._localResolver.getLocal(name); - } - visitPrefixNot(ast, mode) { - return convertToStatementIfNeeded( - mode, - not(this._visit(ast.expression, _Mode.Expression)) - ); - } - visitNonNullAssert(ast, mode) { - return convertToStatementIfNeeded( - mode, - this._visit(ast.expression, _Mode.Expression) - ); - } - visitPropertyRead(ast, mode) { - const leftMostSafe = this.leftMostSafeNode(ast); - if (leftMostSafe) { - return this.convertSafeAccess(ast, leftMostSafe, mode); - } else { - let result = null; - const prevUsesImplicitReceiver = this.usesImplicitReceiver; - const receiver = this._visit(ast.receiver, _Mode.Expression); - if (receiver === this._implicitReceiver) { - result = this._getLocal(ast.name, ast.receiver); - if (result) { - // Restore the previous "usesImplicitReceiver" state since the implicit - // receiver has been replaced with a resolved local expression. - this.usesImplicitReceiver = prevUsesImplicitReceiver; - this.addImplicitReceiverAccess(ast.name); + constructor(_localResolver, _implicitReceiver, bindingId, supportsInterpolation, baseSourceSpan, implicitReceiverAccesses) { + this._localResolver = _localResolver; + this._implicitReceiver = _implicitReceiver; + this.bindingId = bindingId; + this.supportsInterpolation = supportsInterpolation; + this.baseSourceSpan = baseSourceSpan; + this.implicitReceiverAccesses = implicitReceiverAccesses; + this._nodeMap = new Map(); + this._resultMap = new Map(); + this._currentTemporary = 0; + this.temporaryCount = 0; + this.usesImplicitReceiver = false; + } + visitUnary(ast, mode) { + let op; + switch (ast.operator) { + case '+': + op = exports.UnaryOperator.Plus; + break; + case '-': + op = exports.UnaryOperator.Minus; + break; + default: + throw new Error(`Unsupported operator ${ast.operator}`); } - } - if (result == null) { - result = receiver.prop(ast.name, this.convertSourceSpan(ast.span)); - } - return convertToStatementIfNeeded(mode, result); - } - } - visitPropertyWrite(ast, mode) { - const receiver = this._visit(ast.receiver, _Mode.Expression); - const prevUsesImplicitReceiver = this.usesImplicitReceiver; - let varExpr = null; - if (receiver === this._implicitReceiver) { - const localExpr = this._getLocal(ast.name, ast.receiver); - if (localExpr) { - if (localExpr instanceof ReadPropExpr) { - // If the local variable is a property read expression, it's a reference - // to a 'context.property' value and will be used as the target of the - // write expression. - varExpr = localExpr; - // Restore the previous "usesImplicitReceiver" state since the implicit - // receiver has been replaced with a resolved local expression. - this.usesImplicitReceiver = prevUsesImplicitReceiver; - this.addImplicitReceiverAccess(ast.name); - } else { - // Otherwise it's an error. - const receiver = ast.name; - const value = - ast.value instanceof PropertyRead ? ast.value.name : undefined; - throw new Error( - `Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.` - ); + return convertToStatementIfNeeded(mode, new UnaryOperatorExpr(op, this._visit(ast.expr, _Mode.Expression), undefined, this.convertSourceSpan(ast.span))); + } + visitBinary(ast, mode) { + let op; + switch (ast.operation) { + case '+': + op = exports.BinaryOperator.Plus; + break; + case '-': + op = exports.BinaryOperator.Minus; + break; + case '*': + op = exports.BinaryOperator.Multiply; + break; + case '/': + op = exports.BinaryOperator.Divide; + break; + case '%': + op = exports.BinaryOperator.Modulo; + break; + case '&&': + op = exports.BinaryOperator.And; + break; + case '||': + op = exports.BinaryOperator.Or; + break; + case '==': + op = exports.BinaryOperator.Equals; + break; + case '!=': + op = exports.BinaryOperator.NotEquals; + break; + case '===': + op = exports.BinaryOperator.Identical; + break; + case '!==': + op = exports.BinaryOperator.NotIdentical; + break; + case '<': + op = exports.BinaryOperator.Lower; + break; + case '>': + op = exports.BinaryOperator.Bigger; + break; + case '<=': + op = exports.BinaryOperator.LowerEquals; + break; + case '>=': + op = exports.BinaryOperator.BiggerEquals; + break; + case '??': + return this.convertNullishCoalesce(ast, mode); + default: + throw new Error(`Unsupported operation ${ast.operation}`); + } + return convertToStatementIfNeeded(mode, new BinaryOperatorExpr(op, this._visit(ast.left, _Mode.Expression), this._visit(ast.right, _Mode.Expression), undefined, this.convertSourceSpan(ast.span))); + } + visitChain(ast, mode) { + ensureStatementMode(mode, ast); + return this.visitAll(ast.expressions, mode); + } + visitConditional(ast, mode) { + const value = this._visit(ast.condition, _Mode.Expression); + return convertToStatementIfNeeded(mode, value.conditional(this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span))); + } + visitPipe(ast, mode) { + throw new Error(`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`); + } + visitImplicitReceiver(ast, mode) { + ensureExpressionMode(mode, ast); + this.usesImplicitReceiver = true; + return this._implicitReceiver; + } + visitThisReceiver(ast, mode) { + return this.visitImplicitReceiver(ast, mode); + } + visitInterpolation(ast, mode) { + if (!this.supportsInterpolation) { + throw new Error('Unexpected interpolation'); + } + ensureExpressionMode(mode, ast); + let args = []; + for (let i = 0; i < ast.strings.length - 1; i++) { + args.push(literal(ast.strings[i])); + args.push(this._visit(ast.expressions[i], _Mode.Expression)); + } + args.push(literal(ast.strings[ast.strings.length - 1])); + // If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the + // args returned to just the value, because we're going to pass it to a special instruction. + const strings = ast.strings; + if (strings.length === 2 && strings[0] === '' && strings[1] === '') { + // Single argument interpolate instructions. + args = [args[1]]; + } + else if (ast.expressions.length >= 9) { + // 9 or more arguments must be passed to the `interpolateV`-style instructions, which accept + // an array of arguments + args = [literalArr(args)]; + } + return new InterpolationExpression(args); + } + visitKeyedRead(ast, mode) { + const leftMostSafe = this.leftMostSafeNode(ast); + if (leftMostSafe) { + return this.convertSafeAccess(ast, leftMostSafe, mode); + } + else { + return convertToStatementIfNeeded(mode, this._visit(ast.receiver, _Mode.Expression).key(this._visit(ast.key, _Mode.Expression))); + } + } + visitKeyedWrite(ast, mode) { + const obj = this._visit(ast.receiver, _Mode.Expression); + const key = this._visit(ast.key, _Mode.Expression); + const value = this._visit(ast.value, _Mode.Expression); + if (obj === this._implicitReceiver) { + this._localResolver.maybeRestoreView(); + } + return convertToStatementIfNeeded(mode, obj.key(key).set(value)); + } + visitLiteralArray(ast, mode) { + throw new Error(`Illegal State: literal arrays should have been converted into functions`); + } + visitLiteralMap(ast, mode) { + throw new Error(`Illegal State: literal maps should have been converted into functions`); + } + visitLiteralPrimitive(ast, mode) { + // For literal values of null, undefined, true, or false allow type interference + // to infer the type. + const type = ast.value === null || ast.value === undefined || ast.value === true || ast.value === true ? + INFERRED_TYPE : + undefined; + return convertToStatementIfNeeded(mode, literal(ast.value, type, this.convertSourceSpan(ast.span))); + } + _getLocal(name, receiver) { + var _a; + if (((_a = this._localResolver.globals) === null || _a === void 0 ? void 0 : _a.has(name)) && receiver instanceof ThisReceiver) { + return null; + } + return this._localResolver.getLocal(name); + } + visitPrefixNot(ast, mode) { + return convertToStatementIfNeeded(mode, not(this._visit(ast.expression, _Mode.Expression))); + } + visitNonNullAssert(ast, mode) { + return convertToStatementIfNeeded(mode, this._visit(ast.expression, _Mode.Expression)); + } + visitPropertyRead(ast, mode) { + const leftMostSafe = this.leftMostSafeNode(ast); + if (leftMostSafe) { + return this.convertSafeAccess(ast, leftMostSafe, mode); + } + else { + let result = null; + const prevUsesImplicitReceiver = this.usesImplicitReceiver; + const receiver = this._visit(ast.receiver, _Mode.Expression); + if (receiver === this._implicitReceiver) { + result = this._getLocal(ast.name, ast.receiver); + if (result) { + // Restore the previous "usesImplicitReceiver" state since the implicit + // receiver has been replaced with a resolved local expression. + this.usesImplicitReceiver = prevUsesImplicitReceiver; + this.addImplicitReceiverAccess(ast.name); + } + } + if (result == null) { + result = receiver.prop(ast.name, this.convertSourceSpan(ast.span)); + } + return convertToStatementIfNeeded(mode, result); + } + } + visitPropertyWrite(ast, mode) { + const receiver = this._visit(ast.receiver, _Mode.Expression); + const prevUsesImplicitReceiver = this.usesImplicitReceiver; + let varExpr = null; + if (receiver === this._implicitReceiver) { + const localExpr = this._getLocal(ast.name, ast.receiver); + if (localExpr) { + if (localExpr instanceof ReadPropExpr) { + // If the local variable is a property read expression, it's a reference + // to a 'context.property' value and will be used as the target of the + // write expression. + varExpr = localExpr; + // Restore the previous "usesImplicitReceiver" state since the implicit + // receiver has been replaced with a resolved local expression. + this.usesImplicitReceiver = prevUsesImplicitReceiver; + this.addImplicitReceiverAccess(ast.name); + } + else { + // Otherwise it's an error. + const receiver = ast.name; + const value = (ast.value instanceof PropertyRead) ? ast.value.name : undefined; + throw new Error(`Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.`); + } + } + } + // If no local expression could be produced, use the original receiver's + // property as the target. + if (varExpr === null) { + varExpr = receiver.prop(ast.name, this.convertSourceSpan(ast.span)); + } + return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression))); + } + visitSafePropertyRead(ast, mode) { + return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); + } + visitSafeKeyedRead(ast, mode) { + return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); + } + visitAll(asts, mode) { + return asts.map(ast => this._visit(ast, mode)); + } + visitCall(ast, mode) { + const leftMostSafe = this.leftMostSafeNode(ast); + if (leftMostSafe) { + return this.convertSafeAccess(ast, leftMostSafe, mode); + } + const convertedArgs = this.visitAll(ast.args, _Mode.Expression); + if (ast instanceof BuiltinFunctionCall) { + return convertToStatementIfNeeded(mode, ast.converter(convertedArgs)); + } + const receiver = ast.receiver; + if (receiver instanceof PropertyRead && + receiver.receiver instanceof ImplicitReceiver && + !(receiver.receiver instanceof ThisReceiver) && receiver.name === '$any') { + if (convertedArgs.length !== 1) { + throw new Error(`Invalid call to $any, expected 1 argument but received ${convertedArgs.length || 'none'}`); + } + return convertToStatementIfNeeded(mode, convertedArgs[0]); + } + const call = this._visit(receiver, _Mode.Expression) + .callFn(convertedArgs, this.convertSourceSpan(ast.span)); + return convertToStatementIfNeeded(mode, call); + } + visitSafeCall(ast, mode) { + return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); + } + _visit(ast, mode) { + const result = this._resultMap.get(ast); + if (result) + return result; + return (this._nodeMap.get(ast) || ast).visit(this, mode); + } + convertSafeAccess(ast, leftMostSafe, mode) { + // If the expression contains a safe access node on the left it needs to be converted to + // an expression that guards the access to the member by checking the receiver for blank. As + // execution proceeds from left to right, the left most part of the expression must be guarded + // first but, because member access is left associative, the right side of the expression is at + // the top of the AST. The desired result requires lifting a copy of the left part of the + // expression up to test it for blank before generating the unguarded version. + // Consider, for example the following expression: a?.b.c?.d.e + // This results in the ast: + // . + // / \ + // ?. e + // / \ + // . d + // / \ + // ?. c + // / \ + // a b + // The following tree should be generated: + // + // /---- ? ----\ + // / | \ + // a /--- ? ---\ null + // / | \ + // . . null + // / \ / \ + // . c . e + // / \ / \ + // a b . d + // / \ + // . c + // / \ + // a b + // + // Notice that the first guard condition is the left hand of the left most safe access node + // which comes in as leftMostSafe to this routine. + let guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression); + let temporary = undefined; + if (this.needsTemporaryInSafeAccess(leftMostSafe.receiver)) { + // If the expression has method calls or pipes then we need to save the result into a + // temporary variable to avoid calling stateful or impure code more than once. + temporary = this.allocateTemporary(); + // Preserve the result in the temporary variable + guardedExpression = temporary.set(guardedExpression); + // Ensure all further references to the guarded expression refer to the temporary instead. + this._resultMap.set(leftMostSafe.receiver, temporary); + } + const condition = guardedExpression.isBlank(); + // Convert the ast to an unguarded access to the receiver's member. The map will substitute + // leftMostNode with its unguarded version in the call to `this.visit()`. + if (leftMostSafe instanceof SafeCall) { + this._nodeMap.set(leftMostSafe, new Call(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, leftMostSafe.args, leftMostSafe.argumentSpan)); + } + else if (leftMostSafe instanceof SafeKeyedRead) { + this._nodeMap.set(leftMostSafe, new KeyedRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, leftMostSafe.key)); + } + else { + this._nodeMap.set(leftMostSafe, new PropertyRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name)); + } + // Recursively convert the node now without the guarded member access. + const access = this._visit(ast, _Mode.Expression); + // Remove the mapping. This is not strictly required as the converter only traverses each node + // once but is safer if the conversion is changed to traverse the nodes more than once. + this._nodeMap.delete(leftMostSafe); + // If we allocated a temporary, release it. + if (temporary) { + this.releaseTemporary(temporary); + } + // Produce the conditional + return convertToStatementIfNeeded(mode, condition.conditional(NULL_EXPR, access)); + } + convertNullishCoalesce(ast, mode) { + const left = this._visit(ast.left, _Mode.Expression); + const right = this._visit(ast.right, _Mode.Expression); + const temporary = this.allocateTemporary(); + this.releaseTemporary(temporary); + // Generate the following expression. It is identical to how TS + // transpiles binary expressions with a nullish coalescing operator. + // let temp; + // (temp = a) !== null && temp !== undefined ? temp : b; + return convertToStatementIfNeeded(mode, temporary.set(left) + .notIdentical(NULL_EXPR) + .and(temporary.notIdentical(literal(undefined))) + .conditional(temporary, right)); + } + // Given an expression of the form a?.b.c?.d.e then the left most safe node is + // the (a?.b). The . and ?. are left associative thus can be rewritten as: + // ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or + // safe method call as this needs to be transformed initially to: + // a == null ? null : a.c.b.c?.d.e + // then to: + // a == null ? null : a.b.c == null ? null : a.b.c.d.e + leftMostSafeNode(ast) { + const visit = (visitor, ast) => { + return (this._nodeMap.get(ast) || ast).visit(visitor); + }; + return ast.visit({ + visitUnary(ast) { + return null; + }, + visitBinary(ast) { + return null; + }, + visitChain(ast) { + return null; + }, + visitConditional(ast) { + return null; + }, + visitCall(ast) { + return visit(this, ast.receiver); + }, + visitSafeCall(ast) { + return visit(this, ast.receiver) || ast; + }, + visitImplicitReceiver(ast) { + return null; + }, + visitThisReceiver(ast) { + return null; + }, + visitInterpolation(ast) { + return null; + }, + visitKeyedRead(ast) { + return visit(this, ast.receiver); + }, + visitKeyedWrite(ast) { + return null; + }, + visitLiteralArray(ast) { + return null; + }, + visitLiteralMap(ast) { + return null; + }, + visitLiteralPrimitive(ast) { + return null; + }, + visitPipe(ast) { + return null; + }, + visitPrefixNot(ast) { + return null; + }, + visitNonNullAssert(ast) { + return null; + }, + visitPropertyRead(ast) { + return visit(this, ast.receiver); + }, + visitPropertyWrite(ast) { + return null; + }, + visitSafePropertyRead(ast) { + return visit(this, ast.receiver) || ast; + }, + visitSafeKeyedRead(ast) { + return visit(this, ast.receiver) || ast; + } + }); + } + // Returns true of the AST includes a method or a pipe indicating that, if the + // expression is used as the target of a safe property or method access then + // the expression should be stored into a temporary variable. + needsTemporaryInSafeAccess(ast) { + const visit = (visitor, ast) => { + return ast && (this._nodeMap.get(ast) || ast).visit(visitor); + }; + const visitSome = (visitor, ast) => { + return ast.some(ast => visit(visitor, ast)); + }; + return ast.visit({ + visitUnary(ast) { + return visit(this, ast.expr); + }, + visitBinary(ast) { + return visit(this, ast.left) || visit(this, ast.right); + }, + visitChain(ast) { + return false; + }, + visitConditional(ast) { + return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp); + }, + visitCall(ast) { + return true; + }, + visitSafeCall(ast) { + return true; + }, + visitImplicitReceiver(ast) { + return false; + }, + visitThisReceiver(ast) { + return false; + }, + visitInterpolation(ast) { + return visitSome(this, ast.expressions); + }, + visitKeyedRead(ast) { + return false; + }, + visitKeyedWrite(ast) { + return false; + }, + visitLiteralArray(ast) { + return true; + }, + visitLiteralMap(ast) { + return true; + }, + visitLiteralPrimitive(ast) { + return false; + }, + visitPipe(ast) { + return true; + }, + visitPrefixNot(ast) { + return visit(this, ast.expression); + }, + visitNonNullAssert(ast) { + return visit(this, ast.expression); + }, + visitPropertyRead(ast) { + return false; + }, + visitPropertyWrite(ast) { + return false; + }, + visitSafePropertyRead(ast) { + return false; + }, + visitSafeKeyedRead(ast) { + return false; + } + }); + } + allocateTemporary() { + const tempNumber = this._currentTemporary++; + this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount); + return new ReadVarExpr(temporaryName(this.bindingId, tempNumber)); + } + releaseTemporary(temporary) { + this._currentTemporary--; + if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) { + throw new Error(`Temporary ${temporary.name} released out of order`); + } + } + /** + * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`. + * + * `ParseSpan` objects are relative to the start of the expression. + * This method converts these to full `ParseSourceSpan` objects that + * show where the span is within the overall source file. + * + * @param span the relative span to convert. + * @returns a `ParseSourceSpan` for the given span or null if no + * `baseSourceSpan` was provided to this class. + */ + convertSourceSpan(span) { + if (this.baseSourceSpan) { + const start = this.baseSourceSpan.start.moveBy(span.start); + const end = this.baseSourceSpan.start.moveBy(span.end); + const fullStart = this.baseSourceSpan.fullStart.moveBy(span.start); + return new ParseSourceSpan(start, end, fullStart); + } + else { + return null; + } + } + /** Adds the name of an AST to the list of implicit receiver accesses. */ + addImplicitReceiverAccess(name) { + if (this.implicitReceiverAccesses) { + this.implicitReceiverAccesses.add(name); } - } } - // If no local expression could be produced, use the original receiver's - // property as the target. - if (varExpr === null) { - varExpr = receiver.prop(ast.name, this.convertSourceSpan(ast.span)); - } - return convertToStatementIfNeeded( - mode, - varExpr.set(this._visit(ast.value, _Mode.Expression)) - ); - } - visitSafePropertyRead(ast, mode) { - return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); - } - visitSafeKeyedRead(ast, mode) { - return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); - } - visitAll(asts, mode) { - return asts.map((ast) => this._visit(ast, mode)); - } - visitCall(ast, mode) { - const leftMostSafe = this.leftMostSafeNode(ast); - if (leftMostSafe) { - return this.convertSafeAccess(ast, leftMostSafe, mode); - } - const convertedArgs = this.visitAll(ast.args, _Mode.Expression); - if (ast instanceof BuiltinFunctionCall) { - return convertToStatementIfNeeded(mode, ast.converter(convertedArgs)); - } - const receiver = ast.receiver; - if ( - receiver instanceof PropertyRead && - receiver.receiver instanceof ImplicitReceiver && - !(receiver.receiver instanceof ThisReceiver) && - receiver.name === '$any' - ) { - if (convertedArgs.length !== 1) { - throw new Error( - `Invalid call to $any, expected 1 argument but received ${ - convertedArgs.length || 'none' - }` - ); - } - return convertToStatementIfNeeded(mode, convertedArgs[0]); - } - const call = this._visit(receiver, _Mode.Expression).callFn( - convertedArgs, - this.convertSourceSpan(ast.span) - ); - return convertToStatementIfNeeded(mode, call); - } - visitSafeCall(ast, mode) { - return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode); - } - _visit(ast, mode) { - const result = this._resultMap.get(ast); - if (result) return result; - return (this._nodeMap.get(ast) || ast).visit(this, mode); - } - convertSafeAccess(ast, leftMostSafe, mode) { - // If the expression contains a safe access node on the left it needs to be converted to - // an expression that guards the access to the member by checking the receiver for blank. As - // execution proceeds from left to right, the left most part of the expression must be guarded - // first but, because member access is left associative, the right side of the expression is at - // the top of the AST. The desired result requires lifting a copy of the left part of the - // expression up to test it for blank before generating the unguarded version. - // Consider, for example the following expression: a?.b.c?.d.e - // This results in the ast: - // . - // / \ - // ?. e - // / \ - // . d - // / \ - // ?. c - // / \ - // a b - // The following tree should be generated: - // - // /---- ? ----\ - // / | \ - // a /--- ? ---\ null - // / | \ - // . . null - // / \ / \ - // . c . e - // / \ / \ - // a b . d - // / \ - // . c - // / \ - // a b - // - // Notice that the first guard condition is the left hand of the left most safe access node - // which comes in as leftMostSafe to this routine. - let guardedExpression = this._visit( - leftMostSafe.receiver, - _Mode.Expression - ); - let temporary = undefined; - if (this.needsTemporaryInSafeAccess(leftMostSafe.receiver)) { - // If the expression has method calls or pipes then we need to save the result into a - // temporary variable to avoid calling stateful or impure code more than once. - temporary = this.allocateTemporary(); - // Preserve the result in the temporary variable - guardedExpression = temporary.set(guardedExpression); - // Ensure all further references to the guarded expression refer to the temporary instead. - this._resultMap.set(leftMostSafe.receiver, temporary); - } - const condition = guardedExpression.isBlank(); - // Convert the ast to an unguarded access to the receiver's member. The map will substitute - // leftMostNode with its unguarded version in the call to `this.visit()`. - if (leftMostSafe instanceof SafeCall) { - this._nodeMap.set( - leftMostSafe, - new Call( - leftMostSafe.span, - leftMostSafe.sourceSpan, - leftMostSafe.receiver, - leftMostSafe.args, - leftMostSafe.argumentSpan - ) - ); - } else if (leftMostSafe instanceof SafeKeyedRead) { - this._nodeMap.set( - leftMostSafe, - new KeyedRead( - leftMostSafe.span, - leftMostSafe.sourceSpan, - leftMostSafe.receiver, - leftMostSafe.key - ) - ); - } else { - this._nodeMap.set( - leftMostSafe, - new PropertyRead( - leftMostSafe.span, - leftMostSafe.sourceSpan, - leftMostSafe.nameSpan, - leftMostSafe.receiver, - leftMostSafe.name - ) - ); - } - // Recursively convert the node now without the guarded member access. - const access = this._visit(ast, _Mode.Expression); - // Remove the mapping. This is not strictly required as the converter only traverses each node - // once but is safer if the conversion is changed to traverse the nodes more than once. - this._nodeMap.delete(leftMostSafe); - // If we allocated a temporary, release it. - if (temporary) { - this.releaseTemporary(temporary); - } - // Produce the conditional - return convertToStatementIfNeeded( - mode, - condition.conditional(NULL_EXPR, access) - ); - } - convertNullishCoalesce(ast, mode) { - const left = this._visit(ast.left, _Mode.Expression); - const right = this._visit(ast.right, _Mode.Expression); - const temporary = this.allocateTemporary(); - this.releaseTemporary(temporary); - // Generate the following expression. It is identical to how TS - // transpiles binary expressions with a nullish coalescing operator. - // let temp; - // (temp = a) !== null && temp !== undefined ? temp : b; - return convertToStatementIfNeeded( - mode, - temporary - .set(left) - .notIdentical(NULL_EXPR) - .and(temporary.notIdentical(literal(undefined))) - .conditional(temporary, right) - ); - } - // Given an expression of the form a?.b.c?.d.e then the left most safe node is - // the (a?.b). The . and ?. are left associative thus can be rewritten as: - // ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or - // safe method call as this needs to be transformed initially to: - // a == null ? null : a.c.b.c?.d.e - // then to: - // a == null ? null : a.b.c == null ? null : a.b.c.d.e - leftMostSafeNode(ast) { - const visit = (visitor, ast) => { - return (this._nodeMap.get(ast) || ast).visit(visitor); - }; - return ast.visit({ - visitUnary(ast) { - return null; - }, - visitBinary(ast) { - return null; - }, - visitChain(ast) { - return null; - }, - visitConditional(ast) { - return null; - }, - visitCall(ast) { - return visit(this, ast.receiver); - }, - visitSafeCall(ast) { - return visit(this, ast.receiver) || ast; - }, - visitImplicitReceiver(ast) { - return null; - }, - visitThisReceiver(ast) { - return null; - }, - visitInterpolation(ast) { - return null; - }, - visitKeyedRead(ast) { - return visit(this, ast.receiver); - }, - visitKeyedWrite(ast) { - return null; - }, - visitLiteralArray(ast) { - return null; - }, - visitLiteralMap(ast) { - return null; - }, - visitLiteralPrimitive(ast) { - return null; - }, - visitPipe(ast) { - return null; - }, - visitPrefixNot(ast) { - return null; - }, - visitNonNullAssert(ast) { - return null; - }, - visitPropertyRead(ast) { - return visit(this, ast.receiver); - }, - visitPropertyWrite(ast) { - return null; - }, - visitSafePropertyRead(ast) { - return visit(this, ast.receiver) || ast; - }, - visitSafeKeyedRead(ast) { - return visit(this, ast.receiver) || ast; - }, - }); - } - // Returns true of the AST includes a method or a pipe indicating that, if the - // expression is used as the target of a safe property or method access then - // the expression should be stored into a temporary variable. - needsTemporaryInSafeAccess(ast) { - const visit = (visitor, ast) => { - return ast && (this._nodeMap.get(ast) || ast).visit(visitor); - }; - const visitSome = (visitor, ast) => { - return ast.some((ast) => visit(visitor, ast)); - }; - return ast.visit({ - visitUnary(ast) { - return visit(this, ast.expr); - }, - visitBinary(ast) { - return visit(this, ast.left) || visit(this, ast.right); - }, - visitChain(ast) { - return false; - }, - visitConditional(ast) { - return ( - visit(this, ast.condition) || - visit(this, ast.trueExp) || - visit(this, ast.falseExp) - ); - }, - visitCall(ast) { - return true; - }, - visitSafeCall(ast) { - return true; - }, - visitImplicitReceiver(ast) { - return false; - }, - visitThisReceiver(ast) { - return false; - }, - visitInterpolation(ast) { - return visitSome(this, ast.expressions); - }, - visitKeyedRead(ast) { - return false; - }, - visitKeyedWrite(ast) { - return false; - }, - visitLiteralArray(ast) { - return true; - }, - visitLiteralMap(ast) { - return true; - }, - visitLiteralPrimitive(ast) { - return false; - }, - visitPipe(ast) { - return true; - }, - visitPrefixNot(ast) { - return visit(this, ast.expression); - }, - visitNonNullAssert(ast) { - return visit(this, ast.expression); - }, - visitPropertyRead(ast) { - return false; - }, - visitPropertyWrite(ast) { - return false; - }, - visitSafePropertyRead(ast) { - return false; - }, - visitSafeKeyedRead(ast) { - return false; - }, - }); - } - allocateTemporary() { - const tempNumber = this._currentTemporary++; - this.temporaryCount = Math.max( - this._currentTemporary, - this.temporaryCount - ); - return new ReadVarExpr(temporaryName(this.bindingId, tempNumber)); - } - releaseTemporary(temporary) { - this._currentTemporary--; - if ( - temporary.name != temporaryName(this.bindingId, this._currentTemporary) - ) { - throw new Error(`Temporary ${temporary.name} released out of order`); - } - } - /** - * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`. - * - * `ParseSpan` objects are relative to the start of the expression. - * This method converts these to full `ParseSourceSpan` objects that - * show where the span is within the overall source file. - * - * @param span the relative span to convert. - * @returns a `ParseSourceSpan` for the given span or null if no - * `baseSourceSpan` was provided to this class. - */ - convertSourceSpan(span) { - if (this.baseSourceSpan) { - const start = this.baseSourceSpan.start.moveBy(span.start); - const end = this.baseSourceSpan.start.moveBy(span.end); - const fullStart = this.baseSourceSpan.fullStart.moveBy(span.start); - return new ParseSourceSpan(start, end, fullStart); - } else { - return null; - } - } - /** Adds the name of an AST to the list of implicit receiver accesses. */ - addImplicitReceiverAccess(name) { - if (this.implicitReceiverAccesses) { - this.implicitReceiverAccesses.add(name); - } - } } function flattenStatements(arg, output) { - if (Array.isArray(arg)) { - arg.forEach((entry) => flattenStatements(entry, output)); - } else { - output.push(arg); - } + if (Array.isArray(arg)) { + arg.forEach((entry) => flattenStatements(entry, output)); + } + else { + output.push(arg); + } } function unsupported() { - throw new Error('Unsupported operation'); + throw new Error('Unsupported operation'); } class InterpolationExpression extends Expression { - constructor(args) { - super(null, null); - this.args = args; - this.isConstant = unsupported; - this.isEquivalent = unsupported; - this.visitExpression = unsupported; - } + constructor(args) { + super(null, null); + this.args = args; + this.isConstant = unsupported; + this.isEquivalent = unsupported; + this.visitExpression = unsupported; + } } class DefaultLocalResolver { - constructor(globals) { - this.globals = globals; - } - notifyImplicitReceiverUse() {} - maybeRestoreView() {} - getLocal(name) { - if (name === EventHandlerVars.event.name) { - return EventHandlerVars.event; + constructor(globals) { + this.globals = globals; + } + notifyImplicitReceiverUse() { } + maybeRestoreView() { } + getLocal(name) { + if (name === EventHandlerVars.event.name) { + return EventHandlerVars.event; + } + return null; } - return null; - } } class BuiltinFunctionCall extends Call { - constructor(span, sourceSpan, args, converter) { - super(span, sourceSpan, new EmptyExpr(span, sourceSpan), args, null); - this.converter = converter; - } + constructor(span, sourceSpan, args, converter) { + super(span, sourceSpan, new EmptyExpr(span, sourceSpan), args, null); + this.converter = converter; + } } /** @@ -9319,56 +7609,57 @@ /** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */ let _SECURITY_SCHEMA; function SECURITY_SCHEMA() { - if (!_SECURITY_SCHEMA) { - _SECURITY_SCHEMA = {}; - // Case is insignificant below, all element and attribute names are lower-cased for lookup. - registerContext(SecurityContext.HTML, [ - 'iframe|srcdoc', - '*|innerHTML', - '*|outerHTML', - ]); - registerContext(SecurityContext.STYLE, ['*|style']); - // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them. - registerContext(SecurityContext.URL, [ - '*|formAction', - 'area|href', - 'area|ping', - 'audio|src', - 'a|href', - 'a|ping', - 'blockquote|cite', - 'body|background', - 'del|cite', - 'form|action', - 'img|src', - 'input|src', - 'ins|cite', - 'q|cite', - 'source|src', - 'track|src', - 'video|poster', - 'video|src', - ]); - registerContext(SecurityContext.RESOURCE_URL, [ - 'applet|code', - 'applet|codebase', - 'base|href', - 'embed|src', - 'frame|src', - 'head|profile', - 'html|manifest', - 'iframe|src', - 'link|href', - 'media|src', - 'object|codebase', - 'object|data', - 'script|src', - ]); - } - return _SECURITY_SCHEMA; + if (!_SECURITY_SCHEMA) { + _SECURITY_SCHEMA = {}; + // Case is insignificant below, all element and attribute names are lower-cased for lookup. + registerContext(SecurityContext.HTML, [ + 'iframe|srcdoc', + '*|innerHTML', + '*|outerHTML', + ]); + registerContext(SecurityContext.STYLE, ['*|style']); + // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them. + registerContext(SecurityContext.URL, [ + '*|formAction', + 'area|href', + 'area|ping', + 'audio|src', + 'a|href', + 'a|ping', + 'blockquote|cite', + 'body|background', + 'del|cite', + 'form|action', + 'img|src', + 'input|src', + 'ins|cite', + 'q|cite', + 'source|src', + 'track|src', + 'video|poster', + 'video|src', + ]); + registerContext(SecurityContext.RESOURCE_URL, [ + 'applet|code', + 'applet|codebase', + 'base|href', + 'embed|src', + 'frame|src', + 'head|profile', + 'html|manifest', + 'iframe|src', + 'link|href', + 'media|src', + 'object|codebase', + 'object|data', + 'script|src', + ]); + } + return _SECURITY_SCHEMA; } function registerContext(ctx, specs) { - for (const spec of specs) _SECURITY_SCHEMA[spec.toLowerCase()] = ctx; + for (const spec of specs) + _SECURITY_SCHEMA[spec.toLowerCase()] = ctx; } /** * The set of security-sensitive attributes of an `