diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/.prettierignore b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/.prettierignore
new file mode 100644
index 0000000000000..1d085cacc9f8e
--- /dev/null
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/.prettierignore
@@ -0,0 +1 @@
+**
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-default.input.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-default.input.js
index 8792ec86df8a3..ab65e47bd0177 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-default.input.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-default.input.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-import Gatsby from "gatsby"
+import Gatsby from "gatsby";
export const query = graphql`
query {
@@ -7,4 +7,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-default.output.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-default.output.js
index 324487b5c656e..46afcf9782382 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-default.output.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-default.output.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-import Gatsby, { graphql } from "gatsby"
+import Gatsby, { graphql } from "gatsby";
export const query = graphql`
query {
@@ -7,4 +7,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-named-exports.input.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-named-exports.input.js
index 51e9a74220f55..df7334580796d 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-named-exports.input.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-named-exports.input.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-import { Link } from "gatsby"
+import { Link } from "gatsby";
export const query = graphql`
query {
@@ -7,4 +7,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-named-exports.output.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-named-exports.output.js
index 5cc716b82df17..6f04ef3d88480 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-named-exports.output.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-named-exports.output.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-import { Link, graphql } from "gatsby"
+import { Link, graphql } from "gatsby";
export const query = graphql`
query {
@@ -7,4 +7,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-namespace.input.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-namespace.input.js
index 72eb7fab7f880..f3a2b6d645868 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-namespace.input.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-namespace.input.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-import * as Gatsby from "gatsby"
+import * as Gatsby from "gatsby";
export const query = graphql`
query {
@@ -7,4 +7,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-namespace.output.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-namespace.output.js
index 1fb5d7d35ed0b..4e24b425e7c70 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-namespace.output.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/import-namespace.output.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-import * as Gatsby from "gatsby"
+import * as Gatsby from "gatsby";
export const query = Gatsby.graphql`
query {
@@ -7,4 +7,4 @@ export const query = Gatsby.graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/no-import-esm.input.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/no-import-esm.input.js
index 1f891f9fe7460..edd1b8d82a8aa 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/no-import-esm.input.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/no-import-esm.input.js
@@ -6,4 +6,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/no-import-esm.output.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/no-import-esm.output.js
index 664d51a772833..93634daeec07c 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/no-import-esm.output.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/no-import-esm.output.js
@@ -1,6 +1,6 @@
/* eslint-disable */
// TODO: update codemod to make this test pass
-import { graphql } from "gatsby"
+import { graphql } from "gatsby";
export const query = graphql`
query {
@@ -8,4 +8,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-destructure.input.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-destructure.input.js
index e0762febfd0aa..4cf420b484cf1 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-destructure.input.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-destructure.input.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-const { Link } = require(`gatsby`)
+const { Link } = require(`gatsby`);
export const query = graphql`
query {
@@ -7,4 +7,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-destructure.output.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-destructure.output.js
index 9f1bd5af2ffb8..6913f5a518f1b 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-destructure.output.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-destructure.output.js
@@ -1,5 +1,8 @@
/* eslint-disable */
-const { Link, graphql } = require(`gatsby`)
+const {
+ Link,
+ graphql
+} = require(`gatsby`);
export const query = graphql`
query {
@@ -7,4 +10,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-namespace.input.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-namespace.input.js
index 73126d44a1a46..10e90e653ef5d 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-namespace.input.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-namespace.input.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-const Gatsby = require(`gatsby`)
+const Gatsby = require(`gatsby`);
export const query = graphql`
query {
@@ -7,4 +7,4 @@ export const query = graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-namespace.output.js b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-namespace.output.js
index ea2e3eafa45c5..7bdd0ff50de2a 100644
--- a/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-namespace.output.js
+++ b/packages/gatsby-codemods/src/transforms/__testfixtures__/global-graphql-calls/require-namespace.output.js
@@ -1,5 +1,5 @@
/* eslint-disable */
-const Gatsby = require(`gatsby`)
+const Gatsby = require(`gatsby`);
export const query = Gatsby.graphql`
query {
@@ -7,4 +7,4 @@ export const query = Gatsby.graphql`
prefix
}
}
-`
+`;
diff --git a/packages/gatsby-plugin-offline/README.md b/packages/gatsby-plugin-offline/README.md
index 0f6de83e533f9..cd11d5a6238d6 100644
--- a/packages/gatsby-plugin-offline/README.md
+++ b/packages/gatsby-plugin-offline/README.md
@@ -29,34 +29,39 @@ and AppCache setup by changing these options so tread carefully.
```javascript
const options = {
- staticFileGlobs: [
- `${rootDir}/**/*.{woff2}`,
- `${rootDir}/commons-*js`,
- `${rootDir}/app-*js`,
+ staticFileGlobs: files.concat([
`${rootDir}/index.html`,
`${rootDir}/manifest.json`,
`${rootDir}/manifest.webmanifest`,
`${rootDir}/offline-plugin-app-shell-fallback/index.html`,
- ],
+ ...criticalFilePaths,
+ ]),
stripPrefix: rootDir,
+ // If `pathPrefix` is configured by user, we should replace
+ // the `public` prefix with `pathPrefix`.
+ // See more at:
+ // https://github.com/GoogleChrome/sw-precache#replaceprefix-string
+ replacePrefix: args.pathPrefix || ``,
navigateFallback: `/offline-plugin-app-shell-fallback/index.html`,
- // Only match URLs without extensions.
+ // 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.
- navigateFallbackWhitelist: [/^.*(?!\.\w?$)/],
+ //
+ // Regex based on http://stackoverflow.com/a/18017805
+ navigateFallbackWhitelist: [/^.*([^.]{5}|.html)(?
+ return
}
}
diff --git a/packages/gatsby-plugin-offline/src/gatsby-browser.js b/packages/gatsby-plugin-offline/src/gatsby-browser.js
index d15533cbe6ca5..c4ab8148165a6 100644
--- a/packages/gatsby-plugin-offline/src/gatsby-browser.js
+++ b/packages/gatsby-plugin-offline/src/gatsby-browser.js
@@ -9,7 +9,7 @@ exports.onPrefetchPathname = ({ pathname, getResourcesForPathname }) => {
if (swNotInstalled && `serviceWorker` in navigator) {
pathnameResources.push(
new Promise(resolve => {
- getResourcesForPathname(pathname, resources => {
+ getResourcesForPathname(pathname).then(resources => {
resolve(resources)
})
})
@@ -22,24 +22,27 @@ exports.onServiceWorkerInstalled = () => {
swNotInstalled = false
// grab nodes from head of document
- const nodes = document.querySelectorAll(
- `head > script[src], head > link[as=script]`
- )
+ const nodes = document.querySelectorAll(`
+ head > script[src],
+ head > link[as=script],
+ head > link[rel=stylesheet],
+ head > style[data-href]
+ `)
- // get all script URLs
- const scripts = [].slice
+ // get all resource URLs
+ const resources = [].slice
.call(nodes)
- .map(node => (node.src ? node.src : node.href))
+ .map(node => node.src || node.href || node.getAttribute(`data-href`))
+
+ for (const resource of resources) {
+ fetch(resource)
+ }
// loop over all resources and fetch the page component and JSON
// thereby storing it in SW cache
Promise.all(pathnameResources).then(pageResources => {
- pageResources.forEach(pageResource => {
- const [script] = scripts.filter(s =>
- s.includes(pageResource.page.componentChunkName)
- )
- fetch(pageResource.page.jsonURL)
- fetch(script)
- })
+ for (const pageResource of pageResources) {
+ if (pageResource) fetch(pageResource.page.jsonURL)
+ }
})
}
diff --git a/packages/gatsby-plugin-offline/src/gatsby-node.js b/packages/gatsby-plugin-offline/src/gatsby-node.js
index 4f39f7297da5f..1a9c69903e5f5 100644
--- a/packages/gatsby-plugin-offline/src/gatsby-node.js
+++ b/packages/gatsby-plugin-offline/src/gatsby-node.js
@@ -3,6 +3,7 @@ const precache = require(`sw-precache`)
const path = require(`path`)
const slash = require(`slash`)
const _ = require(`lodash`)
+const replace = require(`replace-in-file`)
const getResourcesFromHTML = require(`./get-resources-from-html`)
@@ -46,8 +47,14 @@ exports.onPostBuild = (args, pluginOptions) => {
rootDir
)
- const criticalFilePaths = getResourcesFromHTML(
- `${process.cwd()}/${rootDir}/index.html`
+ const criticalFilePaths = _.uniq(
+ _.concat(
+ getResourcesFromHTML(`${process.cwd()}/${rootDir}/index.html`),
+ getResourcesFromHTML(`${process.cwd()}/${rootDir}/404.html`),
+ getResourcesFromHTML(
+ `${process.cwd()}/${rootDir}/offline-plugin-app-shell-fallback/index.html`
+ )
+ )
)
const options = {
@@ -65,21 +72,22 @@ exports.onPostBuild = (args, pluginOptions) => {
// https://github.com/GoogleChrome/sw-precache#replaceprefix-string
replacePrefix: args.pathPrefix || ``,
navigateFallback: `/offline-plugin-app-shell-fallback/index.html`,
- // Only match URLs without extensions.
+ // 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 from http://stackoverflow.com/a/18017805
- navigateFallbackWhitelist: [/^.*([^.]{5}|.html)$/],
+ // Regex based on http://stackoverflow.com/a/18017805
+ navigateFallbackWhitelist: [/^.*([^.]{5}|.html)(? {
const combinedOptions = _.defaults(pluginOptions, options)
- return precache.write(`public/sw.js`, combinedOptions)
+ return precache.write(`public/sw.js`, combinedOptions).then(() =>
+ // Patch sw.js to include search queries when matching URLs against navigateFallbackWhitelist
+ replace({
+ files: `public/sw.js`,
+ from: `path = (new URL(absoluteUrlString)).pathname`,
+ to: `url = new URL(absoluteUrlString), path = url.pathname + url.search`,
+ }).then(changes => {
+ // Check that the patch has been applied correctly
+ if (changes.length !== 1)
+ throw new Error(
+ `Patching sw.js failed - sw-precache has probably been modified upstream.\n` +
+ `Please report this issue at https://github.com/gatsbyjs/gatsby/issues`
+ )
+ })
+ )
}
diff --git a/packages/gatsby-plugin-offline/src/get-resources-from-html.js b/packages/gatsby-plugin-offline/src/get-resources-from-html.js
index aaa2d00b8f9b3..7c2037fb8c6bd 100644
--- a/packages/gatsby-plugin-offline/src/get-resources-from-html.js
+++ b/packages/gatsby-plugin-offline/src/get-resources-from-html.js
@@ -5,7 +5,20 @@ const _ = require(`lodash`)
module.exports = htmlPath => {
// load index.html to pull scripts/links necessary for proper offline reload
- const html = fs.readFileSync(path.resolve(htmlPath))
+ let html
+ try {
+ // load index.html to pull scripts/links necessary for proper offline reload
+ html = fs.readFileSync(path.resolve(htmlPath))
+ } catch (err) {
+ // ENOENT means the file doesn't exist, which is to be expected when trying
+ // to open 404.html if the user hasn't created a custom 404 page -- return
+ // an empty array.
+ if (err.code === `ENOENT`) {
+ return []
+ } else {
+ throw err
+ }
+ }
// party like it's 2006
const $ = cheerio.load(html)
@@ -13,22 +26,28 @@ module.exports = htmlPath => {
// holds any paths for scripts and links
const criticalFilePaths = []
- $(`script[src], link[as=script], link[as=font], link[as=fetch]`).each(
- (_, elem) => {
- const $elem = $(elem)
- const url = $elem.attr(`src`) || $elem.attr(`href`)
- const blackListRegex = /\.xml$/
-
- if (!blackListRegex.test(url)) {
- let path = url
- if (url.substr(0, 4) !== `http`) {
- path = `public${url}`
- }
+ $(`
+ script[src],
+ link[as=script],
+ link[as=font],
+ link[as=fetch],
+ link[rel=stylesheet],
+ style[data-href]
+ `).each((_, elem) => {
+ const $elem = $(elem)
+ const url =
+ $elem.attr(`src`) || $elem.attr(`href`) || $elem.attr(`data-href`)
+ const blackListRegex = /\.xml$/
- criticalFilePaths.push(path)
+ if (!blackListRegex.test(url)) {
+ let path = url
+ if (url.substr(0, 4) !== `http`) {
+ path = `public${url}`
}
+
+ criticalFilePaths.push(path)
}
- )
+ })
return _.uniq(criticalFilePaths)
}
diff --git a/packages/gatsby/cache-dir/load-directly-or-404.js b/packages/gatsby/cache-dir/load-directly-or-404.js
new file mode 100644
index 0000000000000..a368fcb0ea4f4
--- /dev/null
+++ b/packages/gatsby/cache-dir/load-directly-or-404.js
@@ -0,0 +1,48 @@
+/**
+ * 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) {
+ return new Promise(resolve => {
+ const url = new URL(window.location.origin + path)
+
+ // Append the appropriate query to the URL.
+ if (url.search) {
+ url.search += `&no-cache=1`
+ } else {
+ url.search = `?no-cache=1`
+ }
+
+ // 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.
+ window.location.replace(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 021836bc40c59..72c2d58984202 100644
--- a/packages/gatsby/cache-dir/loader.js
+++ b/packages/gatsby/cache-dir/loader.js
@@ -276,37 +276,6 @@ const queue = {
getPage: pathname => findPage(pathname),
- // If we're loading from a service worker (it's already activated on
- // this initial render) and we can't find a page, there's a good chance
- // we're on a new page that this (now old) service worker doesn't know
- // about so we'll unregister it and reload.
- checkIfDoingInitialRenderForSW: path => {
- if (
- inInitialRender &&
- navigator &&
- navigator.serviceWorker &&
- navigator.serviceWorker.controller &&
- navigator.serviceWorker.controller.state === `activated`
- ) {
- if (!findPage(path)) {
- navigator.serviceWorker
- .getRegistrations()
- .then(function(registrations) {
- // We would probably need this to
- // prevent unnecessary reloading of the page
- // while unregistering of ServiceWorker is not happening
- if (registrations.length) {
- for (let registration of registrations) {
- registration.unregister()
- }
-
- window.location.reload()
- }
- })
- }
- }
- },
-
getResourcesForPathnameSync: path => {
const page = findPage(path)
if (page) {
@@ -320,8 +289,6 @@ const queue = {
// if necessary and then the code/data bundles. Used for prefetching
// and getting resources for page changes.
getResourcesForPathname: path => {
- queue.checkIfDoingInitialRenderForSW(path)
-
return new Promise((resolve, reject) => {
const doingInitialRender = inInitialRender
inInitialRender = false
@@ -352,6 +319,12 @@ const queue = {
if (!page) {
console.log(`A page wasn't found for "${path}"`)
+
+ // Preload the custom 404 page when running `gatsby develop`
+ if (path !== `/404.html` && process.env.NODE_ENV !== `production`) {
+ queue.getResourcesForPathname(`/404.html`)
+ }
+
return resolve()
}
diff --git a/packages/gatsby/cache-dir/navigation.js b/packages/gatsby/cache-dir/navigation.js
index 8030e35fb29c6..9a64e1eeb5729 100644
--- a/packages/gatsby/cache-dir/navigation.js
+++ b/packages/gatsby/cache-dir/navigation.js
@@ -5,6 +5,7 @@ import emitter from "./emitter"
import { globalHistory } from "@reach/router/lib/history"
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) => {
@@ -72,10 +73,12 @@ const navigate = (to, options) => {
loader.getResourcesForPathname(pathname).then(pageResources => {
if (!pageResources && process.env.NODE_ENV === `production`) {
- loader.getResourcesForPathname(`/404.html`).then(() => {
+ loader.getResourcesForPathname(`/404.html`).then(resources => {
clearTimeout(timeoutId)
onPreRouteUpdate(window.location)
- reachNavigate(to, options).then(() => onRouteUpdate(window.location))
+ loadDirectlyOr404(resources, to).then(() =>
+ reachNavigate(to, options).then(() => onRouteUpdate(window.location))
+ )
})
} else {
onPreRouteUpdate(window.location)
diff --git a/packages/gatsby/cache-dir/page-renderer.js b/packages/gatsby/cache-dir/page-renderer.js
index 38742ae7566c2..7b10d736ce2b0 100644
--- a/packages/gatsby/cache-dir/page-renderer.js
+++ b/packages/gatsby/cache-dir/page-renderer.js
@@ -119,7 +119,23 @@ class PageRenderer extends React.Component {
}
render() {
- if (!this.state.pageResources) return null
+ if (
+ !(this.state.pageResources && this.state.pageResources.json) &&
+ process.env.NODE_ENV === `production`
+ ) {
+ // Try to load the page directly - this should result in a 404 or
+ // network offline error.
+
+ const url = new URL(window.location)
+ if (url.search) {
+ url.search += `&no-cache=1`
+ } else {
+ url.search = `?no-cache=1`
+ }
+ window.location.replace(url)
+
+ return null
+ }
const pathContext =
process.env.NODE_ENV !== `production`
diff --git a/packages/gatsby/cache-dir/production-app.js b/packages/gatsby/cache-dir/production-app.js
index 55de00e1d3839..1e31bf74d1b76 100644
--- a/packages/gatsby/cache-dir/production-app.js
+++ b/packages/gatsby/cache-dir/production-app.js
@@ -15,6 +15,7 @@ window.___emitter = emitter
import PageRenderer from "./page-renderer"
import asyncRequires from "./async-requires"
import loader from "./loader"
+import loadDirectlyOr404 from "./load-directly-or-404"
window.asyncRequires = asyncRequires
window.___emitter = emitter
@@ -83,7 +84,11 @@ apiRunnerAsync(`onClientEntry`).then(() => {
.getResourcesForPathname(window.location.pathname)
.then(() => {
if (!loader.getPage(window.location.pathname)) {
- return loader.getResourcesForPathname(`/404.html`)
+ return loader
+ .getResourcesForPathname(`/404.html`)
+ .then(resources =>
+ loadDirectlyOr404(resources, window.location.pathname)
+ )
}
})
.then(() => {
diff --git a/packages/gatsby/cache-dir/register-service-worker.js b/packages/gatsby/cache-dir/register-service-worker.js
index 391f944431d09..3592a490a9787 100644
--- a/packages/gatsby/cache-dir/register-service-worker.js
+++ b/packages/gatsby/cache-dir/register-service-worker.js
@@ -23,8 +23,14 @@ if (`serviceWorker` in navigator) {
// It's the perfect time to display a "Content is cached for offline use." message.
console.log(`Content is now available offline!`)
- // post to service worker that install is complete
- apiRunner(`onServiceWorkerInstalled`, { serviceWorker: reg })
+ // Post to service worker that install is complete.
+ // Delay to allow time for the event listener to be added --
+ // otherwise fetch is called too soon and resources aren't cached.
+ window.setTimeout(() => {
+ apiRunner(`onServiceWorkerInstalled`, {
+ serviceWorker: reg,
+ })
+ }, 100)
}
break
diff --git a/packages/gatsby/cache-dir/root.js b/packages/gatsby/cache-dir/root.js
index 2d72df68562c7..6790683cf6f32 100644
--- a/packages/gatsby/cache-dir/root.js
+++ b/packages/gatsby/cache-dir/root.js
@@ -54,9 +54,8 @@ class RouteHandler extends React.Component {
}
render() {
- const { location } = this.props
- const { pathname } = location
- const pageResources = loader.getResourcesForPathnameSync(pathname)
+ let { location } = this.props
+ const pageResources = loader.getResourcesForPathnameSync(location.pathname)
const isPage = !!(pageResources && pageResources.component)
let child
if (isPage) {
@@ -67,8 +66,17 @@ class RouteHandler extends React.Component {
pageResources={pageResources}
/>
)
+ } else if (loader.getPage(`/404.html`)) {
+ location.pathname = `/404.html`
+ child = (
+
+ )
} else {
- const dev404Page = pages.find(p => /^\/dev-404-page/.test(p.path))
+ const dev404Page = pages.find(p => /^\/dev-404-page\/$/.test(p.path))
child = createElement(
syncRequires.components[dev404Page.componentChunkName],
{
diff --git a/packages/gatsby/src/internal-plugins/dev-404-page/raw_dev-404-page.js b/packages/gatsby/src/internal-plugins/dev-404-page/raw_dev-404-page.js
index d2001ddc47325..827811f980c57 100644
--- a/packages/gatsby/src/internal-plugins/dev-404-page/raw_dev-404-page.js
+++ b/packages/gatsby/src/internal-plugins/dev-404-page/raw_dev-404-page.js
@@ -9,7 +9,9 @@ class Dev404Page extends React.Component {
}
render() {
const { pathname } = this.props.location
- const pages = this.props.pages.filter(p => !/^\/dev-404-page/.test(p.path))
+ const pages = this.props.pages.filter(
+ p => !/^\/dev-404-page\/$/.test(p.path)
+ )
let newFilePath
if (pathname === `/`) {
newFilePath = `src/pages/index.js`
diff --git a/yarn.lock b/yarn.lock
index 00de4bf06ee29..8b1d3cd4e0e29 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -13017,6 +13017,14 @@ replace-ext@1.0.0, replace-ext@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
+replace-in-file@^3.4.2:
+ version "3.4.2"
+ resolved "https://registry.yarnpkg.com/replace-in-file/-/replace-in-file-3.4.2.tgz#6d40f076ac86948e28efeb6fab73fbad5c0bfa2a"
+ dependencies:
+ chalk "^2.4.1"
+ glob "^7.1.2"
+ yargs "^12.0.1"
+
request-promise-core@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6"