From ef6f099337ceaee465222ea7596c4a77dc20146d Mon Sep 17 00:00:00 2001 From: David Bailey <4248177+davidbailey00@users.noreply.github.com> Date: Thu, 19 Sep 2019 16:04:54 +0100 Subject: [PATCH] feat(gatsby-plugin-offline): "Magic" JS detection to make sites function correctly when JS is disabled retroactively (#17590) * Set path resources on route update (for initial page load) * begin "magic" * refactor + fix always re-enabling * implement SW API route + only set head components for offline shell + misc * update snapshots * change messageapi back to a "normal" object * add comment re: syntax * rename back to original casing * add back noscript warning in default-html * restore snapshots --- .../src/gatsby-browser.js | 9 +++++ .../gatsby-plugin-offline/src/gatsby-ssr.js | 19 +++++++++++ .../gatsby-plugin-offline/src/sw-append.js | 33 ++++++++++++++++--- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/gatsby-plugin-offline/src/gatsby-browser.js b/packages/gatsby-plugin-offline/src/gatsby-browser.js index 4d63601980ebb..d79dc2f85e262 100644 --- a/packages/gatsby-plugin-offline/src/gatsby-browser.js +++ b/packages/gatsby-plugin-offline/src/gatsby-browser.js @@ -81,6 +81,15 @@ function setPathResources(path, getResourceURLsForPathname) { exports.onRouteUpdate = ({ location, getResourceURLsForPathname }) => { const pathname = location.pathname.replace(__BASE_PATH__, ``) setPathResources(pathname, getResourceURLsForPathname) + + if ( + `serviceWorker` in navigator && + navigator.serviceWorker.controller !== null + ) { + navigator.serviceWorker.controller.postMessage({ + gatsbyApi: `enableOfflineShell`, + }) + } } exports.onPostPrefetchPathname = ({ pathname, getResourceURLsForPathname }) => { diff --git a/packages/gatsby-plugin-offline/src/gatsby-ssr.js b/packages/gatsby-plugin-offline/src/gatsby-ssr.js index 6812625cbfe89..10e97c5a28e1b 100644 --- a/packages/gatsby-plugin-offline/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-offline/src/gatsby-ssr.js @@ -1,3 +1,5 @@ +import React from "react" + export const onPreRenderHTML = ({ getHeadComponents, pathname, @@ -20,3 +22,20 @@ export const onPreRenderHTML = ({ replaceHeadComponents(filteredHeadComponents) } + +export const onRenderBody = ({ pathname, setHeadComponents }) => { + if (pathname !== `/offline-plugin-app-shell-fallback/`) { + return + } + + setHeadComponents([ + , + ]) +} diff --git a/packages/gatsby-plugin-offline/src/sw-append.js b/packages/gatsby-plugin-offline/src/sw-append.js index 8f4a25f649134..c6dc3865777e4 100644 --- a/packages/gatsby-plugin-offline/src/sw-append.js +++ b/packages/gatsby-plugin-offline/src/sw-append.js @@ -3,8 +3,13 @@ importScripts(`idb-keyval-iife.min.js`) const { NavigationRoute } = workbox.routing +let offlineShellEnabled = true const navigationRoute = new NavigationRoute(async ({ event }) => { + if (!offlineShellEnabled) { + return await fetch(event.request) + } + let { pathname } = new URL(event.request.url) pathname = pathname.replace(new RegExp(`^%pathPrefix%`), ``) @@ -31,17 +36,35 @@ const navigationRoute = new NavigationRoute(async ({ event }) => { workbox.routing.registerRoute(navigationRoute) -const messageApi = { - setPathResources(event, { path, resources }) { +// prefer standard object syntax to support more browsers +const MessageAPI = { + setPathResources: (event, { path, resources }) => { event.waitUntil(idbKeyval.set(`resources:${path}`, resources)) }, - clearPathResources(event) { + clearPathResources: event => { event.waitUntil(idbKeyval.clear()) }, + + enableOfflineShell: () => { + offlineShellEnabled = true + }, + + disableOfflineShell: () => { + offlineShellEnabled = false + }, } self.addEventListener(`message`, event => { - const { gatsbyApi } = event.data - if (gatsbyApi) messageApi[gatsbyApi](event, event.data) + const { gatsbyApi: api } = event.data + if (api) MessageAPI[api](event, event.data) +}) + +workbox.routing.registerRoute(/\/.gatsby-plugin-offline:.+/, ({ event }) => { + const { pathname } = new URL(event.request.url) + + const api = pathname.match(/:(.+)/)[1] + MessageAPI[api]() + + return new Response() })