mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
This commit adds `applicationProviders` to the `bootstrapModule` options object. This allows specifying additional providers at the location of bootstrap, which makes default providers much easier to accomplish. Using this, we can refine the approach taken for downgrade_module to use this more direct API rather than the additional provider variable dance. PR Close #64354
456 lines
21 KiB
TypeScript
456 lines
21 KiB
TypeScript
/**
|
|
* @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.dev/license
|
|
*/
|
|
|
|
import {
|
|
Injector,
|
|
NgModuleFactory,
|
|
NgModuleRef,
|
|
PlatformRef,
|
|
StaticProvider,
|
|
Type,
|
|
ɵinternalProvideZoneChangeDetection as internalProvideZoneChangeDetection,
|
|
} from '@angular/core';
|
|
import {platformBrowser} from '@angular/platform-browser';
|
|
|
|
import {ɵangular1, ɵconstants, ɵutil} from '../common';
|
|
|
|
import {angular1Providers, setTempInjectorRef} from './angular1_providers';
|
|
import {NgAdapterInjector} from './util';
|
|
|
|
let moduleUid = 0;
|
|
|
|
/**
|
|
* @description
|
|
*
|
|
* A helper function for creating an AngularJS module that can bootstrap an Angular module
|
|
* "on-demand" (possibly lazily) when a {@link downgradeComponent downgraded component} needs to be
|
|
* instantiated.
|
|
*
|
|
* *Part of the [upgrade/static](api?query=upgrade/static) library for hybrid upgrade apps that
|
|
* support AOT compilation.*
|
|
*
|
|
* It allows loading/bootstrapping the Angular part of a hybrid application lazily and not having to
|
|
* pay the cost up-front. For example, you can have an AngularJS application that uses Angular for
|
|
* specific routes and only instantiate the Angular modules if/when the user visits one of these
|
|
* routes.
|
|
*
|
|
* The Angular module will be bootstrapped once (when requested for the first time) and the same
|
|
* reference will be used from that point onwards.
|
|
*
|
|
* `downgradeModule()` requires either an `NgModuleFactory`, `NgModule` class or a function:
|
|
* - `NgModuleFactory`: If you pass an `NgModuleFactory`, it will be used to instantiate a module
|
|
* using `platformBrowser`'s {@link PlatformRef#bootstrapModuleFactory bootstrapModuleFactory()}.
|
|
* NOTE: this type of the argument is deprecated. Please either provide an `NgModule` class or a
|
|
* bootstrap function instead.
|
|
* - `NgModule` class: If you pass an NgModule class, it will be used to instantiate a module
|
|
* using `platformBrowser`'s {@link PlatformRef#bootstrapModule bootstrapModule()}.
|
|
* - `Function`: If you pass a function, it is expected to return a promise resolving to an
|
|
* `NgModuleRef`. The function is called with an array of extra {@link StaticProvider Providers}
|
|
* that are expected to be available from the returned `NgModuleRef`'s `Injector`.
|
|
*
|
|
* `downgradeModule()` returns the name of the created AngularJS wrapper module. You can use it to
|
|
* declare a dependency in your main AngularJS module.
|
|
*
|
|
* {@example upgrade/static/ts/lite/module.ts region="basic-how-to"}
|
|
*
|
|
* For more details on how to use `downgradeModule()` see
|
|
* [Upgrading for Performance](https://angular.io/guide/upgrade).
|
|
*
|
|
* @usageNotes
|
|
*
|
|
* Apart from `UpgradeModule`, you can use the rest of the `upgrade/static` helpers as usual to
|
|
* build a hybrid application. Note that the Angular pieces (e.g. downgraded services) will not be
|
|
* available until the downgraded module has been bootstrapped, i.e. by instantiating a downgraded
|
|
* component.
|
|
*
|
|
* <div class="docs-alert docs-alert-important">
|
|
*
|
|
* You cannot use `downgradeModule()` and `UpgradeModule` in the same hybrid application.<br />
|
|
* Use one or the other.
|
|
*
|
|
* </div>
|
|
*
|
|
* ### Differences with `UpgradeModule`
|
|
*
|
|
* Besides their different API, there are two important internal differences between
|
|
* `downgradeModule()` and `UpgradeModule` that affect the behavior of hybrid applications:
|
|
*
|
|
* 1. Unlike `UpgradeModule`, `downgradeModule()` does not bootstrap the main AngularJS module
|
|
* inside the {@link NgZone Angular zone}.
|
|
* 2. Unlike `UpgradeModule`, `downgradeModule()` does not automatically run a
|
|
* [$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest) when changes are
|
|
* detected in the Angular part of the application.
|
|
*
|
|
* What this means is that applications using `UpgradeModule` will run change detection more
|
|
* frequently in order to ensure that both frameworks are properly notified about possible changes.
|
|
* This will inevitably result in more change detection runs than necessary.
|
|
*
|
|
* `downgradeModule()`, on the other side, does not try to tie the two change detection systems as
|
|
* tightly, restricting the explicit change detection runs only to cases where it knows it is
|
|
* necessary (e.g. when the inputs of a downgraded component change). This improves performance,
|
|
* especially in change-detection-heavy applications, but leaves it up to the developer to manually
|
|
* notify each framework as needed.
|
|
*
|
|
* For a more detailed discussion of the differences and their implications, see
|
|
* [Upgrading for Performance](https://angular.io/guide/upgrade).
|
|
*
|
|
* <div class="docs-alert docs-alert-helpful">
|
|
*
|
|
* You can manually trigger a change detection run in AngularJS using
|
|
* [scope.$apply(...)](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply) or
|
|
* [$rootScope.$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest).
|
|
*
|
|
* You can manually trigger a change detection run in Angular using {@link NgZone#run
|
|
* ngZone.run(...)}.
|
|
*
|
|
* </div>
|
|
*
|
|
* ### Downgrading multiple modules
|
|
*
|
|
* It is possible to downgrade multiple modules and include them in an AngularJS application. In
|
|
* that case, each downgraded module will be bootstrapped when an associated downgraded component or
|
|
* injectable needs to be instantiated.
|
|
*
|
|
* Things to keep in mind, when downgrading multiple modules:
|
|
*
|
|
* - Each downgraded component/injectable needs to be explicitly associated with a downgraded
|
|
* module. See `downgradeComponent()` and `downgradeInjectable()` for more details.
|
|
*
|
|
* - If you want some injectables to be shared among all downgraded modules, you can provide them as
|
|
* `StaticProvider`s, when creating the `PlatformRef` (e.g. via `platformBrowser` or
|
|
* `platformBrowserDynamic`).
|
|
*
|
|
* - When using {@link PlatformRef#bootstrapmodule `bootstrapModule()`} or
|
|
* {@link PlatformRef#bootstrapmodulefactory `bootstrapModuleFactory()`} to bootstrap the
|
|
* downgraded modules, each one is considered a "root" module. As a consequence, a new instance
|
|
* will be created for every injectable provided in `"root"` (via
|
|
* {@link /api/core/Injectable#providedIn providedIn}
|
|
* If this is not your intention, you can have a shared module (that will act as act as the "root"
|
|
* module) and create all downgraded modules using that module's injector:
|
|
*
|
|
* {@example upgrade/static/ts/lite-multi-shared/module.ts region="shared-root-module"}
|
|
*
|
|
* @publicApi
|
|
*/
|
|
export function downgradeModule<T>(
|
|
moduleOrBootstrapFn: Type<T> | ((extraProviders: StaticProvider[]) => Promise<NgModuleRef<T>>),
|
|
): string;
|
|
/**
|
|
* @description
|
|
*
|
|
* A helper function for creating an AngularJS module that can bootstrap an Angular module
|
|
* "on-demand" (possibly lazily) when a {@link downgradeComponent downgraded component} needs to be
|
|
* instantiated.
|
|
*
|
|
* *Part of the [upgrade/static](api?query=upgrade/static) library for hybrid upgrade apps that
|
|
* support AOT compilation.*
|
|
*
|
|
* It allows loading/bootstrapping the Angular part of a hybrid application lazily and not having to
|
|
* pay the cost up-front. For example, you can have an AngularJS application that uses Angular for
|
|
* specific routes and only instantiate the Angular modules if/when the user visits one of these
|
|
* routes.
|
|
*
|
|
* The Angular module will be bootstrapped once (when requested for the first time) and the same
|
|
* reference will be used from that point onwards.
|
|
*
|
|
* `downgradeModule()` requires either an `NgModuleFactory`, `NgModule` class or a function:
|
|
* - `NgModuleFactory`: If you pass an `NgModuleFactory`, it will be used to instantiate a module
|
|
* using `platformBrowser`'s {@link PlatformRef#bootstrapModuleFactory bootstrapModuleFactory()}.
|
|
* NOTE: this type of the argument is deprecated. Please either provide an `NgModule` class or a
|
|
* bootstrap function instead.
|
|
* - `NgModule` class: If you pass an NgModule class, it will be used to instantiate a module
|
|
* using `platformBrowser`'s {@link PlatformRef#bootstrapModule bootstrapModule()}.
|
|
* - `Function`: If you pass a function, it is expected to return a promise resolving to an
|
|
* `NgModuleRef`. The function is called with an array of extra {@link StaticProvider Providers}
|
|
* that are expected to be available from the returned `NgModuleRef`'s `Injector`.
|
|
*
|
|
* `downgradeModule()` returns the name of the created AngularJS wrapper module. You can use it to
|
|
* declare a dependency in your main AngularJS module.
|
|
*
|
|
* {@example upgrade/static/ts/lite/module.ts region="basic-how-to"}
|
|
*
|
|
* For more details on how to use `downgradeModule()` see
|
|
* [Upgrading for Performance](https://angular.io/guide/upgrade).
|
|
*
|
|
* @usageNotes
|
|
*
|
|
* Apart from `UpgradeModule`, you can use the rest of the `upgrade/static` helpers as usual to
|
|
* build a hybrid application. Note that the Angular pieces (e.g. downgraded services) will not be
|
|
* available until the downgraded module has been bootstrapped, i.e. by instantiating a downgraded
|
|
* component.
|
|
*
|
|
* <div class="docs-alert docs-alert-important">
|
|
*
|
|
* You cannot use `downgradeModule()` and `UpgradeModule` in the same hybrid application.<br />
|
|
* Use one or the other.
|
|
*
|
|
* </div>
|
|
*
|
|
* ### Differences with `UpgradeModule`
|
|
*
|
|
* Besides their different API, there are two important internal differences between
|
|
* `downgradeModule()` and `UpgradeModule` that affect the behavior of hybrid applications:
|
|
*
|
|
* 1. Unlike `UpgradeModule`, `downgradeModule()` does not bootstrap the main AngularJS module
|
|
* inside the {@link NgZone Angular zone}.
|
|
* 2. Unlike `UpgradeModule`, `downgradeModule()` does not automatically run a
|
|
* [$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest) when changes are
|
|
* detected in the Angular part of the application.
|
|
*
|
|
* What this means is that applications using `UpgradeModule` will run change detection more
|
|
* frequently in order to ensure that both frameworks are properly notified about possible changes.
|
|
* This will inevitably result in more change detection runs than necessary.
|
|
*
|
|
* `downgradeModule()`, on the other side, does not try to tie the two change detection systems as
|
|
* tightly, restricting the explicit change detection runs only to cases where it knows it is
|
|
* necessary (e.g. when the inputs of a downgraded component change). This improves performance,
|
|
* especially in change-detection-heavy applications, but leaves it up to the developer to manually
|
|
* notify each framework as needed.
|
|
*
|
|
* For a more detailed discussion of the differences and their implications, see
|
|
* [Upgrading for Performance](https://angular.io/guide/upgrade).
|
|
*
|
|
* <div class="docs-alert docs-alert-helpful">
|
|
*
|
|
* You can manually trigger a change detection run in AngularJS using
|
|
* [scope.$apply(...)](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply) or
|
|
* [$rootScope.$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest).
|
|
*
|
|
* You can manually trigger a change detection run in Angular using {@link NgZone#run
|
|
* ngZone.run(...)}.
|
|
*
|
|
* </div>
|
|
*
|
|
* ### Downgrading multiple modules
|
|
*
|
|
* It is possible to downgrade multiple modules and include them in an AngularJS application. In
|
|
* that case, each downgraded module will be bootstrapped when an associated downgraded component or
|
|
* injectable needs to be instantiated.
|
|
*
|
|
* Things to keep in mind, when downgrading multiple modules:
|
|
*
|
|
* - Each downgraded component/injectable needs to be explicitly associated with a downgraded
|
|
* module. See `downgradeComponent()` and `downgradeInjectable()` for more details.
|
|
*
|
|
* - If you want some injectables to be shared among all downgraded modules, you can provide them as
|
|
* `StaticProvider`s, when creating the `PlatformRef` (e.g. via `platformBrowser` or
|
|
* `platformBrowserDynamic`).
|
|
*
|
|
* - When using {@link PlatformRef#bootstrapmodule `bootstrapModule()`} or
|
|
* {@link PlatformRef#bootstrapmodulefactory `bootstrapModuleFactory()`} to bootstrap the
|
|
* downgraded modules, each one is considered a "root" module. As a consequence, a new instance
|
|
* will be created for every injectable provided in `"root"` (via
|
|
* {@link /api/core/Injectable#providedIn providedIn}
|
|
* If this is not your intention, you can have a shared module (that will act as act as the "root"
|
|
* module) and create all downgraded modules using that module's injector:
|
|
*
|
|
* {@example upgrade/static/ts/lite-multi-shared/module.ts region="shared-root-module"}
|
|
*
|
|
* @publicApi
|
|
*
|
|
* @deprecated Passing `NgModuleFactory` as the `downgradeModule` function argument is deprecated,
|
|
* please pass an NgModule class reference instead.
|
|
*/
|
|
export function downgradeModule<T>(moduleOrBootstrapFn: NgModuleFactory<T>): string;
|
|
/**
|
|
* @description
|
|
*
|
|
* A helper function for creating an AngularJS module that can bootstrap an Angular module
|
|
* "on-demand" (possibly lazily) when a {@link downgradeComponent downgraded component} needs to be
|
|
* instantiated.
|
|
*
|
|
* *Part of the [upgrade/static](api?query=upgrade/static) library for hybrid upgrade apps that
|
|
* support AOT compilation.*
|
|
*
|
|
* It allows loading/bootstrapping the Angular part of a hybrid application lazily and not having to
|
|
* pay the cost up-front. For example, you can have an AngularJS application that uses Angular for
|
|
* specific routes and only instantiate the Angular modules if/when the user visits one of these
|
|
* routes.
|
|
*
|
|
* The Angular module will be bootstrapped once (when requested for the first time) and the same
|
|
* reference will be used from that point onwards.
|
|
*
|
|
* `downgradeModule()` requires either an `NgModuleFactory`, `NgModule` class or a function:
|
|
* - `NgModuleFactory`: If you pass an `NgModuleFactory`, it will be used to instantiate a module
|
|
* using `platformBrowser`'s {@link PlatformRef#bootstrapModuleFactory bootstrapModuleFactory()}.
|
|
* NOTE: this type of the argument is deprecated. Please either provide an `NgModule` class or a
|
|
* bootstrap function instead.
|
|
* - `NgModule` class: If you pass an NgModule class, it will be used to instantiate a module
|
|
* using `platformBrowser`'s {@link PlatformRef#bootstrapModule bootstrapModule()}.
|
|
* - `Function`: If you pass a function, it is expected to return a promise resolving to an
|
|
* `NgModuleRef`. The function is called with an array of extra {@link StaticProvider Providers}
|
|
* that are expected to be available from the returned `NgModuleRef`'s `Injector`.
|
|
*
|
|
* `downgradeModule()` returns the name of the created AngularJS wrapper module. You can use it to
|
|
* declare a dependency in your main AngularJS module.
|
|
*
|
|
* {@example upgrade/static/ts/lite/module.ts region="basic-how-to"}
|
|
*
|
|
* For more details on how to use `downgradeModule()` see
|
|
* [Upgrading for Performance](https://angular.io/guide/upgrade).
|
|
*
|
|
* @usageNotes
|
|
*
|
|
* Apart from `UpgradeModule`, you can use the rest of the `upgrade/static` helpers as usual to
|
|
* build a hybrid application. Note that the Angular pieces (e.g. downgraded services) will not be
|
|
* available until the downgraded module has been bootstrapped, i.e. by instantiating a downgraded
|
|
* component.
|
|
*
|
|
* <div class="docs-alert docs-alert-important">
|
|
*
|
|
* You cannot use `downgradeModule()` and `UpgradeModule` in the same hybrid application.<br />
|
|
* Use one or the other.
|
|
*
|
|
* </div>
|
|
*
|
|
* ### Differences with `UpgradeModule`
|
|
*
|
|
* Besides their different API, there are two important internal differences between
|
|
* `downgradeModule()` and `UpgradeModule` that affect the behavior of hybrid applications:
|
|
*
|
|
* 1. Unlike `UpgradeModule`, `downgradeModule()` does not bootstrap the main AngularJS module
|
|
* inside the {@link NgZone Angular zone}.
|
|
* 2. Unlike `UpgradeModule`, `downgradeModule()` does not automatically run a
|
|
* [$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest) when changes are
|
|
* detected in the Angular part of the application.
|
|
*
|
|
* What this means is that applications using `UpgradeModule` will run change detection more
|
|
* frequently in order to ensure that both frameworks are properly notified about possible changes.
|
|
* This will inevitably result in more change detection runs than necessary.
|
|
*
|
|
* `downgradeModule()`, on the other side, does not try to tie the two change detection systems as
|
|
* tightly, restricting the explicit change detection runs only to cases where it knows it is
|
|
* necessary (e.g. when the inputs of a downgraded component change). This improves performance,
|
|
* especially in change-detection-heavy applications, but leaves it up to the developer to manually
|
|
* notify each framework as needed.
|
|
*
|
|
* For a more detailed discussion of the differences and their implications, see
|
|
* [Upgrading for Performance](https://angular.io/guide/upgrade).
|
|
*
|
|
* <div class="docs-alert docs-alert-helpful">
|
|
*
|
|
* You can manually trigger a change detection run in AngularJS using
|
|
* [scope.$apply(...)](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply) or
|
|
* [$rootScope.$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest).
|
|
*
|
|
* You can manually trigger a change detection run in Angular using {@link NgZone#run
|
|
* ngZone.run(...)}.
|
|
*
|
|
* </div>
|
|
*
|
|
* ### Downgrading multiple modules
|
|
*
|
|
* It is possible to downgrade multiple modules and include them in an AngularJS application. In
|
|
* that case, each downgraded module will be bootstrapped when an associated downgraded component or
|
|
* injectable needs to be instantiated.
|
|
*
|
|
* Things to keep in mind, when downgrading multiple modules:
|
|
*
|
|
* - Each downgraded component/injectable needs to be explicitly associated with a downgraded
|
|
* module. See `downgradeComponent()` and `downgradeInjectable()` for more details.
|
|
*
|
|
* - If you want some injectables to be shared among all downgraded modules, you can provide them as
|
|
* `StaticProvider`s, when creating the `PlatformRef` (e.g. via `platformBrowser` or
|
|
* `platformBrowserDynamic`).
|
|
*
|
|
* - When using {@link PlatformRef#bootstrapmodule `bootstrapModule()`} or
|
|
* {@link PlatformRef#bootstrapmodulefactory `bootstrapModuleFactory()`} to bootstrap the
|
|
* downgraded modules, each one is considered a "root" module. As a consequence, a new instance
|
|
* will be created for every injectable provided in `"root"` (via
|
|
* {@link /api/core/Injectable#providedIn providedIn}
|
|
* If this is not your intention, you can have a shared module (that will act as act as the "root"
|
|
* module) and create all downgraded modules using that module's injector:
|
|
*
|
|
* {@example upgrade/static/ts/lite-multi-shared/module.ts region="shared-root-module"}
|
|
*
|
|
* @publicApi
|
|
*/
|
|
export function downgradeModule<T>(
|
|
moduleOrBootstrapFn:
|
|
| Type<T>
|
|
| NgModuleFactory<T>
|
|
| ((extraProviders: StaticProvider[]) => Promise<NgModuleRef<T>>),
|
|
): string {
|
|
const lazyModuleName = `${ɵconstants.UPGRADE_MODULE_NAME}.lazy${++moduleUid}`;
|
|
const lazyModuleRefKey = `${ɵconstants.LAZY_MODULE_REF}${lazyModuleName}`;
|
|
const lazyInjectorKey = `${ɵconstants.INJECTOR_KEY}${lazyModuleName}`;
|
|
|
|
let bootstrapFn: (extraProviders: StaticProvider[]) => Promise<NgModuleRef<T>>;
|
|
if (ɵutil.isNgModuleType(moduleOrBootstrapFn)) {
|
|
// NgModule class
|
|
bootstrapFn = (extraProviders: StaticProvider[]) =>
|
|
platformBrowser(extraProviders).bootstrapModule(moduleOrBootstrapFn, {
|
|
applicationProviders: [internalProvideZoneChangeDetection({})],
|
|
});
|
|
} else if (!ɵutil.isFunction(moduleOrBootstrapFn)) {
|
|
// NgModule factory
|
|
bootstrapFn = (extraProviders: StaticProvider[]) =>
|
|
platformBrowser(extraProviders).bootstrapModuleFactory(moduleOrBootstrapFn, {
|
|
applicationProviders: [internalProvideZoneChangeDetection({})],
|
|
});
|
|
} else {
|
|
// bootstrap function
|
|
bootstrapFn = moduleOrBootstrapFn;
|
|
}
|
|
|
|
let injector: Injector;
|
|
|
|
// Create an ng1 module to bootstrap.
|
|
ɵangular1
|
|
.module_(lazyModuleName, [])
|
|
.constant(ɵconstants.UPGRADE_APP_TYPE_KEY, ɵutil.UpgradeAppType.Lite)
|
|
.factory(ɵconstants.INJECTOR_KEY, [lazyInjectorKey, identity])
|
|
.factory(lazyInjectorKey, () => {
|
|
if (!injector) {
|
|
throw new Error(
|
|
'Trying to get the Angular injector before bootstrapping the corresponding ' +
|
|
'Angular module.',
|
|
);
|
|
}
|
|
return injector;
|
|
})
|
|
.factory(ɵconstants.LAZY_MODULE_REF, [lazyModuleRefKey, identity])
|
|
.factory(lazyModuleRefKey, [
|
|
ɵconstants.$INJECTOR,
|
|
($injector: ɵangular1.IInjectorService) => {
|
|
setTempInjectorRef($injector);
|
|
const result: ɵutil.LazyModuleRef = {
|
|
promise: bootstrapFn(angular1Providers).then((ref) => {
|
|
injector = result.injector = new NgAdapterInjector(ref.injector);
|
|
injector.get(ɵconstants.$INJECTOR);
|
|
|
|
// Destroy the AngularJS app once the Angular `PlatformRef` is destroyed.
|
|
// This does not happen in a typical SPA scenario, but it might be useful for
|
|
// other use-cases where disposing of an Angular/AngularJS app is necessary
|
|
// (such as Hot Module Replacement (HMR)).
|
|
// See https://github.com/angular/angular/issues/39935.
|
|
injector.get(PlatformRef).onDestroy(() => ɵutil.destroyApp($injector));
|
|
|
|
return injector;
|
|
}),
|
|
};
|
|
return result;
|
|
},
|
|
])
|
|
.config([
|
|
ɵconstants.$INJECTOR,
|
|
ɵconstants.$PROVIDE,
|
|
($injector: ɵangular1.IInjectorService, $provide: ɵangular1.IProvideService) => {
|
|
$provide.constant(
|
|
ɵconstants.DOWNGRADED_MODULE_COUNT_KEY,
|
|
ɵutil.getDowngradedModuleCount($injector) + 1,
|
|
);
|
|
},
|
|
]);
|
|
|
|
return lazyModuleName;
|
|
}
|
|
|
|
function identity<T = any>(x: T): T {
|
|
return x;
|
|
}
|