Skip to content

Commit d1f1ef1

Browse files
kkempleKyleAMathews
authored andcommitted
[offline-plugin] Add support for SW caching of prefetched resources (#6566)
* remove babel compiled file * refactor: change how we process prefetches after SW install, clean up code * fix: make sure we don't cache xml * Small copy tweak * refactor: minor cleanup * fix: pass sw instance to all sw events
1 parent 7510db1 commit d1f1ef1

File tree

8 files changed

+617
-428
lines changed

8 files changed

+617
-428
lines changed

packages/gatsby-plugin-offline/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
/gatsby-ssr.js
33
/gatsby-browser.js
44
/app-shell.js
5+
/prefetch-catcher.js

packages/gatsby-plugin-offline/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"dependencies": {
1010
"@babel/runtime": "7.0.0-beta.52",
1111
"cheerio": "^1.0.0-rc.2",
12-
"sw-precache": "^5.0.0"
12+
"sw-precache": "^5.2.1"
1313
},
1414
"devDependencies": {
1515
"@babel/cli": "7.0.0-beta.52",
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,45 @@
11
exports.registerServiceWorker = () => true
2+
3+
let swNotInstalled = true
4+
const pathnameResources = []
5+
6+
exports.onPrefetchPathname = ({ pathname, getResourcesForPathname }) => {
7+
// if SW is not installed, we need to record any prefetches
8+
// that happen so we can then add them to SW cache once installed
9+
if (swNotInstalled && `serviceWorker` in navigator) {
10+
pathnameResources.push(
11+
new Promise(resolve => {
12+
getResourcesForPathname(pathname, resources => {
13+
resolve(resources)
14+
})
15+
})
16+
)
17+
}
18+
}
19+
20+
exports.onServiceWorkerInstalled = () => {
21+
// stop recording prefetch events
22+
swNotInstalled = false
23+
24+
// grab nodes from head of document
25+
const nodes = document.querySelectorAll(
26+
`head > script[src], head > link[as=script]`
27+
)
28+
29+
// get all script URLs
30+
const scripts = [].slice
31+
.call(nodes)
32+
.map(node => (node.src ? node.src : node.href))
33+
34+
// loop over all resources and fetch the page component and JSON
35+
// thereby storing it in SW cache
36+
Promise.all(pathnameResources).then(pageResources => {
37+
pageResources.forEach(pageResource => {
38+
const [script] = scripts.filter(s =>
39+
s.includes(pageResource.page.componentChunkName)
40+
)
41+
fetch(pageResource.page.jsonURL)
42+
fetch(script)
43+
})
44+
})
45+
}

packages/gatsby-plugin-offline/src/gatsby-node.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,15 @@ exports.onPostBuild = (args, pluginOptions) => {
5656
// holds any paths for scripts and links
5757
const criticalFilePaths = []
5858

59-
$(`script`)
60-
.filter((_, elem) => $(elem).attr(`src`) !== undefined)
61-
.each((_, elem) => {
62-
criticalFilePaths.push(`${rootDir}${$(elem).attr(`src`)}`)
63-
})
59+
$(`script[src], link[as=script]`).each((_, elem) => {
60+
const $elem = $(elem)
61+
const url = $elem.attr(`src`) || $elem.attr(`href`)
62+
const blackListRegex = /\.xml$/
6463

65-
$(`link`)
66-
.filter((_, elem) => $(elem).attr(`as`) !== `script`)
67-
.each((_, elem) => {
68-
criticalFilePaths.push(`${rootDir}${$(elem).attr(`href`)}`)
69-
})
64+
if (!blackListRegex.test(url)) {
65+
criticalFilePaths.push(`${rootDir}${url}`)
66+
}
67+
})
7068

7169
const options = {
7270
staticFileGlobs: files.concat([

packages/gatsby/cache-dir/loader.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const fetchPageResourceMap = () => {
4141
return fetchingPageResourceMapPromise
4242
}
4343

44+
const createJsonURL = jsonName => `${__PATH_PREFIX__}/static/d/${jsonName}.json`
45+
4446
const fetchResource = resourceName => {
4547
// Find resource
4648
let resourceFunction
@@ -52,9 +54,7 @@ const fetchResource = resourceName => {
5254
} else {
5355
resourceFunction = () => {
5456
const fetchPromise = new Promise((resolve, reject) => {
55-
const url = `${__PATH_PREFIX__}/static/d/${
56-
jsonDataPaths[resourceName]
57-
}.json`
57+
const url = createJsonURL(jsonDataPaths[resourceName])
5858
var req = new XMLHttpRequest()
5959
req.open(`GET`, url, true)
6060
req.withCredentials = true
@@ -198,7 +198,10 @@ const queue = {
198198
// Tell plugins with custom prefetching logic that they should start
199199
// prefetching this path.
200200
if (!prefetchTriggered[path]) {
201-
apiRunner(`onPrefetchPathname`, { pathname: path })
201+
apiRunner(`onPrefetchPathname`, {
202+
pathname: path,
203+
getResourcesForPathname: queue.getResourcesForPathname,
204+
})
202205
prefetchTriggered[path] = true
203206
}
204207

@@ -355,7 +358,7 @@ const queue = {
355358
getResourceModule(page.jsonName),
356359
]).then(([component, json]) => {
357360
const pageResources = { component, json, page }
358-
361+
pageResources.page.jsonURL = createJsonURL(jsonDataPaths[page.jsonName])
359362
pathScriptsCache[path] = pageResources
360363
cb(pageResources)
361364

packages/gatsby/cache-dir/register-service-worker.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import emitter from "./emitter"
1+
import { apiRunner } from "./api-runner-browser"
22

33
if (`serviceWorker` in navigator) {
44
navigator.serviceWorker
55
.register(`${__PATH_PREFIX__}/sw.js`)
66
.then(function(reg) {
77
reg.addEventListener(`updatefound`, () => {
8+
apiRunner(`onServiceWorkerUpdateFound`, { serviceWorker: reg })
89
// The updatefound event implies that reg.installing is set; see
910
// https://w3c.github.io/ServiceWorker/#service-worker-registration-updatefound-event
1011
const installingWorker = reg.installing
@@ -21,12 +22,19 @@ if (`serviceWorker` in navigator) {
2122
// At this point, everything has been precached.
2223
// It's the perfect time to display a "Content is cached for offline use." message.
2324
console.log(`Content is now available offline!`)
24-
emitter.emit(`sw:installed`)
25+
26+
// post to service worker that install is complete
27+
apiRunner(`onServiceWorkerInstalled`, { serviceWorker: reg })
2528
}
2629
break
2730

2831
case `redundant`:
2932
console.error(`The installing service worker became redundant.`)
33+
apiRunner(`onServiceWorkerRedundant`, { serviceWorker: reg })
34+
break
35+
36+
case `active`:
37+
apiRunner(`onServiceWorkerActive`, { serviceWorker: reg })
3038
break
3139
}
3240
})

packages/gatsby/src/utils/api-browser-docs.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ exports.wrapRootComponent = true
104104
* for plugins with custom prefetching logic.
105105
* @param {object} $0
106106
* @param {object} $0.pathname The pathname whose resources should now be prefetched
107+
* @param {object} $0.getResourcesForPathname Function for fetching resources related to pathname
107108
*/
108109
exports.onPrefetchPathname = true
109110

@@ -126,3 +127,31 @@ exports.disableCorePrefetching = true
126127
* };
127128
*/
128129
exports.replaceHydrateFunction = true
130+
131+
/**
132+
* Inform plugins when a service worker has been installed.
133+
* @param {object} $0
134+
* @param {object} $0.serviceWorker The service worker instance.
135+
*/
136+
exports.onServiceWorkerInstalled = true
137+
138+
/**
139+
* Inform plugins of when a service worker has an update available.
140+
* @param {object} $0
141+
* @param {object} $0.serviceWorker The service worker instance.
142+
*/
143+
exports.onServiceWorkerUpdateFound = true
144+
145+
/**
146+
* Inform plugins when a service worker has become active.
147+
* @param {object} $0
148+
* @param {object} $0.serviceWorker The service worker instance.
149+
*/
150+
exports.onServiceWorkerActive = true
151+
152+
/**
153+
* Inform plugins when a service worker is redundant.
154+
* @param {object} $0
155+
* @param {object} $0.serviceWorker The service worker instance.
156+
*/
157+
exports.onServiceWorkerRedundant = true

0 commit comments

Comments
 (0)