Skip to content

Commit 2aa8325

Browse files
authoredSep 30, 2022
feat(gatsby): Partial hydration router integration (#36616)
* feat(gatsby): Integrate reach router with RSC compat * Revert removal of RouteHandler * Bump reach router canary * Use ServerLocation, Router instead of hard coded location prop * Remove unnecessary Router element from tree * Fix key warning when node env is dev * Support pathPrefix feature * Update lockfile * Revert router version * Add v5 patch for router upgrade * Fix type imports * Add back webpack loader used in v4 with condition * Revert reach router utils imports to v1 * Update patch * Fix patch * Revert accidental file rename

File tree

10 files changed

+225
-34
lines changed

10 files changed

+225
-34
lines changed
 

‎packages/gatsby-link/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from "react"
2-
import { NavigateFn, LinkProps } from "@reach/router"
2+
import { NavigateFn, LinkProps } from "@reach/router" // These come from `@types/reach__router`
33

44
// eslint-disable-next-line @typescript-eslint/naming-convention
55
export interface GatsbyLinkProps<TState> extends LinkProps<TState> {

‎packages/gatsby/cache-dir/navigation.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import { maybeGetBrowserRedirect } from "./redirect-utils.js"
55
import { apiRunner } from "./api-runner-browser"
66
import emitter from "./emitter"
77
import { RouteAnnouncerProps } from "./route-announcer-props"
8-
import { navigate as reachNavigate } from "@gatsbyjs/reach-router"
9-
import { globalHistory } from "@gatsbyjs/reach-router/lib/history"
8+
import {
9+
navigate as reachNavigate,
10+
globalHistory,
11+
} from "@gatsbyjs/reach-router"
1012
import { parsePath } from "gatsby-link"
1113

1214
function maybeRedirect(pathname) {

‎packages/gatsby/cache-dir/ssr-develop-static-entry.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { grabMatchParams } from "./find-path"
99
import syncRequires from "$virtual/ssr-sync-requires"
1010

1111
import { RouteAnnouncerProps } from "./route-announcer-props"
12-
import { ServerLocation, Router, isRedirect } from "@reach/router"
12+
import { ServerLocation, Router, isRedirect } from "@gatsbyjs/reach-router"
1313
import { headHandlerForSSR } from "./head/head-export-handler-for-ssr"
1414
import { getStaticQueryResults } from "./loader"
1515

‎packages/gatsby/index.d.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from "react"
22
import { Renderer } from "react-dom"
33
import { EventEmitter } from "events"
4-
import { WindowLocation, NavigateFn, NavigateOptions } from "@reach/router"
4+
import { WindowLocation, NavigateFn, NavigateOptions } from "@reach/router" // These come from `@types/reach__router`
55
import { Reporter } from "gatsby-cli/lib/reporter/reporter"
66
import { Span } from "opentracing"
77
export { Reporter }
@@ -17,7 +17,10 @@ import { GraphQLOutputType } from "graphql"
1717
import { PluginOptionsSchemaJoi, ObjectSchema } from "gatsby-plugin-utils"
1818
import { IncomingMessage, ServerResponse } from "http"
1919

20-
export type AvailableFeatures = "image-cdn" | "graphql-typegen" | "content-file-path"
20+
export type AvailableFeatures =
21+
| "image-cdn"
22+
| "graphql-typegen"
23+
| "content-file-path"
2124

2225
export {
2326
default as Link,
@@ -158,7 +161,7 @@ export type HeadProps<DataType = object, PageContextType = object> = {
158161
/**
159162
* Returns the Location object's URL's path.
160163
*/
161-
pathname: string;
164+
pathname: string
162165
}
163166
/** The URL parameters when the page has a `matchPath` */
164167
params: Record<string, string>
@@ -175,7 +178,7 @@ export type HeadProps<DataType = object, PageContextType = object> = {
175178
/**
176179
* A shorthand type for combining the props and return type for the [Gatsby Head API](https://gatsby.dev/gatsby-head).
177180
*/
178-
export type HeadFC<DataType = object, PageContextType = object> = (
181+
export type HeadFC<DataType = object, PageContextType = object> = (
179182
props: HeadProps<DataType, PageContextType>
180183
) => JSX.Element
181184

‎packages/gatsby/src/commands/build-html.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { IProgram, Stage } from "./types"
1818
import { ROUTES_DIRECTORY } from "../constants"
1919
import { PackageJson } from "../.."
2020
import { IPageDataWithQueryResult } from "../utils/page-data"
21+
import { getPublicPath } from "../utils/get-public-path"
2122

2223
import type { GatsbyWorkerPool } from "../utils/worker/pool"
2324
type IActivity = any // TODO
@@ -449,7 +450,8 @@ const renderHTMLQueue = async (
449450
const renderPartialHydrationQueue = async (
450451
workerPool: GatsbyWorkerPool,
451452
activity: IActivity,
452-
pages: Array<string>
453+
pages: Array<string>,
454+
program: IProgram
453455
): Promise<void> => {
454456
// We need to only pass env vars that are set programmatically in gatsby-cli
455457
// to child process. Other vars will be picked up from environment.
@@ -461,7 +463,9 @@ const renderPartialHydrationQueue = async (
461463

462464
const segments = chunk(pages, 50)
463465
const sessionId = Date.now()
464-
// const { webpackCompilationHash } = store.getState()
466+
467+
const { config } = store.getState()
468+
const { assetPrefix, pathPrefix } = config
465469

466470
// Let the error bubble up
467471
await Promise.all(
@@ -470,6 +474,7 @@ const renderPartialHydrationQueue = async (
470474
envVars,
471475
paths: pageSegment,
472476
sessionId,
477+
pathPrefix: getPublicPath({ assetPrefix, pathPrefix, ...program }),
473478
})
474479

475480
if (activity && activity.tick) {
@@ -578,7 +583,7 @@ export const buildHTML = async ({
578583
process.env.GATSBY_PARTIAL_HYDRATION === `1`) &&
579584
_CFLAGS_.GATSBY_MAJOR === `5`
580585
) {
581-
await renderPartialHydrationQueue(workerPool, activity, pagePaths)
586+
await renderPartialHydrationQueue(workerPool, activity, pagePaths, program)
582587
}
583588
}
584589

@@ -668,7 +673,8 @@ export async function buildHTMLPagesAndDeleteStaleArtifacts({
668673
await renderPartialHydrationQueue(
669674
workerPool,
670675
buildHTMLActivityProgress,
671-
toRegenerate
676+
toRegenerate,
677+
program
672678
)
673679
} catch (error) {
674680
// Generic error with page path and useful stack trace, accurate code frame can be a future improvement

‎packages/gatsby/src/utils/get-server-data.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { Request } from "express"
22
import type { IGatsbyPage } from "../redux/types"
3-
43
import { match } from "@gatsbyjs/reach-router/lib/utils"
54

65
export interface IServerData {

‎packages/gatsby/src/utils/webpack.config.js

+16-14
Original file line numberDiff line numberDiff line change
@@ -379,12 +379,11 @@ module.exports = async (
379379
rules.images(),
380380
rules.media(),
381381
rules.miscAssets(),
382+
]
382383

383-
// This is a hack that exports one of @reach/router internals (BaseContext)
384-
// to export list. We need it to reset basepath and baseuri context after
385-
// Gatsby main router changes it, to keep v2 behaviour.
386-
// We will need to most likely remove this for v3.
387-
{
384+
// TODO(v5): Remove since this is only useful during Gatsby 4 publishes
385+
if (_CFLAGS_.GATSBY_MAJOR !== `5`) {
386+
configRules.push({
388387
test: require.resolve(`@gatsbyjs/reach-router/es/index`),
389388
type: `javascript/auto`,
390389
use: [
@@ -394,8 +393,8 @@ module.exports = async (
394393
),
395394
},
396395
],
397-
},
398-
]
396+
})
397+
}
399398

400399
// Speedup 🏎️💨 the build! We only include transpilation of node_modules on javascript production builds
401400
// TODO create gatsby plugin to enable this behaviour on develop (only when people are requesting this feature)
@@ -501,13 +500,16 @@ module.exports = async (
501500
],
502501
}
503502

504-
const target =
505-
stage === `build-html` || stage === `develop-html` ? `node` : `web`
506-
if (target === `web`) {
507-
resolve.alias[`@reach/router`] = path.join(
508-
getPackageRoot(`@gatsbyjs/reach-router`),
509-
`es`
510-
)
503+
// TODO(v5): Remove since this is only useful during Gatsby 4 publishes
504+
if (_CFLAGS_.GATSBY_MAJOR !== `5`) {
505+
const target =
506+
stage === `build-html` || stage === `develop-html` ? `node` : `web`
507+
if (target === `web`) {
508+
resolve.alias[`@reach/router`] = path.join(
509+
getPackageRoot(`@gatsbyjs/reach-router`),
510+
`es`
511+
)
512+
}
511513
}
512514

513515
if (stage === `build-javascript` && program.profile) {

‎packages/gatsby/src/utils/worker/child/render-html.ts

+26-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
IResourcesForTemplate,
2020
getStaticQueryContext,
2121
} from "../../static-query-utils"
22+
import { ServerLocation } from "@gatsbyjs/reach-router"
2223
// we want to force posix-style joins, so Windows doesn't produce backslashes for urls
2324
const { join } = path.posix
2425

@@ -283,10 +284,12 @@ export async function renderPartialHydrationProd({
283284
paths,
284285
envVars,
285286
sessionId,
287+
pathPrefix,
286288
}: {
287289
paths: Array<string>
288290
envVars: Array<[string, string | undefined]>
289291
sessionId: number
292+
pathPrefix
290293
}): Promise<void> {
291294
const publicDir = join(process.cwd(), `public`)
292295

@@ -340,18 +343,34 @@ export async function renderPartialHydrationProd({
340343

341344
const stream = fs.createWriteStream(outputPath)
342345

346+
const prefixedPagePath = pathPrefix
347+
? `${pathPrefix}${pageData.path}`
348+
: pageData.path
349+
const [pathname, search = ``] = prefixedPagePath.split(`?`)
350+
343351
const { pipe } = renderToPipeableStream(
344352
React.createElement(
345353
StaticQueryServerContext.Provider,
346354
{ value: staticQueryContext },
347355
[
348-
React.createElement(chunk.default, {
349-
data: pageData.result.data,
350-
pageContext: pageData.result.pageContext,
351-
location: {
352-
pathname: pageData.path,
353-
},
354-
}),
356+
// Make `useLocation` hook usuable in children
357+
React.createElement(
358+
ServerLocation,
359+
{ key: `partial-hydration-server-location`, url: pageData.path },
360+
[
361+
React.createElement(chunk.default, {
362+
key: `partial-hydration-page`,
363+
data: pageData.result.data,
364+
pageContext: pageData.result.pageContext,
365+
// Make location available to page as props, logic extracted from `LocationProvider`
366+
location: {
367+
pathname,
368+
search,
369+
hash: ``,
370+
},
371+
}),
372+
]
373+
),
355374
]
356375
),
357376
JSON.parse(

0 commit comments

Comments
 (0)
Please sign in to comment.