/
service-worker.js
159 lines (136 loc) · 4.72 KB
/
service-worker.js
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* eslint-env serviceworker */
import { version, files, build } from '$service-worker'
const CACHE_NAME = `cache${version}`
const OFFLINE_URL = '/offline'
const toCache = [
// `build` is an array of all the files generated by the bundler,
...build.filter((fileName) => {
// the build output includes all fonts that I import, including .woff files
// but it's fairly safe to say that anything that supports service workers
// also supports .woff2 webfonts
// so there's no need to cache the .woff files
if (fileName.endsWith('.woff')) {
return false
}
return true
}),
// `files` is an array of everything in the `static` directory
...files.filter((fileName) => {
if (
// there are too many of these and they're big, so they fill up the cache
fileName.startsWith('/images') ||
fileName.startsWith('/video') ||
// netlify does not allow access to these on a live site
// so they'll 404 and bust the service worker addAll()
fileName === '/_headers' ||
fileName === '/_redirects' ||
// no browser needs these:
fileName === '/googlee9efb83fdd31f6a4.html' ||
fileName === '/humans.txt' ||
fileName === '/robots.txt'
) {
return false
}
return true
}),
]
const cachedBuildAndStaticFiles = new Set(toCache)
self.addEventListener('install', (event) => {
event.waitUntil(
(async () => {
const cache = await caches.open(CACHE_NAME)
// adding static assets
await cache.addAll(toCache)
// Setting {cache: 'reload'} in the new request will ensure that the
// response isn't fulfilled from the HTTP cache; i.e., it will be from
// the network.
await cache.add(new Request(OFFLINE_URL, { cache: 'reload' }))
})()
)
self.skipWaiting()
})
self.addEventListener('activate', (event) => {
event.waitUntil(
(async () => {
const keys = await caches.keys()
// delete old caches
for (const key of keys) {
if (key !== CACHE_NAME) {
await caches.delete(key)
}
}
// Enable navigation preload if it's supported.
// See https://developers.google.com/web/updates/2017/02/navigation-preload
if ('navigationPreload' in self.registration) {
await self.registration.navigationPreload.enable()
}
})()
)
self.clients.claim()
})
self.addEventListener('fetch', (event) => {
const { request } = event
if (request.method !== 'GET' || request.headers.has('range')) {
return
}
const url = new URL(request.url)
const cached = caches.match(request)
// don't try to handle e.g. data: URIs
if (!url.protocol.startsWith('http')) {
return
}
// ignore dev server requests
if (url.origin !== self.origin) {
return
}
// always serve static files and bundler-generated assets from cache
if (cachedBuildAndStaticFiles.has(url.pathname)) {
event.respondWith(cached)
return
}
if (request.cache === 'only-if-cached') {
return
}
// for everything else, try the network first, falling back to
// cache if the user is offline. (If the pages never change, you
// might prefer a cache-first approach to a network-first one.)
event.respondWith(
(async () => {
try {
let networkResponse
// First, try to use the navigation preload response if it's supported
// this appears to be a Chrome-only thing if the service worker hasn't started up yet
// when the network request was initiated
// https://developers.google.com/web/updates/2017/02/navigation-preload
const preloadResponse = await event.preloadResponse
if (preloadResponse) {
// if it's there, we can use that response instead of hitting the network again
// (that'd be bad)
networkResponse = preloadResponse
} else {
// if there wasn't a preloadResponse, go to the network now
networkResponse = await fetch(request)
}
// if it's OK, add it to the cache
if (networkResponse.ok && networkResponse.type === 'basic') {
const clone = networkResponse.clone()
caches.open(CACHE_NAME).then((cache) => {
cache.put(request, clone)
})
}
return networkResponse
} catch (err) {
// fall back to the cache, which might have a response already
const maybeCachedResponse = await cached
if (maybeCachedResponse) {
return cached
}
// for navigation requests, if we're offline we can return the /offline page
if (request.mode === 'navigate') {
return caches.match(OFFLINE_URL)
}
// if you don't return anything, the browser will treat it as a regular error it seems
}
})()
)
})