/
register.ts
113 lines (98 loc) · 4.09 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
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 @typescript-eslint/prefer-ts-expect-error
// @ts-ignore replace at build
const auto = autoUpdateMode === 'true'
// eslint-disable-next-line @typescript-eslint/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 registration: ServiceWorkerRegistration | 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) {
const { Workbox } = await import('workbox-window')
// __SW__, __SCOPE__ and __TYPE__ will be replaced by virtual module
wb = new Workbox('__SW__', { scope: '__SCOPE__', type: '__TYPE__' })
sendSkipWaitingMessage = async () => {
if (registration && registration.waiting) {
// 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()
}
}
wb.addEventListener('activated', (event) => {
// This will only controls the offline request.
// event.isUpdate will be true if another version of the service
// worker was controlling the page when this version was registered.
// When using multiple clients, if the client that fires the update is not the current one,
// workbox-window will fire this event with isUpdate=undefined and isExternal=true
// we only need to check this case and force reloading the page, otherwise use current logic
if (!event.isUpdate && event.isExternal)
window.location.reload()
else if (event.isUpdate)
auto && window.location.reload()
else if (!autoDestroy)
onOfflineReady?.()
})
if (!auto) {
const showSkipWaitingPrompt = () => {
// \`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?.()
}
// 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) => {
registration = r
if (onRegisteredSW)
onRegisteredSW('__SW__', r)
else
onRegistered?.(r)
}).catch((e) => {
onRegisterError?.(e)
})
}
}
registerPromise = register()
return updateServiceWorker
}