/
register.ts
134 lines (120 loc) · 4.41 KB
/
register.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import type { RegisterSWOptions } from '../type'
// __SW_AUTO_UPDATE__ will be replaced by virtual module
const autoUpdateMode = '__SW_AUTO_UPDATE__'
// __SW_SELF_DESTROYING__ will be replaced by virtual module
const selfDestroying = '__SW_SELF_DESTROYING__'
// eslint-disable-next-line ts/prefer-ts-expect-error
// @ts-ignore replace at build
const auto = autoUpdateMode === 'true'
// eslint-disable-next-line ts/prefer-ts-expect-error
// @ts-ignore replace at build time
const autoDestroy = selfDestroying === 'true'
export type { RegisterSWOptions }
export function registerSW(options: RegisterSWOptions = {}) {
const {
immediate = false,
onNeedRefresh,
onOfflineReady,
onRegistered,
onRegisteredSW,
onRegisterError,
} = options
let wb: import('workbox-window').Workbox | undefined
let registerPromise: Promise<void>
let sendSkipWaitingMessage: () => Promise<void> | undefined
const updateServiceWorker = async (_reloadPage = true) => {
await registerPromise
if (!auto) {
await sendSkipWaitingMessage?.()
}
}
async function register() {
if ('serviceWorker' in navigator) {
wb = await import('workbox-window').then(({ Workbox }) => {
// __SW__, __SCOPE__ and __TYPE__ will be replaced by virtual module
return new Workbox('__SW__', { scope: '__SCOPE__', type: '__TYPE__' })
}).catch((e) => {
onRegisterError?.(e)
return undefined
})
if (!wb)
return
sendSkipWaitingMessage = async () => {
// Send a message to the waiting service worker,
// instructing it to activate.
// Note: for this to work, you have to add a message
// listener in your service worker. See below.
await wb?.messageSkipWaiting()
}
if (!autoDestroy) {
if (auto) {
wb.addEventListener('activated', (event) => {
if (event.isUpdate || event.isExternal)
window.location.reload()
})
wb.addEventListener('installed', (event) => {
if (!event.isUpdate) {
onOfflineReady?.()
}
});
}
else {
let onNeedRefreshCalled = false
const showSkipWaitingPrompt = () => {
onNeedRefreshCalled = true
// \`event.wasWaitingBeforeRegister\` will be false if this is
// the first time the updated service worker is waiting.
// When \`event.wasWaitingBeforeRegister\` is true, a previously
// updated service worker is still waiting.
// You may want to customize the UI prompt accordingly.
// Assumes your app has some sort of prompt UI element
// that a user can either accept or reject.
// Assuming the user accepted the update, set up a listener
// that will reload the page as soon as the previously waiting
// service worker has taken control.
wb?.addEventListener('controlling', (event) => {
if (event.isUpdate)
window.location.reload()
})
onNeedRefresh?.()
}
wb.addEventListener('installed', (event) => {
if (typeof event.isUpdate === 'undefined') {
if (typeof event.isExternal !== 'undefined') {
if (event.isExternal)
showSkipWaitingPrompt()
else
!onNeedRefreshCalled && onOfflineReady?.()
}
else {
if (event.isExternal)
window.location.reload()
else
!onNeedRefreshCalled && onOfflineReady?.()
}
}
else if (!event.isUpdate) {
onOfflineReady?.()
}
});
// Add an event listener to detect when the registered
// service worker has installed but is waiting to activate.
wb.addEventListener('waiting', showSkipWaitingPrompt)
// @ts-expect-error event listener provided by workbox-window
wb.addEventListener('externalwaiting', showSkipWaitingPrompt)
}
}
// register the service worker
wb.register({ immediate }).then((r) => {
if (onRegisteredSW)
onRegisteredSW('__SW__', r)
else
onRegistered?.(r)
}).catch((e) => {
onRegisterError?.(e)
})
}
}
registerPromise = register()
return updateServiceWorker
}