diff --git a/e2e-tests/development-runtime/cypress/integration/ssr.js b/e2e-tests/development-runtime/cypress/integration/ssr.js
new file mode 100644
index 0000000000000..00dc2236c4fd4
--- /dev/null
+++ b/e2e-tests/development-runtime/cypress/integration/ssr.js
@@ -0,0 +1,108 @@
+const staticPath = `/ssr/static-path/`
+const paramPath = `/ssr/param-path/`
+const wildcardPath = `/ssr/wildcard-path/`
+
+describe(`Static path ('${staticPath}')`, () => {
+ it(`Direct visit no query params`, () => {
+ cy.visit(staticPath).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ })
+
+ it(`Direct visit with query params`, () => {
+ cy.visit(staticPath + `?foo=bar`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ })
+
+ it(`Client navigation to same path with different query params`, () => {
+ cy.visit(staticPath).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ cy.window()
+ .then(win => win.___navigate(staticPath + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ cy.window()
+ .then(win => win.___navigate(staticPath + `?foo=baz`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"baz"}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ cy.window()
+ .then(win => win.___navigate(staticPath))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ })
+})
+
+describe(`Param path ('${paramPath}:param')`, () => {
+ it(`Direct visit no query params`, () => {
+ cy.visit(paramPath + `foo/`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"param":"foo"}`)
+ })
+
+ it(`Direct visit with query params`, () => {
+ cy.visit(paramPath + `foo/` + `?foo=bar`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"param":"foo"}`)
+ })
+
+ it(`Client navigation to same param path with different query params and url params`, () => {
+ cy.visit(paramPath + `foo/`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"param":"foo"}`)
+ cy.window()
+ .then(win => win.___navigate(paramPath + `foo/` + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"param":"foo"}`)
+ cy.window()
+ .then(win => win.___navigate(paramPath + `baz/` + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"param":"baz"}`)
+ cy.window()
+ .then(win => win.___navigate(paramPath + `baz/`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"param":"baz"}`)
+ })
+})
+
+describe(`Wildcard path ('${wildcardPath}*')`, () => {
+ it(`Direct visit no query params`, () => {
+ cy.visit(wildcardPath + `foo/nested/`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"foo/nested"}`)
+ })
+
+ it(`Direct visit with query params`, () => {
+ cy.visit(wildcardPath + `foo/nested/` + `?foo=bar`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"foo/nested"}`)
+ })
+
+ it(`Client navigation to same param path with different query params and url params`, () => {
+ cy.visit(wildcardPath + `foo/`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"foo"}`)
+ cy.window()
+ .then(win => win.___navigate(wildcardPath + `foo/` + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"foo"}`)
+ cy.window()
+ .then(win => win.___navigate(wildcardPath + `baz/` + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"baz"}`)
+ cy.window()
+ .then(win => win.___navigate(wildcardPath + `baz/`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"baz"}`)
+ })
+})
diff --git a/e2e-tests/development-runtime/src/pages/ssr/param-path/[param].js b/e2e-tests/development-runtime/src/pages/ssr/param-path/[param].js
new file mode 100644
index 0000000000000..af860ab139565
--- /dev/null
+++ b/e2e-tests/development-runtime/src/pages/ssr/param-path/[param].js
@@ -0,0 +1,22 @@
+import React from "react"
+
+export default function Params({ serverData }) {
+ return (
+
+
Query
+
{JSON.stringify(serverData?.arg?.query)}
+
Params
+
{JSON.stringify(serverData?.arg?.params)}
+
Debug
+
{JSON.stringify({ serverData }, null, 2)}
+
+ )
+}
+
+export function getServerData(arg) {
+ return {
+ props: {
+ arg,
+ },
+ }
+}
diff --git a/e2e-tests/development-runtime/src/pages/ssr/static-path.js b/e2e-tests/development-runtime/src/pages/ssr/static-path.js
new file mode 100644
index 0000000000000..ed3265bef789f
--- /dev/null
+++ b/e2e-tests/development-runtime/src/pages/ssr/static-path.js
@@ -0,0 +1,22 @@
+import React from "react"
+
+export default function StaticPath({ serverData }) {
+ return (
+
+
Query
+
{JSON.stringify(serverData?.arg?.query)}
+
Params
+
{JSON.stringify(serverData?.arg?.params)}
+
Debug
+
{JSON.stringify({ serverData }, null, 2)}
+
+ )
+}
+
+export function getServerData(arg) {
+ return {
+ props: {
+ arg,
+ },
+ }
+}
diff --git a/e2e-tests/development-runtime/src/pages/ssr/wildcard-path/[...wildcard].js b/e2e-tests/development-runtime/src/pages/ssr/wildcard-path/[...wildcard].js
new file mode 100644
index 0000000000000..9af5b2c9e2f71
--- /dev/null
+++ b/e2e-tests/development-runtime/src/pages/ssr/wildcard-path/[...wildcard].js
@@ -0,0 +1,22 @@
+import React from "react"
+
+export default function Wildcard({ serverData }) {
+ return (
+
+
Query
+
{JSON.stringify(serverData?.arg?.query)}
+
Params
+
{JSON.stringify(serverData?.arg?.params)}
+
Debug
+
{JSON.stringify({ serverData }, null, 2)}
+
+ )
+}
+
+export function getServerData(arg) {
+ return {
+ props: {
+ arg,
+ },
+ }
+}
diff --git a/e2e-tests/production-runtime/cypress/integration/redirects.js b/e2e-tests/production-runtime/cypress/integration/redirects.js
index 49229e27fa8dc..82e83350723ca 100644
--- a/e2e-tests/production-runtime/cypress/integration/redirects.js
+++ b/e2e-tests/production-runtime/cypress/integration/redirects.js
@@ -75,7 +75,7 @@ describe(`Redirects`, () => {
failOnStatusCode: false,
}).waitForRouteChange()
- cy.location(`pathname`).should(`equal`, `/redirect-search-hash/`)
+ cy.location(`pathname`).should(`equal`, `/redirect-search-hash`)
cy.location(`hash`).should(`equal`, `#anchor`)
cy.location(`search`).should(`equal`, ``)
})
@@ -96,7 +96,7 @@ describe(`Redirects`, () => {
failOnStatusCode: false,
}).waitForRouteChange()
- cy.location(`pathname`).should(`equal`, `/redirect-search-hash/`)
+ cy.location(`pathname`).should(`equal`, `/redirect-search-hash`)
cy.location(`hash`).should(`equal`, ``)
cy.location(`search`).should(`equal`, `?query_param=hello`)
})
@@ -117,7 +117,7 @@ describe(`Redirects`, () => {
failOnStatusCode: false,
}).waitForRouteChange()
- cy.location(`pathname`).should(`equal`, `/redirect-search-hash/`)
+ cy.location(`pathname`).should(`equal`, `/redirect-search-hash`)
cy.location(`hash`).should(`equal`, `#anchor`)
cy.location(`search`).should(`equal`, `?query_param=hello`)
})
diff --git a/e2e-tests/production-runtime/cypress/integration/ssr.js b/e2e-tests/production-runtime/cypress/integration/ssr.js
new file mode 100644
index 0000000000000..00dc2236c4fd4
--- /dev/null
+++ b/e2e-tests/production-runtime/cypress/integration/ssr.js
@@ -0,0 +1,108 @@
+const staticPath = `/ssr/static-path/`
+const paramPath = `/ssr/param-path/`
+const wildcardPath = `/ssr/wildcard-path/`
+
+describe(`Static path ('${staticPath}')`, () => {
+ it(`Direct visit no query params`, () => {
+ cy.visit(staticPath).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ })
+
+ it(`Direct visit with query params`, () => {
+ cy.visit(staticPath + `?foo=bar`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ })
+
+ it(`Client navigation to same path with different query params`, () => {
+ cy.visit(staticPath).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ cy.window()
+ .then(win => win.___navigate(staticPath + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ cy.window()
+ .then(win => win.___navigate(staticPath + `?foo=baz`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"baz"}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ cy.window()
+ .then(win => win.___navigate(staticPath))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{}`)
+ })
+})
+
+describe(`Param path ('${paramPath}:param')`, () => {
+ it(`Direct visit no query params`, () => {
+ cy.visit(paramPath + `foo/`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"param":"foo"}`)
+ })
+
+ it(`Direct visit with query params`, () => {
+ cy.visit(paramPath + `foo/` + `?foo=bar`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"param":"foo"}`)
+ })
+
+ it(`Client navigation to same param path with different query params and url params`, () => {
+ cy.visit(paramPath + `foo/`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"param":"foo"}`)
+ cy.window()
+ .then(win => win.___navigate(paramPath + `foo/` + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"param":"foo"}`)
+ cy.window()
+ .then(win => win.___navigate(paramPath + `baz/` + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"param":"baz"}`)
+ cy.window()
+ .then(win => win.___navigate(paramPath + `baz/`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"param":"baz"}`)
+ })
+})
+
+describe(`Wildcard path ('${wildcardPath}*')`, () => {
+ it(`Direct visit no query params`, () => {
+ cy.visit(wildcardPath + `foo/nested/`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"foo/nested"}`)
+ })
+
+ it(`Direct visit with query params`, () => {
+ cy.visit(wildcardPath + `foo/nested/` + `?foo=bar`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"foo/nested"}`)
+ })
+
+ it(`Client navigation to same param path with different query params and url params`, () => {
+ cy.visit(wildcardPath + `foo/`).waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"foo"}`)
+ cy.window()
+ .then(win => win.___navigate(wildcardPath + `foo/` + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"foo"}`)
+ cy.window()
+ .then(win => win.___navigate(wildcardPath + `baz/` + `?foo=bar`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{"foo":"bar"}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"baz"}`)
+ cy.window()
+ .then(win => win.___navigate(wildcardPath + `baz/`))
+ .waitForRouteChange()
+ cy.getTestElement(`query`).contains(`{}`)
+ cy.getTestElement(`params`).contains(`{"wildcard":"baz"}`)
+ })
+})
diff --git a/e2e-tests/production-runtime/src/pages/ssr/param-path/[param].js b/e2e-tests/production-runtime/src/pages/ssr/param-path/[param].js
new file mode 100644
index 0000000000000..af860ab139565
--- /dev/null
+++ b/e2e-tests/production-runtime/src/pages/ssr/param-path/[param].js
@@ -0,0 +1,22 @@
+import React from "react"
+
+export default function Params({ serverData }) {
+ return (
+
+
Query
+
{JSON.stringify(serverData?.arg?.query)}
+
Params
+
{JSON.stringify(serverData?.arg?.params)}
+
Debug
+
{JSON.stringify({ serverData }, null, 2)}
+
+ )
+}
+
+export function getServerData(arg) {
+ return {
+ props: {
+ arg,
+ },
+ }
+}
diff --git a/e2e-tests/production-runtime/src/pages/ssr/static-path.js b/e2e-tests/production-runtime/src/pages/ssr/static-path.js
new file mode 100644
index 0000000000000..ed3265bef789f
--- /dev/null
+++ b/e2e-tests/production-runtime/src/pages/ssr/static-path.js
@@ -0,0 +1,22 @@
+import React from "react"
+
+export default function StaticPath({ serverData }) {
+ return (
+
+
Query
+
{JSON.stringify(serverData?.arg?.query)}
+
Params
+
{JSON.stringify(serverData?.arg?.params)}
+
Debug
+
{JSON.stringify({ serverData }, null, 2)}
+
+ )
+}
+
+export function getServerData(arg) {
+ return {
+ props: {
+ arg,
+ },
+ }
+}
diff --git a/e2e-tests/production-runtime/src/pages/ssr/wildcard-path/[...wildcard].js b/e2e-tests/production-runtime/src/pages/ssr/wildcard-path/[...wildcard].js
new file mode 100644
index 0000000000000..9af5b2c9e2f71
--- /dev/null
+++ b/e2e-tests/production-runtime/src/pages/ssr/wildcard-path/[...wildcard].js
@@ -0,0 +1,22 @@
+import React from "react"
+
+export default function Wildcard({ serverData }) {
+ return (
+
+
Query
+
{JSON.stringify(serverData?.arg?.query)}
+
Params
+
{JSON.stringify(serverData?.arg?.params)}
+
Debug
+
{JSON.stringify({ serverData }, null, 2)}
+
+ )
+}
+
+export function getServerData(arg) {
+ return {
+ props: {
+ arg,
+ },
+ }
+}
diff --git a/packages/gatsby-link/src/index.js b/packages/gatsby-link/src/index.js
index 47f6fc2e77767..c1abef043f2bb 100644
--- a/packages/gatsby-link/src/index.js
+++ b/packages/gatsby-link/src/index.js
@@ -117,17 +117,19 @@ class GatsbyLink extends React.Component {
}
_prefetch() {
- let currentPath = window.location.pathname
+ let currentPath = window.location.pathname + window.location.search
// reach router should have the correct state
if (this.props._location && this.props._location.pathname) {
- currentPath = this.props._location.pathname
+ currentPath = this.props._location.pathname + this.props._location.search
}
const rewrittenPath = rewriteLinkPath(this.props.to, currentPath)
- const newPathName = parsePath(rewrittenPath).pathname
+ const parsed = parsePath(rewrittenPath)
- // Prefech is used to speed up next navigations. When you use it on the current navigation,
+ const newPathName = parsed.pathname + parsed.search
+
+ // Prefetch is used to speed up next navigations. When you use it on the current navigation,
// there could be a race-condition where Chrome uses the stale data instead of waiting for the network to complete
if (currentPath !== newPathName) {
___loader.enqueue(newPathName)
@@ -224,7 +226,8 @@ class GatsbyLink extends React.Component {
if (onMouseEnter) {
onMouseEnter(e)
}
- ___loader.hovering(parsePath(prefixedTo).pathname)
+ const parsed = parsePath(prefixedTo)
+ ___loader.hovering(parsed.pathname + parsed.search)
}}
onClick={e => {
if (onClick) {
diff --git a/packages/gatsby/cache-dir/__tests__/ensure-resources.tsx b/packages/gatsby/cache-dir/__tests__/ensure-resources.tsx
index 8d5841c614fbd..472617ee2b983 100644
--- a/packages/gatsby/cache-dir/__tests__/ensure-resources.tsx
+++ b/packages/gatsby/cache-dir/__tests__/ensure-resources.tsx
@@ -22,6 +22,7 @@ describe(`EnsureResources`, () => {
it(`loads pages synchronously`, () => {
const location = {
pathname: `/`,
+ search: ``,
}
const { container } = render(
diff --git a/packages/gatsby/cache-dir/app.js b/packages/gatsby/cache-dir/app.js
index dfd7cae909877..45f439e57bfd7 100644
--- a/packages/gatsby/cache-dir/app.js
+++ b/packages/gatsby/cache-dir/app.js
@@ -174,7 +174,7 @@ apiRunnerAsync(`onClientEntry`).then(() => {
Promise.all([
loader.loadPage(`/dev-404-page/`),
loader.loadPage(`/404.html`),
- loader.loadPage(window.location.pathname),
+ loader.loadPage(window.location.pathname + window.location.search),
]).then(() => {
navigationInit()
diff --git a/packages/gatsby/cache-dir/ensure-resources.js b/packages/gatsby/cache-dir/ensure-resources.js
index 80bbb8d8dcdf9..b1880c92b643b 100644
--- a/packages/gatsby/cache-dir/ensure-resources.js
+++ b/packages/gatsby/cache-dir/ensure-resources.js
@@ -10,15 +10,20 @@ class EnsureResources extends React.Component {
location: { ...location },
pageResources:
pageResources ||
- loader.loadPageSync(location.pathname, { withErrorDetails: true }),
+ loader.loadPageSync(location.pathname + location.search, {
+ withErrorDetails: true,
+ }),
}
}
static getDerivedStateFromProps({ location }, prevState) {
if (prevState.location.href !== location.href) {
- const pageResources = loader.loadPageSync(location.pathname, {
- withErrorDetails: true,
- })
+ const pageResources = loader.loadPageSync(
+ location.pathname + location.search,
+ {
+ withErrorDetails: true,
+ }
+ )
return {
pageResources,
@@ -48,7 +53,9 @@ class EnsureResources extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// Always return false if we're missing resources.
if (!nextState.pageResources) {
- this.loadResources(nextProps.location.pathname)
+ this.loadResources(
+ nextProps.location.pathname + nextProps.location.search
+ )
return false
}
@@ -56,7 +63,9 @@ class EnsureResources extends React.Component {
process.env.BUILD_STAGE === `develop` &&
nextState.pageResources.stale
) {
- this.loadResources(nextProps.location.pathname)
+ this.loadResources(
+ nextProps.location.pathname + nextProps.location.search
+ )
return false
}
diff --git a/packages/gatsby/cache-dir/find-path.js b/packages/gatsby/cache-dir/find-path.js
index 969ce6a22765b..1b1d149d2051d 100644
--- a/packages/gatsby/cache-dir/find-path.js
+++ b/packages/gatsby/cache-dir/find-path.js
@@ -15,8 +15,6 @@ const trimPathname = rawPathname => {
)
// Remove any hashfragment
.split(`#`)[0]
- // Remove search query
- .split(`?`)[0]
return trimmedPathname
}
diff --git a/packages/gatsby/cache-dir/loader.js b/packages/gatsby/cache-dir/loader.js
index 3f08cabca0b56..161205943d08f 100644
--- a/packages/gatsby/cache-dir/loader.js
+++ b/packages/gatsby/cache-dir/loader.js
@@ -24,9 +24,12 @@ const stripSurroundingSlashes = s => {
return s
}
-const createPageDataUrl = path => {
+const createPageDataUrl = rawPath => {
+ const [path, maybeSearch] = rawPath.split(`?`)
const fixedPath = path === `/` ? `index` : stripSurroundingSlashes(path)
- return `${__PATH_PREFIX__}/page-data/${fixedPath}/page-data.json`
+ return `${__PATH_PREFIX__}/page-data/${fixedPath}/page-data.json${
+ maybeSearch ? `?${maybeSearch}` : ``
+ }`
}
function doFetch(url, method = `GET`) {
@@ -141,6 +144,11 @@ export class BaseLoader {
throw new Error(`not a valid pageData response`)
}
+ const maybeSearch = pagePath.split(`?`)[1]
+ if (maybeSearch && !jsonPayload.path.includes(maybeSearch)) {
+ jsonPayload.path += `?${maybeSearch}`
+ }
+
return Object.assign(loadObj, {
status: PageResourceStatus.Success,
payload: jsonPayload,
diff --git a/packages/gatsby/cache-dir/navigation.js b/packages/gatsby/cache-dir/navigation.js
index 6a7b1ae3f09c8..9bf8fbbb43679 100644
--- a/packages/gatsby/cache-dir/navigation.js
+++ b/packages/gatsby/cache-dir/navigation.js
@@ -85,7 +85,7 @@ const navigate = (to, options = {}) => {
})
}, 1000)
- loader.loadPage(pathname).then(pageResources => {
+ loader.loadPage(pathname + search).then(pageResources => {
// If no page resources, then refresh the page
// Do this, rather than simply `window.location.reload()`, so that
// pressing the back/forward buttons work - otherwise when pressing
@@ -102,10 +102,6 @@ const navigate = (to, options = {}) => {
// If the loaded page has a different compilation hash to the
// window, then a rebuild has occurred on the server. Reload.
if (process.env.NODE_ENV === `production` && pageResources) {
- // window.___webpackCompilationHash gets set in production-app.js after navigationInit() is called
- // So on a direct visit of a page with a browser redirect this check is truthy and thus the codepath is hit
- // While the resource actually exists, but only too late
- // TODO: This should probably be fixed by setting ___webpackCompilationHash before navigationInit() is called
if (
pageResources.page.webpackCompilationHash !==
window.___webpackCompilationHash
@@ -172,9 +168,6 @@ function init() {
window.___push = to => navigate(to, { replace: false })
window.___replace = to => navigate(to, { replace: true })
window.___navigate = (to, options) => navigate(to, options)
-
- // Check for initial page-load redirect
- maybeRedirect(window.location.pathname)
}
class RouteAnnouncer extends React.Component {
diff --git a/packages/gatsby/cache-dir/normalize-page-path.js b/packages/gatsby/cache-dir/normalize-page-path.js
index e3aa70b2f45c5..51dd49a6b82b9 100644
--- a/packages/gatsby/cache-dir/normalize-page-path.js
+++ b/packages/gatsby/cache-dir/normalize-page-path.js
@@ -1,12 +1,17 @@
-export default path => {
- if (path === undefined) {
- return path
+export default pathAndSearch => {
+ if (pathAndSearch === undefined) {
+ return pathAndSearch
}
+ let [path, search = ``] = pathAndSearch.split(`?`)
+ if (search) {
+ search = `?` + search
+ }
+
if (path === `/`) {
- return `/`
+ return `/` + search
}
if (path.charAt(path.length - 1) === `/`) {
- return path.slice(0, -1)
+ return path.slice(0, -1) + search
}
- return path
+ return path + search
}
diff --git a/packages/gatsby/cache-dir/production-app.js b/packages/gatsby/cache-dir/production-app.js
index 5633e5863a706..225297cf018a2 100644
--- a/packages/gatsby/cache-dir/production-app.js
+++ b/packages/gatsby/cache-dir/production-app.js
@@ -107,8 +107,10 @@ apiRunnerAsync(`onClientEntry`).then(() => {
pageResources.page.path === `/404.html`
? stripPrefix(location.pathname, __BASE_PATH__)
: encodeURI(
- pageResources.page.matchPath ||
+ (
+ pageResources.page.matchPath ||
pageResources.page.path
+ ).split(`?`)[0]
)
}
{...this.props}
@@ -128,15 +130,18 @@ apiRunnerAsync(`onClientEntry`).then(() => {
const { pagePath, location: browserLoc } = window
// Explicitly call navigate if the canonical path (window.pagePath)
- // is different to the browser path (window.location.pathname). But
- // only if NONE of the following conditions hold:
+ // is different to the browser path (window.location.pathname). SSR
+ // page paths might include search params, while SSG and DSG won't.
+ // If page path include search params we also compare query params.
+ // But only if NONE of the following conditions hold:
//
// - The url matches a client side route (page.matchPath)
// - it's a 404 page
// - it's the offline plugin shell (/offline-plugin-app-shell-fallback/)
if (
pagePath &&
- __BASE_PATH__ + pagePath !== browserLoc.pathname &&
+ __BASE_PATH__ + pagePath !==
+ browserLoc.pathname + (pagePath.includes(`?`) ? browserLoc.search : ``) &&
!(
loader.findMatchPath(stripPrefix(browserLoc.pathname, __BASE_PATH__)) ||
pagePath === `/404.html` ||
@@ -144,12 +149,12 @@ apiRunnerAsync(`onClientEntry`).then(() => {
pagePath.match(/^\/offline-plugin-app-shell-fallback\/?$/)
)
) {
- navigate(__BASE_PATH__ + pagePath + browserLoc.search + browserLoc.hash, {
+ navigate(__BASE_PATH__ + pagePath + browserLoc.hash, {
replace: true,
})
}
- publicLoader.loadPage(browserLoc.pathname).then(page => {
+ publicLoader.loadPage(browserLoc.pathname + browserLoc.search).then(page => {
if (!page || page.status === PageResourceStatus.Error) {
const message = `page resources for ${browserLoc.pathname} not found. Not rendering React`
diff --git a/packages/gatsby/cache-dir/root.js b/packages/gatsby/cache-dir/root.js
index fc0b1625c3595..66c034d7641dd 100644
--- a/packages/gatsby/cache-dir/root.js
+++ b/packages/gatsby/cache-dir/root.js
@@ -32,7 +32,7 @@ class LocationHandler extends React.Component {
render() {
const { location } = this.props
- if (!loader.isPageNotFound(location.pathname)) {
+ if (!loader.isPageNotFound(location.pathname + location.search)) {
return (
{locationAndPageResources => (
@@ -48,8 +48,10 @@ class LocationHandler extends React.Component {
>
=> {
}
return res.send(results)
}
-
- return res.status(404).sendFile(`404.html`, { root })
}
return next()
})
diff --git a/packages/gatsby/src/utils/develop-preload-headers.ts b/packages/gatsby/src/utils/develop-preload-headers.ts
index 80d251d9e582c..20154270a8d9c 100644
--- a/packages/gatsby/src/utils/develop-preload-headers.ts
+++ b/packages/gatsby/src/utils/develop-preload-headers.ts
@@ -32,7 +32,7 @@ export async function appendPreloadHeaders(
// add page-data.json preload
// our runtime also demands 404 and dev-404 page-data to be fetched to even render (see cache-dir/app.js)
const pagePathsToPreload = [`/404.html`, `/dev-404-page/`]
- if (!pagePathsToPreload.includes(page.path)) {
+ if (page.mode !== `SSR` && !pagePathsToPreload.includes(page.path)) {
// let's make sure page path is first one (order shouldn't matter, just for reasonable order)
pagePathsToPreload.unshift(page.path)
}
diff --git a/packages/gatsby/src/utils/page-ssr-module/entry.ts b/packages/gatsby/src/utils/page-ssr-module/entry.ts
index 221244265a970..fad7870043674 100644
--- a/packages/gatsby/src/utils/page-ssr-module/entry.ts
+++ b/packages/gatsby/src/utils/page-ssr-module/entry.ts
@@ -29,6 +29,7 @@ export interface ISSRData {
templateDetails: ITemplateDetails
potentialPagePath: string
serverDataHeaders?: Record
+ searchString: string
}
const pageTemplateDetailsMap: Record<
@@ -101,15 +102,34 @@ export async function getData({
}
results.pageContext = page.context
+ let searchString = ``
+ if (req?.query) {
+ const maybeQueryString = Object.entries(req.query)
+ .map(([k, v]) => `${k}=${v}`)
+ .join(`&`)
+ if (maybeQueryString) {
+ searchString = `?${maybeQueryString}`
+ }
+ }
+
return {
results,
page,
templateDetails,
potentialPagePath,
serverDataHeaders: serverData?.headers,
+ searchString,
}
}
+function getPath(data: ISSRData): string {
+ return (
+ (data.page.mode !== `SSG` && data.page.matchPath
+ ? data.potentialPagePath
+ : data.page.path) + (data.page.mode === `SSR` ? data.searchString : ``)
+ )
+}
+
export async function renderPageData({
data,
}: {
@@ -118,10 +138,7 @@ export async function renderPageData({
const results = await constructPageDataString(
{
componentChunkName: data.page.componentChunkName,
- path:
- data.page.mode !== `SSG` && data.page.matchPath
- ? data.potentialPagePath
- : data.page.path,
+ path: getPath(data),
matchPath: data.page.matchPath,
staticQueryHashes: data.templateDetails.staticQueryHashes,
},
@@ -161,10 +178,7 @@ export async function renderHTML({
)
const results = await htmlComponentRenderer({
- pagePath:
- data.page.mode !== `SSG` && data.page.matchPath
- ? data.potentialPagePath
- : data.page.path,
+ pagePath: getPath(data),
pageData,
staticQueryContext,
...data.templateDetails.assets,