-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(gatsby-plugin-offline): Change navigation handler logic (#13502)
* Refactor offline plugin: check for resources rather than checking against a whitelist * Fix broken tests * Fix tests, for real (fix illegal invocations) * Upgrade CircleCI Chrome to 74 * Chrome 73 (74 isn't available yet) * Specify correct image version * Catch failed resource errors * Remove waitForAPIorTimeout in favor of smart wait skipping * Fix function name * Fix skipping wrong path * Add comment explaining navigation handler logic * Fix code style issues * Fix Markdown formatting * Improve Cypress logging; temporary logging for testing * Revert some temporary changes * Clean up * Clean up some more * Remove testing line * Undo React lifecycle function rename * bump timeout back to 10sec * Revert timeout to 5s and Chrome version * Bump timeout back to 10 seconds * fm,l
- Loading branch information
Showing
6 changed files
with
68 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,101 +1,46 @@ | ||
/* global importScripts, workbox, idbKeyval */ | ||
|
||
importScripts(`idb-keyval-iife.min.js`) | ||
const WHITELIST_KEY = `custom-navigation-whitelist` | ||
|
||
const navigationRoute = new workbox.routing.NavigationRoute(({ event }) => { | ||
const { pathname } = new URL(event.request.url) | ||
const { NavigationRoute } = workbox.routing | ||
|
||
return idbKeyval.get(WHITELIST_KEY).then((customWhitelist = []) => { | ||
// Respond with the offline shell if we match the custom whitelist | ||
if (customWhitelist.includes(pathname)) { | ||
const offlineShell = `%pathPrefix%/offline-plugin-app-shell-fallback/index.html` | ||
const cacheName = workbox.core.cacheNames.precache | ||
const navigationRoute = new NavigationRoute(async ({ event }) => { | ||
let { pathname } = new URL(event.request.url) | ||
pathname = pathname.replace(new RegExp(`^%pathPrefix%`), ``) | ||
|
||
return caches.match(offlineShell, { cacheName }).then(cachedResponse => { | ||
if (cachedResponse) return cachedResponse | ||
|
||
console.error( | ||
`The offline shell (${offlineShell}) was not found ` + | ||
`while attempting to serve a response for ${pathname}` | ||
) | ||
// Check for resources + the app bundle | ||
// The latter may not exist if the SW is updating to a new version | ||
const resources = await idbKeyval.get(`resources:${pathname}`) | ||
if (!resources || !(await caches.match(`%pathPrefix%/%appFile%`))) { | ||
return await fetch(event.request) | ||
} | ||
|
||
return fetch(offlineShell).then(response => { | ||
if (response.ok) { | ||
return caches.open(cacheName).then(cache => | ||
// Clone is needed because put() consumes the response body. | ||
cache.put(offlineShell, response.clone()).then(() => response) | ||
) | ||
} else { | ||
return fetch(event.request) | ||
} | ||
}) | ||
}) | ||
for (const resource of resources) { | ||
// As soon as we detect a failed resource, fetch the entire page from | ||
// network - that way we won't risk being in an inconsistent state with | ||
// some parts of the page failing. | ||
if (!(await caches.match(resource))) { | ||
return await fetch(event.request) | ||
} | ||
} | ||
|
||
return fetch(event.request) | ||
}) | ||
const offlineShell = `%pathPrefix%/offline-plugin-app-shell-fallback/index.html` | ||
return await caches.match(offlineShell) | ||
}) | ||
|
||
workbox.routing.registerRoute(navigationRoute) | ||
|
||
let updatingWhitelist = null | ||
|
||
function rawWhitelistPathnames(pathnames) { | ||
if (updatingWhitelist !== null) { | ||
// Prevent the whitelist from being updated twice at the same time | ||
return updatingWhitelist.then(() => rawWhitelistPathnames(pathnames)) | ||
} | ||
|
||
updatingWhitelist = idbKeyval | ||
.get(WHITELIST_KEY) | ||
.then((customWhitelist = []) => { | ||
pathnames.forEach(pathname => { | ||
if (!customWhitelist.includes(pathname)) customWhitelist.push(pathname) | ||
}) | ||
|
||
return idbKeyval.set(WHITELIST_KEY, customWhitelist) | ||
}) | ||
.then(() => { | ||
updatingWhitelist = null | ||
}) | ||
|
||
return updatingWhitelist | ||
} | ||
|
||
function rawResetWhitelist() { | ||
if (updatingWhitelist !== null) { | ||
return updatingWhitelist.then(() => rawResetWhitelist()) | ||
} | ||
|
||
updatingWhitelist = idbKeyval.set(WHITELIST_KEY, []).then(() => { | ||
updatingWhitelist = null | ||
}) | ||
|
||
return updatingWhitelist | ||
} | ||
|
||
const messageApi = { | ||
whitelistPathnames(event) { | ||
let { pathnames } = event.data | ||
|
||
pathnames = pathnames.map(({ pathname, includesPrefix }) => { | ||
if (!includesPrefix) { | ||
return `%pathPrefix%${pathname}` | ||
} else { | ||
return pathname | ||
} | ||
}) | ||
|
||
event.waitUntil(rawWhitelistPathnames(pathnames)) | ||
setPathResources(event, { path, resources }) { | ||
event.waitUntil(idbKeyval.set(`resources:${path}`, resources)) | ||
}, | ||
|
||
resetWhitelist(event) { | ||
event.waitUntil(rawResetWhitelist()) | ||
clearPathResources(event) { | ||
event.waitUntil(idbKeyval.clear()) | ||
}, | ||
} | ||
|
||
self.addEventListener(`message`, event => { | ||
const { gatsbyApi } = event.data | ||
if (gatsbyApi) messageApi[gatsbyApi](event) | ||
if (gatsbyApi) messageApi[gatsbyApi](event, event.data) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters