From 62f4c5e47c8d6d4290353e35c844c2ec3cde2450 Mon Sep 17 00:00:00 2001 From: David Bailey <4248177+davidbailey00@users.noreply.github.com> Date: Tue, 20 Nov 2018 22:03:27 +0000 Subject: [PATCH] feat(gatsby-plugin-offline): replace no-cache detection with dynamic path whitelist (#9907) * Remove all no-cache code * Remove references to no-cache in offline plugin * Initial work on hybrid navigation handler * Refactor whitelist code to allow it to support onPostPrefetchPathname * Fix service worker detection * Fix IndexedDB race condition * Prevent race conditions + reset whitelist on SW update * Remove unnecessary API handler (onPostPrefetchPathname is called anyway) * Add debugging statements + fix some minor problems * Fix back/forward not working after 404 * Remove unneeded debugging statements * Bundle idb-keyval instead of using an external CDN * Update README * Backport fixes from #9907 * minor fixes for things I copy-pasted wrong * Refactor prefetching so we can detect success --- packages/gatsby-plugin-offline/README.md | 15 +--- packages/gatsby-plugin-offline/package.json | 1 + .../src/gatsby-browser.js | 49 ++++++++--- .../gatsby-plugin-offline/src/gatsby-node.js | 22 ++--- .../gatsby-plugin-offline/src/sw-append.js | 84 ++++++++++++++++++- packages/gatsby/cache-dir/ensure-resources.js | 15 ++-- .../gatsby/cache-dir/load-directly-or-404.js | 72 ---------------- packages/gatsby/cache-dir/loader.js | 49 +++++------ packages/gatsby/cache-dir/navigation.js | 21 ++--- packages/gatsby/cache-dir/prefetch.js | 67 ++++++++++----- packages/gatsby/cache-dir/production-app.js | 82 ++++++++---------- yarn.lock | 5 ++ 12 files changed, 260 insertions(+), 222 deletions(-) delete mode 100644 packages/gatsby/cache-dir/load-directly-or-404.js diff --git a/packages/gatsby-plugin-offline/README.md b/packages/gatsby-plugin-offline/README.md index 809cc3b41db64..d35ee7630cc4f 100644 --- a/packages/gatsby-plugin-offline/README.md +++ b/packages/gatsby-plugin-offline/README.md @@ -24,8 +24,8 @@ plugins: [`gatsby-plugin-offline`] When adding this plugin to your `gatsby-config.js`, you can pass in options to override the default [Workbox](https://developers.google.com/web/tools/workbox/modules/workbox-build) config. -The default config is as follows. Warning, you can break the offline support -and AppCache setup by changing these options so tread carefully. +The default config is as follows. Warning: you can break the offline support by +changing these options, so tread carefully. ```javascript const options = { @@ -37,17 +37,6 @@ const options = { // the default prefix with `pathPrefix`. "/": `${pathPrefix}/`, }, - navigateFallback: `${pathPrefix}/offline-plugin-app-shell-fallback/index.html`, - // Only match URLs without extensions or the query `no-cache=1`. - // So example.com/about/ will pass but - // example.com/about/?no-cache=1 and - // example.com/cheeseburger.jpg will not. - // We only want the service worker to handle our "clean" - // URLs and not any files hosted on the site. - // - // Regex based on http://stackoverflow.com/a/18017805 - navigateFallbackWhitelist: [/^([^.?]*|[^?]*\.([^.?]{5,}|html))(\?.*)?$/], - navigateFallbackBlacklist: [/\?(.+&)?no-cache=1$/], cacheId: `gatsby-plugin-offline`, // Don't cache-bust JS or CSS files, and anything in the static directory, // since these files have unique URLs and their contents will never change diff --git a/packages/gatsby-plugin-offline/package.json b/packages/gatsby-plugin-offline/package.json index 6471db1d46af5..9e23bf885ea51 100644 --- a/packages/gatsby-plugin-offline/package.json +++ b/packages/gatsby-plugin-offline/package.json @@ -9,6 +9,7 @@ "dependencies": { "@babel/runtime": "^7.0.0", "cheerio": "^1.0.0-rc.2", + "idb-keyval": "^3.1.0", "lodash": "^4.17.10", "workbox-build": "^3.6.3" }, diff --git a/packages/gatsby-plugin-offline/src/gatsby-browser.js b/packages/gatsby-plugin-offline/src/gatsby-browser.js index 0f5749e483eb9..947a2bc2649e6 100644 --- a/packages/gatsby-plugin-offline/src/gatsby-browser.js +++ b/packages/gatsby-plugin-offline/src/gatsby-browser.js @@ -1,23 +1,12 @@ exports.registerServiceWorker = () => true -let swNotInstalled = true const prefetchedPathnames = [] - -exports.onPostPrefetchPathname = ({ pathname }) => { - // if SW is not installed, we need to record any prefetches - // that happen so we can then add them to SW cache once installed - if (swNotInstalled && `serviceWorker` in navigator) { - prefetchedPathnames.push(pathname) - } -} +const whitelistedPathnames = [] exports.onServiceWorkerActive = ({ getResourceURLsForPathname, serviceWorker, }) => { - // stop recording prefetch events - swNotInstalled = false - // grab nodes from head of document const nodes = document.querySelectorAll(` head > script[src], @@ -51,4 +40,40 @@ exports.onServiceWorkerActive = ({ document.head.appendChild(link) }) + + serviceWorker.active.postMessage({ + gatsbyApi: `whitelistPathnames`, + pathnames: whitelistedPathnames, + }) +} + +function whitelistPathname(pathname, includesPrefix) { + if (`serviceWorker` in navigator) { + const { serviceWorker } = navigator + + if (serviceWorker.controller !== null) { + serviceWorker.controller.postMessage({ + gatsbyApi: `whitelistPathnames`, + pathnames: [{ pathname, includesPrefix }], + }) + } else { + whitelistedPathnames.push({ pathname, includesPrefix }) + } + } +} + +exports.onPostPrefetchPathname = ({ pathname }) => { + whitelistPathname(pathname, false) + + // if SW is not installed, we need to record any prefetches + // that happen so we can then add them to SW cache once installed + if ( + `serviceWorker` in navigator && + !( + navigator.serviceWorker.controller !== null && + navigator.serviceWorker.controller.state === `activated` + ) + ) { + prefetchedPathnames.push(pathname) + } } diff --git a/packages/gatsby-plugin-offline/src/gatsby-node.js b/packages/gatsby-plugin-offline/src/gatsby-node.js index 892e99eb953dc..c522b426b51f1 100644 --- a/packages/gatsby-plugin-offline/src/gatsby-node.js +++ b/packages/gatsby-plugin-offline/src/gatsby-node.js @@ -79,17 +79,6 @@ exports.onPostBuild = (args, pluginOptions) => { // the default prefix with `pathPrefix`. "/": `${pathPrefix}/`, }, - navigateFallback: `${pathPrefix}/offline-plugin-app-shell-fallback/index.html`, - // Only match URLs without extensions or the query `no-cache=1`. - // So example.com/about/ will pass but - // example.com/about/?no-cache=1 and - // example.com/cheeseburger.jpg will not. - // We only want the service worker to handle our "clean" - // URLs and not any files hosted on the site. - // - // Regex based on http://stackoverflow.com/a/18017805 - navigateFallbackWhitelist: [/^([^.?]*|[^?]*\.([^.?]{5,}|html))(\?.*)?$/], - navigateFallbackBlacklist: [/\?(.+&)?no-cache=1$/], cacheId: `gatsby-plugin-offline`, // Don't cache-bust JS or CSS files, and anything in the static directory, // since these files have unique URLs and their contents will never change @@ -122,15 +111,22 @@ exports.onPostBuild = (args, pluginOptions) => { delete pluginOptions.plugins const combinedOptions = _.defaults(pluginOptions, options) + const idbKeyvalFile = `idb-keyval-iife.min.js` + const idbKeyvalSource = require.resolve(`idb-keyval/dist/${idbKeyvalFile}`) + const idbKeyvalDest = `public/${idbKeyvalFile}` + fs.createReadStream(idbKeyvalSource).pipe(fs.createWriteStream(idbKeyvalDest)) + const swDest = `public/sw.js` return workboxBuild .generateSW({ swDest, ...combinedOptions }) .then(({ count, size, warnings }) => { if (warnings) warnings.forEach(warning => console.warn(warning)) - const swAppend = fs.readFileSync(`${__dirname}/sw-append.js`) - fs.appendFileSync(`public/sw.js`, swAppend) + const swAppend = fs + .readFileSync(`${__dirname}/sw-append.js`, `utf8`) + .replace(/%pathPrefix%/g, pathPrefix) + fs.appendFileSync(`public/sw.js`, swAppend) console.log( `Generated ${swDest}, which will precache ${count} files, totaling ${size} bytes.` ) diff --git a/packages/gatsby-plugin-offline/src/sw-append.js b/packages/gatsby-plugin-offline/src/sw-append.js index 172f1ae6a468c..0d6c4a191d467 100644 --- a/packages/gatsby-plugin-offline/src/sw-append.js +++ b/packages/gatsby-plugin-offline/src/sw-append.js @@ -1 +1,83 @@ -// noop +/* 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) + + 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 + + return caches.match(offlineShell, { cacheName }) + } + + return fetch(event.request) + }) +}) + +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)) + }, + + resetWhitelist(event) { + event.waitUntil(rawResetWhitelist()) + }, +} + +self.addEventListener(`message`, event => { + const { gatsbyApi } = event.data + if (gatsbyApi) messageApi[gatsbyApi](event) +}) diff --git a/packages/gatsby/cache-dir/ensure-resources.js b/packages/gatsby/cache-dir/ensure-resources.js index c94146cd181c0..c05a82673ae29 100644 --- a/packages/gatsby/cache-dir/ensure-resources.js +++ b/packages/gatsby/cache-dir/ensure-resources.js @@ -2,7 +2,6 @@ import React from "react" import PropTypes from "prop-types" import loader from "./loader" import shallowCompare from "shallow-compare" -import { getRedirectUrl } from "./load-directly-or-404" // Pass pathname in as prop. // component will try fetching resources. If they exist, @@ -94,15 +93,19 @@ class EnsureResources extends React.Component { } render() { + // This should only occur if the network is offline, or if the + // path is nonexistent and there's no custom 404 page. if ( process.env.NODE_ENV === `production` && !(this.state.pageResources && this.state.pageResources.json) ) { - // This should only occur if there's no custom 404 page - const url = getRedirectUrl(this.state.location.href) - if (url) { - window.location.replace(url) - } + // Do this, rather than simply `window.location.reload()`, so that + // pressing the back/forward buttons work - otherwise Reach Router will + // try to handle back/forward navigation, causing the URL to change but + // the page displayed to stay the same. + const originalUrl = new URL(location.href) + window.history.replaceState({}, `404`, `${location.pathname}?gatsby-404`) + window.location.replace(originalUrl) return null } diff --git a/packages/gatsby/cache-dir/load-directly-or-404.js b/packages/gatsby/cache-dir/load-directly-or-404.js deleted file mode 100644 index f232c3f55c114..0000000000000 --- a/packages/gatsby/cache-dir/load-directly-or-404.js +++ /dev/null @@ -1,72 +0,0 @@ -export function getRedirectUrl(path) { - const url = new URL(path, window.location.origin) - - // This should never happen, but check just in case - otherwise, there would - // be an infinite redirect loop - if (url.search.match(/\?(.*&)?no-cache=1(&|$)/)) { - console.error( - `Found no-cache=1 while attempting to load a page directly; ` + - `this is likely due to a bug in Gatsby, or a misconfiguration in your project.` - ) - return false - } - - // Append the appropriate query to the URL. - if (url.search) { - url.search += `&no-cache=1` - } else { - url.search = `?no-cache=1` - } - - return url -} - -/** - * When other parts of the code can't find resources for a page, they load the 404 page's - * resources (if it exists) and then pass them here. This module then does the following: - * 1. Checks if 404 pages resources exist. If not, just navigate directly to the desired URL - * to show whatever server 404 page exists. - * 2. Try fetching the desired page to see if it exists on the server but we - * were just prevented from seeing it due to loading the site from a SW. If this is the case, - * trigger a hard reload to grab that page from the server. - * 3. If the page doesn't exist, show the normal 404 page component. - * 4. If the fetch failed (generally meaning we're offline), then navigate anyways to show - * either the browser's offline page or whatever the server error is. - */ -export default function(resources, path, replaceOnSuccess = false) { - return new Promise((resolve, reject) => { - const url = getRedirectUrl(path) - if (!url) { - reject(url) - return - } - - // Always navigate directly if a custom 404 page doesn't exist. - if (!resources) { - window.location = url - } else { - // Now test if the page is available directly - fetch(url.href) - .then(response => { - if (response.status !== 404) { - // Redirect there if there isn't a 404. If a different HTTP - // error occurs, the appropriate error message will be - // displayed after loading the page directly. - if (replaceOnSuccess) { - window.location.replace(url) - } else { - window.location = url - } - } else { - // If a 404 occurs, show the custom 404 page. - resolve() - } - }) - .catch(() => { - // If an error occurs (usually when offline), navigate to the - // page anyway to show the browser's proper offline error page - window.location = url - }) - } - }) -} diff --git a/packages/gatsby/cache-dir/loader.js b/packages/gatsby/cache-dir/loader.js index 392d12e285477..23a3dde924a7e 100644 --- a/packages/gatsby/cache-dir/loader.js +++ b/packages/gatsby/cache-dir/loader.js @@ -1,6 +1,5 @@ import pageFinderFactory from "./find-page" import emitter from "./emitter" -import stripPrefix from "./strip-prefix" import prefetchHelper from "./prefetch" const preferDefault = m => (m && m.default) || m @@ -16,7 +15,6 @@ let fetchingPageResourceMapPromise = null let fetchedPageResourceMap = false let apiRunner const failedPaths = {} -const failedResources = {} const MAX_HISTORY = 5 const jsonPromiseStore = {} @@ -72,6 +70,7 @@ const fetchResource = resourceName => { if (req.status === 200) { resolve(JSON.parse(req.responseText)) } else { + delete jsonPromiseStore[resourceName] reject() } } @@ -90,7 +89,8 @@ const fetchResource = resourceName => { const fetchPromise = resourceFunction() let failed = false return fetchPromise - .catch(() => { + .catch(e => { + console.error(e) failed = true }) .then(component => { @@ -99,10 +99,6 @@ const fetchResource = resourceName => { succeeded: !failed, }) - if (!failedResources[resourceName]) { - failedResources[resourceName] = failed - } - fetchHistory = fetchHistory.slice(-MAX_HISTORY) resolve(component) @@ -112,10 +108,12 @@ const fetchResource = resourceName => { const prefetchResource = resourceName => { if (resourceName.slice(0, 12) === `component---`) { - createComponentUrls(resourceName).forEach(url => prefetchHelper(url)) + return Promise.all( + createComponentUrls(resourceName).map(url => prefetchHelper(url)) + ) } else { const url = createJsonURL(jsonDataPaths[resourceName]) - prefetchHelper(url) + return prefetchHelper(url) } } @@ -148,14 +146,14 @@ const handleResourceLoadError = (path, message) => { const onPrefetchPathname = pathname => { if (!prefetchTriggered[pathname]) { - apiRunner(`onPrefetchPathname`, { pathname: pathname }) + apiRunner(`onPrefetchPathname`, { pathname }) prefetchTriggered[pathname] = true } } const onPostPrefetchPathname = pathname => { if (!prefetchCompleted[pathname]) { - apiRunner(`onPostPrefetchPathname`, { pathname: pathname }) + apiRunner(`onPostPrefetchPathname`, { pathname }) prefetchCompleted[pathname] = true } } @@ -188,12 +186,10 @@ const queue = { // Hovering on a link is a very strong indication the user is going to // click on it soon so let's start prefetching resources for this // pathname. - hovering: rawPath => { - const path = stripPrefix(rawPath, __PATH_PREFIX__) + hovering: path => { queue.getResourcesForPathname(path) }, - enqueue: rawPath => { - const path = stripPrefix(rawPath, __PATH_PREFIX__) + enqueue: path => { if (!apiRunner) console.error(`Run setApiRunnerForLoader() before enqueing paths`) @@ -218,7 +214,7 @@ const queue = { ) { // If page wasn't found check and we didn't fetch resources map for // all pages, wait for fetch to complete and try find page again - return fetchPageResourceMap().then(() => queue.enqueue(rawPath)) + return fetchPageResourceMap().then(() => queue.enqueue(path)) } if (!page) { @@ -234,13 +230,15 @@ const queue = { // Prefetch resources. if (process.env.NODE_ENV === `production`) { - prefetchResource(page.jsonName) - prefetchResource(page.componentChunkName) + Promise.all([ + prefetchResource(page.jsonName), + prefetchResource(page.componentChunkName), + ]).then(() => { + // Tell plugins the path has been successfully prefetched + onPostPrefetchPathname(path) + }) } - // Tell plugins the path has been successfully prefetched - onPostPrefetchPathname(path) - return true }, @@ -350,6 +348,9 @@ const queue = { page, pageResources, }) + // Tell plugins the path has been successfully prefetched + onPostPrefetchPathname(path) + resolve(pageResources) }) } else { @@ -378,6 +379,9 @@ const queue = { pageResources, }) + // Tell plugins the path has been successfully prefetched + onPostPrefetchPathname(path) + if (doingInitialRender) { // We got all resources needed for first mount, // we can fetch resoures for all pages. @@ -385,9 +389,6 @@ const queue = { } }) } - - // Tell plugins the path has been successfully prefetched - onPostPrefetchPathname(path) }), } diff --git a/packages/gatsby/cache-dir/navigation.js b/packages/gatsby/cache-dir/navigation.js index 3eb6728c45298..1932dfc64d491 100644 --- a/packages/gatsby/cache-dir/navigation.js +++ b/packages/gatsby/cache-dir/navigation.js @@ -6,7 +6,6 @@ import { apiRunner } from "./api-runner-browser" import emitter from "./emitter" import { navigate as reachNavigate } from "@reach/router" import parsePath from "./parse-path" -import loadDirectlyOr404 from "./load-directly-or-404" // Convert to a map for faster lookup in maybeRedirect() const redirectMap = redirects.reduce((map, redirect) => { @@ -66,8 +65,12 @@ const navigate = (to, options = {}) => { pathname = parsePath(to).pathname } - // If we had a service worker update, no matter the path, reload window + // If we had a service worker update, no matter the path, reload window and + // reset the pathname whitelist if (window.GATSBY_SW_UPDATED) { + const { controller } = navigator.serviceWorker + controller.postMessage({ gatsbyApi: `resetWhitelist` }) + window.location = pathname return } @@ -82,18 +85,8 @@ const navigate = (to, options = {}) => { }, 1000) loader.getResourcesForPathname(pathname).then(pageResources => { - if ( - (!pageResources || pageResources.page.path === `/404.html`) && - process.env.NODE_ENV === `production` - ) { - clearTimeout(timeoutId) - loadDirectlyOr404(pageResources, to).then(() => - reachNavigate(to, options) - ) - } else { - reachNavigate(to, options) - clearTimeout(timeoutId) - } + reachNavigate(to, options) + clearTimeout(timeoutId) }) } diff --git a/packages/gatsby/cache-dir/prefetch.js b/packages/gatsby/cache-dir/prefetch.js index 82c119ccb5a43..1d6322d8aeeca 100644 --- a/packages/gatsby/cache-dir/prefetch.js +++ b/packages/gatsby/cache-dir/prefetch.js @@ -12,23 +12,44 @@ const support = function(feature) { } return false } + const linkPrefetchStrategy = function(url) { - if (typeof document === `undefined`) { - return - } - const link = document.createElement(`link`) - link.setAttribute(`rel`, `prefetch`) - link.setAttribute(`href`, url) - const parentElement = - document.getElementsByTagName(`head`)[0] || - document.getElementsByName(`script`)[0].parentNode - parentElement.appendChild(link) + return new Promise((resolve, reject) => { + if (typeof document === `undefined`) { + reject() + return + } + + const link = document.createElement(`link`) + link.setAttribute(`rel`, `prefetch`) + link.setAttribute(`href`, url) + + link.onload = resolve + link.onerror = reject + + const parentElement = + document.getElementsByTagName(`head`)[0] || + document.getElementsByName(`script`)[0].parentNode + parentElement.appendChild(link) + }) } + const xhrPrefetchStrategy = function(url) { - const req = new XMLHttpRequest() - req.open(`GET`, url, true) - req.withCredentials = true - req.send(null) + return new Promise((resolve, reject) => { + const req = new XMLHttpRequest() + req.open(`GET`, url, true) + req.withCredentials = true + + req.onload = () => { + if (req.status === 200) { + resolve() + } else { + reject() + } + } + + req.send(null) + }) } const supportedPrefetchStrategy = support(`prefetch`) @@ -38,11 +59,19 @@ const supportedPrefetchStrategy = support(`prefetch`) const preFetched = {} const prefetch = function(url) { - if (preFetched[url]) { - return - } - preFetched[url] = true - supportedPrefetchStrategy(url) + return new Promise(resolve => { + if (preFetched[url]) { + resolve() + return + } + + supportedPrefetchStrategy(url) + .then(() => { + resolve() + preFetched[url] = true + }) + .catch(() => {}) // 404s are logged to the console anyway + }) } export default prefetch diff --git a/packages/gatsby/cache-dir/production-app.js b/packages/gatsby/cache-dir/production-app.js index 0770164274baa..a3d51f0579a20 100644 --- a/packages/gatsby/cache-dir/production-app.js +++ b/packages/gatsby/cache-dir/production-app.js @@ -15,7 +15,6 @@ window.___emitter = emitter import PageRenderer from "./page-renderer" import asyncRequires from "./async-requires" import loader, { setApiRunnerForLoader } from "./loader" -import loadDirectlyOr404 from "./load-directly-or-404" import EnsureResources from "./ensure-resources" window.asyncRequires = asyncRequires @@ -85,56 +84,43 @@ apiRunnerAsync(`onClientEntry`).then(() => { ) } - loader - .getResourcesForPathname(browserLoc.pathname) - .then(resources => { - if (!resources || resources.page.path === `/404.html`) { - return loadDirectlyOr404( - resources, - browserLoc.pathname + browserLoc.search + browserLoc.hash, - true - ) - } - - return null - }) - .then(() => { - const Root = () => - createElement( - Router, - { - basepath: __PATH_PREFIX__, - }, - createElement(RouteHandler, { path: `/*` }) - ) + loader.getResourcesForPathname(browserLoc.pathname).then(() => { + const Root = () => + createElement( + Router, + { + basepath: __PATH_PREFIX__, + }, + createElement(RouteHandler, { path: `/*` }) + ) - const WrappedRoot = apiRunner( - `wrapRootElement`, - { element: }, - , - ({ result }) => { - return { element: result } - } - ).pop() + const WrappedRoot = apiRunner( + `wrapRootElement`, + { element: }, + , + ({ result }) => { + return { element: result } + } + ).pop() - let NewRoot = () => WrappedRoot + let NewRoot = () => WrappedRoot - const renderer = apiRunner( - `replaceHydrateFunction`, - undefined, - ReactDOM.hydrate - )[0] + const renderer = apiRunner( + `replaceHydrateFunction`, + undefined, + ReactDOM.hydrate + )[0] - domReady(() => { - renderer( - , - typeof window !== `undefined` - ? document.getElementById(`___gatsby`) - : void 0, - () => { - apiRunner(`onInitialClientRender`) - } - ) - }) + domReady(() => { + renderer( + , + typeof window !== `undefined` + ? document.getElementById(`___gatsby`) + : void 0, + () => { + apiRunner(`onInitialClientRender`) + } + ) }) + }) }) diff --git a/yarn.lock b/yarn.lock index ea7da8fc28962..1e54ed20973c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9862,6 +9862,11 @@ icss-utils@^2.1.0: dependencies: postcss "^6.0.1" +idb-keyval@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-3.1.0.tgz#cce9ed320734446784d52ed398c4b075a4273f51" + integrity sha512-iFwFN5n00KNNnVxlOOK280SJJfXWY7pbMUOQXdIXehvvc/mGCV/6T2Ae+Pk2KwAkkATDTwfMavOiDH5lrJKWXQ== + ieee754@^1.1.4: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"