7
7
*/
8
8
9
9
import {
10
- APP_INITIALIZER ,
11
10
ApplicationRef ,
12
11
EnvironmentProviders ,
12
+ inject ,
13
13
InjectionToken ,
14
14
Injector ,
15
15
makeEnvironmentProviders ,
16
16
NgZone ,
17
+ provideAppInitializer ,
17
18
} from '@angular/core' ;
18
- import { merge , from , Observable , of } from 'rxjs' ;
19
- import { delay , take } from 'rxjs/operators' ;
19
+ import type { Observable } from 'rxjs' ;
20
20
21
21
import { NgswCommChannel } from './low_level' ;
22
22
import { SwPush } from './push' ;
23
23
import { SwUpdate } from './update' ;
24
24
25
25
export const SCRIPT = new InjectionToken < string > ( ngDevMode ? 'NGSW_REGISTER_SCRIPT' : '' ) ;
26
26
27
- export function ngswAppInitializer (
28
- injector : Injector ,
29
- script : string ,
30
- options : SwRegistrationOptions ,
31
- ) : Function {
32
- return ( ) => {
33
- if ( typeof ngServerMode !== 'undefined' && ngServerMode ) {
34
- return ;
35
- }
27
+ export function ngswAppInitializer ( ) : void {
28
+ if ( typeof ngServerMode !== 'undefined' && ngServerMode ) {
29
+ return ;
30
+ }
36
31
37
- if ( ! ( 'serviceWorker' in navigator && options . enabled !== false ) ) {
38
- return ;
39
- }
32
+ const options = inject ( SwRegistrationOptions ) ;
40
33
41
- const ngZone = injector . get ( NgZone ) ;
42
- const appRef = injector . get ( ApplicationRef ) ;
34
+ if ( ! ( 'serviceWorker' in navigator && options . enabled !== false ) ) {
35
+ return ;
36
+ }
43
37
44
- // Set up the `controllerchange` event listener outside of
45
- // the Angular zone to avoid unnecessary change detections,
46
- // as this event has no impact on view updates.
47
- ngZone . runOutsideAngular ( ( ) => {
48
- // Wait for service worker controller changes, and fire an INITIALIZE action when a new SW
49
- // becomes active. This allows the SW to initialize itself even if there is no application
50
- // traffic.
51
- const sw = navigator . serviceWorker ;
52
- const onControllerChange = ( ) => sw . controller ?. postMessage ( { action : 'INITIALIZE' } ) ;
38
+ const script = inject ( SCRIPT ) ;
39
+ const ngZone = inject ( NgZone ) ;
40
+ const appRef = inject ( ApplicationRef ) ;
53
41
54
- sw . addEventListener ( 'controllerchange' , onControllerChange ) ;
42
+ // Set up the `controllerchange` event listener outside of
43
+ // the Angular zone to avoid unnecessary change detections,
44
+ // as this event has no impact on view updates.
45
+ ngZone . runOutsideAngular ( ( ) => {
46
+ // Wait for service worker controller changes, and fire an INITIALIZE action when a new SW
47
+ // becomes active. This allows the SW to initialize itself even if there is no application
48
+ // traffic.
49
+ const sw = navigator . serviceWorker ;
50
+ const onControllerChange = ( ) => sw . controller ?. postMessage ( { action : 'INITIALIZE' } ) ;
55
51
56
- appRef . onDestroy ( ( ) => {
57
- sw . removeEventListener ( 'controllerchange' , onControllerChange ) ;
58
- } ) ;
52
+ sw . addEventListener ( 'controllerchange' , onControllerChange ) ;
53
+
54
+ appRef . onDestroy ( ( ) => {
55
+ sw . removeEventListener ( 'controllerchange' , onControllerChange ) ;
59
56
} ) ;
57
+ } ) ;
60
58
61
- let readyToRegister$ : Observable < unknown > ;
59
+ // Run outside the Angular zone to avoid preventing the app from stabilizing (especially
60
+ // given that some registration strategies wait for the app to stabilize).
61
+ ngZone . runOutsideAngular ( ( ) => {
62
+ let readyToRegister : Promise < void > ;
62
63
63
- if ( typeof options . registrationStrategy === 'function' ) {
64
- readyToRegister$ = options . registrationStrategy ( ) ;
64
+ const { registrationStrategy} = options ;
65
+ if ( typeof registrationStrategy === 'function' ) {
66
+ readyToRegister = new Promise ( ( resolve ) => registrationStrategy ( ) . subscribe ( ( ) => resolve ( ) ) ) ;
65
67
} else {
66
- const [ strategy , ...args ] = (
67
- options . registrationStrategy || 'registerWhenStable:30000'
68
- ) . split ( ':' ) ;
68
+ const [ strategy , ...args ] = ( registrationStrategy || 'registerWhenStable:30000' ) . split ( ':' ) ;
69
69
70
70
switch ( strategy ) {
71
71
case 'registerImmediately' :
72
- readyToRegister$ = of ( null ) ;
72
+ readyToRegister = Promise . resolve ( ) ;
73
73
break ;
74
74
case 'registerWithDelay' :
75
- readyToRegister$ = delayWithTimeout ( + args [ 0 ] || 0 ) ;
75
+ readyToRegister = delayWithTimeout ( + args [ 0 ] || 0 ) ;
76
76
break ;
77
77
case 'registerWhenStable' :
78
- const whenStable$ = from ( injector . get ( ApplicationRef ) . whenStable ( ) ) ;
79
- readyToRegister$ = ! args [ 0 ]
80
- ? whenStable$
81
- : merge ( whenStable$ , delayWithTimeout ( + args [ 0 ] ) ) ;
78
+ readyToRegister = Promise . race ( [ appRef . whenStable ( ) , delayWithTimeout ( + args [ 0 ] ) ] ) ;
82
79
break ;
83
80
default :
84
81
// Unknown strategy.
@@ -89,30 +86,28 @@ export function ngswAppInitializer(
89
86
}
90
87
91
88
// Don't return anything to avoid blocking the application until the SW is registered.
92
- // Also, run outside the Angular zone to avoid preventing the app from stabilizing (especially
93
- // given that some registration strategies wait for the app to stabilize).
94
89
// Catch and log the error if SW registration fails to avoid uncaught rejection warning.
95
- ngZone . runOutsideAngular ( ( ) =>
96
- readyToRegister$
97
- . pipe ( take ( 1 ) )
98
- . subscribe ( ( ) =>
99
- navigator . serviceWorker
100
- . register ( script , { scope : options . scope } )
101
- . catch ( ( err ) => console . error ( 'Service worker registration failed with:' , err ) ) ,
102
- ) ,
90
+ readyToRegister . then ( ( ) =>
91
+ navigator . serviceWorker
92
+ . register ( script , { scope : options . scope } )
93
+ . catch ( ( err ) => console . error ( 'Service worker registration failed with:' , err ) ) ,
103
94
) ;
104
- } ;
95
+ } ) ;
105
96
}
106
97
107
- function delayWithTimeout ( timeout : number ) : Observable < unknown > {
108
- return of ( null ) . pipe ( delay ( timeout ) ) ;
98
+ function delayWithTimeout ( timeout : number ) : Promise < void > {
99
+ return new Promise ( ( resolve ) => setTimeout ( resolve , timeout ) ) ;
109
100
}
110
101
111
- export function ngswCommChannelFactory ( opts : SwRegistrationOptions ) : NgswCommChannel {
102
+ export function ngswCommChannelFactory (
103
+ opts : SwRegistrationOptions ,
104
+ injector : Injector ,
105
+ ) : NgswCommChannel {
112
106
const isBrowser = ! ( typeof ngServerMode !== 'undefined' && ngServerMode ) ;
113
107
114
108
return new NgswCommChannel (
115
109
isBrowser && opts . enabled !== false ? navigator . serviceWorker : undefined ,
110
+ injector ,
116
111
) ;
117
112
}
118
113
@@ -205,13 +200,8 @@ export function provideServiceWorker(
205
200
{
206
201
provide : NgswCommChannel ,
207
202
useFactory : ngswCommChannelFactory ,
208
- deps : [ SwRegistrationOptions ] ,
209
- } ,
210
- {
211
- provide : APP_INITIALIZER ,
212
- useFactory : ngswAppInitializer ,
213
- deps : [ Injector , SCRIPT , SwRegistrationOptions ] ,
214
- multi : true ,
203
+ deps : [ SwRegistrationOptions , Injector ] ,
215
204
} ,
205
+ provideAppInitializer ( ngswAppInitializer ) ,
216
206
] ) ;
217
207
}
0 commit comments