/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/service-worker/src/provider.ts
255 строк
9 KB
Anuj Chhajed
refactor(core): correct all `typeof ngDevMode` comparison patterns introduced by #63875
08 дек 2025, 21:30
08 дек 2025, 21:30
96b79fc
Код
Авторство
О чём код?
/*! * @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 { ApplicationRef, EnvironmentProviders, inject, InjectionToken, Injector, makeEnvironmentProviders, NgZone, provideAppInitializer, ɵRuntimeError as RuntimeError, ɵformatRuntimeError as formatRuntimeError, } from '@angular/core'; import type {Observable} from 'rxjs'; import {NgswCommChannel} from './low_level'; import {SwPush} from './push'; import {SwUpdate} from './update'; import {RuntimeErrorCode} from './errors'; export const SCRIPT = new InjectionToken<string>( typeof ngDevMode !== 'undefined' && ngDevMode ? 'NGSW_REGISTER_SCRIPT' : '', ); export function ngswAppInitializer(): void { if (typeof ngServerMode !== 'undefined' && ngServerMode) { return; } const options = inject(SwRegistrationOptions); if (!('serviceWorker' in navigator && options.enabled !== false)) { return; } const script = inject(SCRIPT); const ngZone = inject(NgZone); const appRef = inject(ApplicationRef); // Set up the `controllerchange` event listener outside of // the Angular zone to avoid unnecessary change detections, // as this event has no impact on view updates. ngZone.runOutsideAngular(() => { // Wait for service worker controller changes, and fire an INITIALIZE action when a new SW // becomes active. This allows the SW to initialize itself even if there is no application // traffic. const sw = navigator.serviceWorker; const onControllerChange = () => sw.controller?.postMessage({action: 'INITIALIZE'}); sw.addEventListener('controllerchange', onControllerChange); appRef.onDestroy(() => { sw.removeEventListener('controllerchange', onControllerChange); }); }); // Run outside the Angular zone to avoid preventing the app from stabilizing (especially // given that some registration strategies wait for the app to stabilize). ngZone.runOutsideAngular(() => { let readyToRegister: Promise<void>; const {registrationStrategy} = options; if (typeof registrationStrategy === 'function') { readyToRegister = new Promise((resolve) => registrationStrategy().subscribe(() => resolve())); } else { const [strategy, ...args] = (registrationStrategy || 'registerWhenStable:30000').split(':'); switch (strategy) { case 'registerImmediately': readyToRegister = Promise.resolve(); break; case 'registerWithDelay': readyToRegister = delayWithTimeout(+args[0] || 0); break; case 'registerWhenStable': readyToRegister = Promise.race([appRef.whenStable(), delayWithTimeout(+args[0])]); break; default: // Unknown strategy. throw new RuntimeError( RuntimeErrorCode.UNKNOWN_REGISTRATION_STRATEGY, (typeof ngDevMode === 'undefined' || ngDevMode) && `Unknown ServiceWorker registration strategy: ${options.registrationStrategy}`, ); } } // Don't return anything to avoid blocking the application until the SW is registered. // Catch and log the error if SW registration fails to avoid uncaught rejection warning. readyToRegister.then(() => { // If the registration strategy has resolved after the application has // been explicitly destroyed by the user (e.g., by navigating away to // another application), we simply should not register the worker. if (appRef.destroyed) { return; } navigator.serviceWorker .register(script, { scope: options.scope, updateViaCache: options.updateViaCache, type: options.type, }) .catch((err) => console.error( formatRuntimeError( RuntimeErrorCode.SERVICE_WORKER_REGISTRATION_FAILED, (typeof ngDevMode === 'undefined' || ngDevMode) && 'Service worker registration failed with: ' + err, ), ), ); }); }); } function delayWithTimeout(timeout: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, timeout)); } export function ngswCommChannelFactory(): NgswCommChannel { const opts = inject(SwRegistrationOptions); const injector = inject(Injector); const isBrowser = !(typeof ngServerMode !== 'undefined' && ngServerMode); return new NgswCommChannel( isBrowser && opts.enabled !== false ? navigator.serviceWorker : undefined, injector, ); } /** * Token that can be used to provide options for `ServiceWorkerModule` outside of * `ServiceWorkerModule.register()`. * * You can use this token to define a provider that generates the registration options at runtime, * for example via a function call: * * {@example service-worker/registration-options/module.ts region="registration-options" * header="app.module.ts"} * * @see [Service worker configuration](ecosystem/service-workers/getting-started#service-worker-configuration) * * @publicApi */ export abstract class SwRegistrationOptions { /** * Whether the ServiceWorker will be registered and the related services (such as `SwPush` and * `SwUpdate`) will attempt to communicate and interact with it. * * Default: true */ enabled?: boolean; /** * The value of the setting used to determine the circumstances in which the browser * will consult the HTTP cache when it tries to update the service worker or any scripts that are imported via importScripts(). * [ServiceWorkerRegistration.updateViaCache](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/updateViaCache) */ updateViaCache?: ServiceWorkerUpdateViaCache; /** * The type of the ServiceWorker script to register. * [ServiceWorkerRegistration#type](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register#type) * - `classic`: Registers the script as a classic worker. ES module features such as `import` and `export` are NOT allowed in the script. * - `module`: Registers the script as an ES module. Allows use of `import`/`export` syntax and module features. * * @default 'classic' */ type?: WorkerType; /** * A URL that defines the ServiceWorker's registration scope; that is, what range of URLs it can * control. It will be used when calling * [ServiceWorkerContainer#register()](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register). */ scope?: string; /** * Defines the ServiceWorker registration strategy, which determines when it will be registered * with the browser. * * The default behavior of registering once the application stabilizes (i.e. as soon as there are * no pending micro- and macro-tasks) is designed to register the ServiceWorker as soon as * possible but without affecting the application's first time load. * * Still, there might be cases where you want more control over when the ServiceWorker is * registered (for example, there might be a long-running timeout or polling interval, preventing * the app from stabilizing). The available option are: * * - `registerWhenStable:<timeout>`: Register as soon as the application stabilizes (no pending * micro-/macro-tasks) but no later than `<timeout>` milliseconds. If the app hasn't * stabilized after `<timeout>` milliseconds (for example, due to a recurrent asynchronous * task), the ServiceWorker will be registered anyway. * If `<timeout>` is omitted, the ServiceWorker will only be registered once the app * stabilizes. * - `registerImmediately`: Register immediately. * - `registerWithDelay:<timeout>`: Register with a delay of `<timeout>` milliseconds. For * example, use `registerWithDelay:5000` to register the ServiceWorker after 5 seconds. If * `<timeout>` is omitted, is defaults to `0`, which will register the ServiceWorker as soon * as possible but still asynchronously, once all pending micro-tasks are completed. * - An Observable factory function: A function that returns an `Observable`. * The function will be used at runtime to obtain and subscribe to the `Observable` and the * ServiceWorker will be registered as soon as the first value is emitted. * * Default: 'registerWhenStable:30000' */ registrationStrategy?: string | (() => Observable<unknown>); } /** * @publicApi * * Sets up providers to register the given Angular Service Worker script. * * If `enabled` is set to `false` in the given options, the module will behave as if service * workers are not supported by the browser, and the service worker will not be registered. * * Example usage: * ```ts * bootstrapApplication(AppComponent, { * providers: [ * provideServiceWorker('ngsw-worker.js') * ], * }); * ``` * * @see [Custom service worker script](ecosystem/service-workers/custom-service-worker-scripts) * * @see [Service worker configuration](ecosystem/service-workers/getting-started#service-worker-configuration) * */ export function provideServiceWorker( script: string, options: SwRegistrationOptions = {}, ): EnvironmentProviders { return makeEnvironmentProviders([ SwPush, SwUpdate, {provide: SCRIPT, useValue: script}, {provide: SwRegistrationOptions, useValue: options}, { provide: NgswCommChannel, useFactory: ngswCommChannelFactory, }, provideAppInitializer(ngswAppInitializer), ]); }