From 0c6126574d203c0e6fef173b76859cdcab2f13aa Mon Sep 17 00:00:00 2001 From: Ward Peeters Date: Tue, 15 Mar 2022 18:40:06 +0100 Subject: [PATCH] fix: compatibility with react rc 2 (#35108) Co-authored-by: Lennart --- packages/gatsby-link/package.json | 4 +- packages/gatsby-plugin-cxs/package.json | 4 +- packages/gatsby-plugin-feed/package.json | 4 +- packages/gatsby-plugin-fullstory/package.json | 4 +- .../package.json | 4 +- .../gatsby-plugin-google-gtag/package.json | 4 +- .../package.json | 4 +- packages/gatsby-plugin-image/package.json | 9 ++- .../__tests__/gatsby-image.browser.tsx | 8 +- .../src/components/__tests__/hooks.ts | 10 +-- .../src/components/gatsby-image.browser.tsx | 39 +++++++++- .../src/components/lazy-hydrate.tsx | 73 +++++++++++++------ .../gatsby-plugin-image/src/gatsby-node.ts | 4 + packages/gatsby-plugin-image/src/global.d.ts | 1 + packages/gatsby-plugin-jss/package.json | 4 +- packages/gatsby-plugin-mdx/package.json | 4 +- .../gatsby-plugin-netlify-cms/package.json | 4 +- packages/gatsby-plugin-offline/package.json | 4 +- packages/gatsby-plugin-sitemap/package.json | 4 +- .../package.json | 4 +- .../gatsby-plugin-typography/package.json | 4 +- .../gatsby-react-router-scroll/package.json | 4 +- .../package.json | 4 +- .../cache-dir/__tests__/static-entry.js | 1 + packages/gatsby/cache-dir/app.js | 41 +++++------ packages/gatsby/cache-dir/production-app.js | 19 +++-- packages/gatsby/cache-dir/static-entry.js | 5 +- packages/gatsby/package.json | 4 +- packages/gatsby/src/services/initialize.ts | 4 +- packages/gatsby/src/utils/webpack.config.js | 4 + yarn.lock | 41 +++++------ 31 files changed, 195 insertions(+), 132 deletions(-) diff --git a/packages/gatsby-link/package.json b/packages/gatsby-link/package.json index 1c7dfef036743..cd1e6bcaad57a 100644 --- a/packages/gatsby-link/package.json +++ b/packages/gatsby-link/package.json @@ -21,8 +21,8 @@ }, "peerDependencies": { "@gatsbyjs/reach-router": "^1.3.5", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "homepage": "https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-link#readme", "keywords": [ diff --git a/packages/gatsby-plugin-cxs/package.json b/packages/gatsby-plugin-cxs/package.json index 38554df13b2ba..97ecab9044b85 100644 --- a/packages/gatsby-plugin-cxs/package.json +++ b/packages/gatsby-plugin-cxs/package.json @@ -28,8 +28,8 @@ "peerDependencies": { "cxs": ">=5.0.0", "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-feed/package.json b/packages/gatsby-plugin-feed/package.json index 9fede561d5a8a..62d2bee5c7bc9 100644 --- a/packages/gatsby-plugin-feed/package.json +++ b/packages/gatsby-plugin-feed/package.json @@ -33,8 +33,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-fullstory/package.json b/packages/gatsby-plugin-fullstory/package.json index f5abf7862ba03..664b4c92ccfa7 100644 --- a/packages/gatsby-plugin-fullstory/package.json +++ b/packages/gatsby-plugin-fullstory/package.json @@ -34,8 +34,8 @@ }, "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "engines": { "node": ">=14.15.0" diff --git a/packages/gatsby-plugin-google-analytics/package.json b/packages/gatsby-plugin-google-analytics/package.json index f42f6ad24380a..efca76c13e3aa 100644 --- a/packages/gatsby-plugin-google-analytics/package.json +++ b/packages/gatsby-plugin-google-analytics/package.json @@ -28,8 +28,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-google-gtag/package.json b/packages/gatsby-plugin-google-gtag/package.json index e4ea57afd1eee..f63a1f0378aa5 100644 --- a/packages/gatsby-plugin-google-gtag/package.json +++ b/packages/gatsby-plugin-google-gtag/package.json @@ -27,8 +27,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-google-tagmanager/package.json b/packages/gatsby-plugin-google-tagmanager/package.json index ceeb9c09f1c25..31e66b2caf3ee 100644 --- a/packages/gatsby-plugin-google-tagmanager/package.json +++ b/packages/gatsby-plugin-google-tagmanager/package.json @@ -28,8 +28,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-image/package.json b/packages/gatsby-plugin-image/package.json index b9ef37403ec2f..fe5c1248dc103 100644 --- a/packages/gatsby-plugin-image/package.json +++ b/packages/gatsby-plugin-image/package.json @@ -49,8 +49,8 @@ "@types/fs-extra": "^9.0.13", "@types/node": "^14.10.2", "@types/prop-types": "^15.7.3", - "@types/react": "^16.9.56", - "@types/react-dom": "^16.9.8", + "@types/react": "^17.0.40", + "@types/react-dom": "^17.0.13", "ast-pretty-print": "^2.0.1", "babel-plugin-macros": "^2.8.0", "cross-env": "^7.0.3", @@ -60,6 +60,7 @@ "microbundle": "^0.13.0", "npm-run-all": "^4.1.5", "postcss": "^8.2.9", + "semver": "^7.0.0", "terser": "^5.3.8", "typescript": "^4.5.5" }, @@ -68,8 +69,8 @@ "gatsby": "^4.0.0-next", "gatsby-plugin-sharp": "^4.0.0-next", "gatsby-source-filesystem": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "dependencies": { "@babel/code-frame": "^7.14.0", diff --git a/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx b/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx index 54b76092333cf..ac74a05b8fe42 100644 --- a/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx +++ b/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx @@ -3,9 +3,9 @@ */ import React from "react" -import { GatsbyImage, IGatsbyImageData } from "../gatsby-image.browser" import { render, waitFor } from "@testing-library/react" import * as hooks from "../hooks" +import type { IGatsbyImageData } from "../gatsby-image.browser" // Prevents terser for bailing because we're not in a babel plugin jest.mock( @@ -20,15 +20,16 @@ jest.mock( describe(`GatsbyImage browser`, () => { let beforeHydrationContent: HTMLDivElement let image: IGatsbyImageData + let GatsbyImage beforeEach(() => { console.warn = jest.fn() console.error = jest.fn() global.SERVER = true global.GATSBY___IMAGE = true - }) + global.HAS_REACT_18 = false - beforeEach(() => { + GatsbyImage = require(`../gatsby-image.browser`).GatsbyImage image = { width: 100, height: 100, @@ -80,6 +81,7 @@ describe(`GatsbyImage browser`, () => { jest.clearAllMocks() global.SERVER = undefined global.GATSBY___IMAGE = undefined + global.HAS_REACT_18 = undefined process.env.NODE_ENV = `test` }) diff --git a/packages/gatsby-plugin-image/src/components/__tests__/hooks.ts b/packages/gatsby-plugin-image/src/components/__tests__/hooks.ts index 87d5d13433e86..83855288d16aa 100644 --- a/packages/gatsby-plugin-image/src/components/__tests__/hooks.ts +++ b/packages/gatsby-plugin-image/src/components/__tests__/hooks.ts @@ -1,12 +1,6 @@ import { Node } from "gatsby" -import { - getSrc, - getSrcSet, - getImage, - IGatsbyImageData, - IGetImageDataArgs, -} from "../../" -import { getImageData } from "../hooks" +import { getImageData, getSrc, getSrcSet, getImage } from "../hooks" +import type { IGatsbyImageData, IGetImageDataArgs } from "../../" const imageData: IGatsbyImageData = { images: { diff --git a/packages/gatsby-plugin-image/src/components/gatsby-image.browser.tsx b/packages/gatsby-plugin-image/src/components/gatsby-image.browser.tsx index 73a48941a22e8..625c39fd01c12 100644 --- a/packages/gatsby-plugin-image/src/components/gatsby-image.browser.tsx +++ b/packages/gatsby-plugin-image/src/components/gatsby-image.browser.tsx @@ -1,3 +1,4 @@ +/* global HAS_REACT_18 */ /* eslint-disable no-unused-expressions */ import React, { Component, @@ -22,7 +23,33 @@ import { Layout } from "../image-utils" import { getSizer } from "./layout-wrapper" import { propTypes } from "./gatsby-image.server" import { Unobserver } from "./intersection-observer" -import { render } from "react-dom" +import type { Root } from "react-dom/client" + +let reactRender +if (HAS_REACT_18) { + const reactDomClient = require(`react-dom/client`) + reactRender = ( + Component: React.ReactChild | Iterable, + el: ReactDOM.Container, + root: Root + ): Root => { + if (!root) { + root = reactDomClient.createRoot(el) + } + + root.render(Component) + + return root + } +} else { + const reactDomClient = require(`react-dom`) + reactRender = ( + Component: React.ReactChild | Iterable, + el: ReactDOM.Container + ): void => { + reactDomClient.render(Component, el) + } +} // eslint-disable-next-line @typescript-eslint/naming-convention export interface GatsbyImageProps @@ -69,6 +96,7 @@ class GatsbyImageHydrator extends Component< lazyHydrator: () => void | null = null ref = createRef() unobserveRef: Unobserver + reactRootRef: MutableRefObject = createRef() constructor(props) { super(props) @@ -108,7 +136,8 @@ class GatsbyImageHydrator extends Component< }, this.root, this.hydrated, - this.forceRender + this.forceRender, + this.reactRootRef ) }) } @@ -152,7 +181,11 @@ class GatsbyImageHydrator extends Component< // // on unmount, make sure we cleanup if (this.hydrated.current && this.lazyHydrator) { - render(null, this.root.current) + this.reactRootRef.current = reactRender( + null, + this.root.current, + this.reactRootRef.current + ) } } diff --git a/packages/gatsby-plugin-image/src/components/lazy-hydrate.tsx b/packages/gatsby-plugin-image/src/components/lazy-hydrate.tsx index 84c6051a93b0a..41caf32644481 100644 --- a/packages/gatsby-plugin-image/src/components/lazy-hydrate.tsx +++ b/packages/gatsby-plugin-image/src/components/lazy-hydrate.tsx @@ -1,11 +1,12 @@ +/* global HAS_REACT_18 */ import React, { MutableRefObject } from "react" -import ReactDOM from "react-dom" import { GatsbyImageProps } from "./gatsby-image.browser" import { LayoutWrapper } from "./layout-wrapper" import { Placeholder } from "./placeholder" import { MainImageProps, MainImage } from "./main-image" import { getMainProps, getPlaceholderProps } from "./hooks" import { ReactElement } from "react" +import type { Root } from "react-dom/client" type LazyHydrateProps = Omit & { isLoading: boolean @@ -14,6 +15,38 @@ type LazyHydrateProps = Omit & { ref: MutableRefObject } +let reactRender +let reactHydrate +if (HAS_REACT_18) { + const reactDomClient = require(`react-dom/client`) + reactRender = ( + Component: React.ReactChild | Iterable, + el: ReactDOM.Container, + root: Root + ): Root => { + if (!root) { + root = reactDomClient.createRoot(el) + } + + root.render(Component) + + return root + } + reactHydrate = ( + Component: React.ReactChild | Iterable, + el: ReactDOM.Container + ): Root => reactDomClient.hydrateRoot(el, Component) +} else { + const reactDomClient = require(`react-dom`) + reactRender = ( + Component: React.ReactChild | Iterable, + el: ReactDOM.Container + ): void => { + reactDomClient.render(Component, el) + } + reactHydrate = reactDomClient.hydrate +} + export function lazyHydrate( { image, @@ -31,7 +64,8 @@ export function lazyHydrate( }: LazyHydrateProps, root: MutableRefObject, hydrated: MutableRefObject, - forceHydrate: MutableRefObject + forceHydrate: MutableRefObject, + reactRootRef: MutableRefObject ): (() => void) | null { const { width, @@ -87,34 +121,25 @@ export function lazyHydrate( if (root.current) { // Force render to mitigate "Expected server HTML to contain a matching" in develop - // @ts-ignore react 18 typings - if (ReactDOM.createRoot) { - if (!hydrated.current) { - // @ts-ignore react 18 typings - hydrated.current = ReactDOM.createRoot(root.current) - } - - // @ts-ignore react 18 typings - hydrated.current.render(component) + if (hydrated.current || forceHydrate.current || HAS_REACT_18) { + reactRootRef.current = reactRender( + component, + root.current, + reactRootRef.current + ) } else { - const doRender = - hydrated.current || forceHydrate.current - ? ReactDOM.render - : ReactDOM.hydrate - doRender(component, root.current) - hydrated.current = true + reactHydrate(component, root.current) } + hydrated.current = true } return (): void => { if (root.current) { - // @ts-ignore react 18 typings - if (ReactDOM.createRoot) { - // @ts-ignore react 18 typings - hydrated.current.render(null) - } else { - ReactDOM.render(null as unknown as ReactElement, root.current) - } + reactRender( + null as unknown as ReactElement, + root.current, + reactRootRef.current + ) } } } diff --git a/packages/gatsby-plugin-image/src/gatsby-node.ts b/packages/gatsby-plugin-image/src/gatsby-node.ts index 139122befaed3..9de986c24fb45 100644 --- a/packages/gatsby-plugin-image/src/gatsby-node.ts +++ b/packages/gatsby-plugin-image/src/gatsby-node.ts @@ -5,6 +5,7 @@ import { ImageLayoutType, ImagePlaceholderType, } from "./resolver-utils" +import { major } from "semver" export * from "./node-apis/preprocess-source" @@ -51,6 +52,9 @@ export const onCreateWebpackConfig: GatsbyNode["onCreateWebpackConfig"] = ({ plugins.define({ // eslint-disable-next-line @typescript-eslint/naming-convention GATSBY___IMAGE: true, + HAS_REACT_18: JSON.stringify( + major(require(`react-dom/package.json`).version) >= 18 + ), }), ], }) diff --git a/packages/gatsby-plugin-image/src/global.d.ts b/packages/gatsby-plugin-image/src/global.d.ts index 5a3a80537bb17..a76544f65b970 100644 --- a/packages/gatsby-plugin-image/src/global.d.ts +++ b/packages/gatsby-plugin-image/src/global.d.ts @@ -3,4 +3,5 @@ export {} declare global { declare var SERVER: boolean | undefined declare var GATSBY___IMAGE: boolean | undefined + declare var HAS_REACT_18: boolean | undefined } diff --git a/packages/gatsby-plugin-jss/package.json b/packages/gatsby-plugin-jss/package.json index 0771c3ba3736f..9550e7f510420 100644 --- a/packages/gatsby-plugin-jss/package.json +++ b/packages/gatsby-plugin-jss/package.json @@ -26,8 +26,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-mdx/package.json b/packages/gatsby-plugin-mdx/package.json index 32d774f3aa741..2f946e2956668 100644 --- a/packages/gatsby-plugin-mdx/package.json +++ b/packages/gatsby-plugin-mdx/package.json @@ -17,8 +17,8 @@ "@mdx-js/mdx": "^1.0.0", "@mdx-js/react": "^1.0.0", "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "dependencies": { "@babel/core": "^7.15.5", diff --git a/packages/gatsby-plugin-netlify-cms/package.json b/packages/gatsby-plugin-netlify-cms/package.json index 7f16aa8e2da5a..37f43d1810724 100644 --- a/packages/gatsby-plugin-netlify-cms/package.json +++ b/packages/gatsby-plugin-netlify-cms/package.json @@ -38,8 +38,8 @@ "peerDependencies": { "gatsby": "^4.0.0-next", "netlify-cms-app": "^2.9.0", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0", + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0", "webpack": "^5.0.0" }, "repository": { diff --git a/packages/gatsby-plugin-offline/package.json b/packages/gatsby-plugin-offline/package.json index f924e24c5f281..7f56a935718e7 100644 --- a/packages/gatsby-plugin-offline/package.json +++ b/packages/gatsby-plugin-offline/package.json @@ -36,8 +36,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-sitemap/package.json b/packages/gatsby-plugin-sitemap/package.json index e0ea0cdc3b147..71dc5a1f52221 100644 --- a/packages/gatsby-plugin-sitemap/package.json +++ b/packages/gatsby-plugin-sitemap/package.json @@ -31,8 +31,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-plugin-styled-components/package.json b/packages/gatsby-plugin-styled-components/package.json index 6b2de1b12a459..7064af5704a3b 100644 --- a/packages/gatsby-plugin-styled-components/package.json +++ b/packages/gatsby-plugin-styled-components/package.json @@ -26,8 +26,8 @@ "peerDependencies": { "babel-plugin-styled-components": ">1.5.0", "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0", + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0", "styled-components": ">=2.0.0" }, "repository": { diff --git a/packages/gatsby-plugin-typography/package.json b/packages/gatsby-plugin-typography/package.json index 0c6bedfb57f8e..2181b678679f6 100644 --- a/packages/gatsby-plugin-typography/package.json +++ b/packages/gatsby-plugin-typography/package.json @@ -30,8 +30,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0", + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0", "react-typography": "^0.16.1 || ^1.0.0-alpha.0", "typography": "^0.16.0 || ^1.0.0-alpha.0" }, diff --git a/packages/gatsby-react-router-scroll/package.json b/packages/gatsby-react-router-scroll/package.json index 98c749746ff12..9b0dc566f3ac2 100644 --- a/packages/gatsby-react-router-scroll/package.json +++ b/packages/gatsby-react-router-scroll/package.json @@ -26,8 +26,8 @@ "main": "index.js", "peerDependencies": { "@gatsbyjs/reach-router": "^1.3.5", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby-remark-autolink-headers/package.json b/packages/gatsby-remark-autolink-headers/package.json index 127aa1f3c6dde..ce94a7ed85941 100644 --- a/packages/gatsby-remark-autolink-headers/package.json +++ b/packages/gatsby-remark-autolink-headers/package.json @@ -30,8 +30,8 @@ "main": "index.js", "peerDependencies": { "gatsby": "^4.0.0-next", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby/cache-dir/__tests__/static-entry.js b/packages/gatsby/cache-dir/__tests__/static-entry.js index eee0027d7cc22..76aa0690f83c9 100644 --- a/packages/gatsby/cache-dir/__tests__/static-entry.js +++ b/packages/gatsby/cache-dir/__tests__/static-entry.js @@ -194,6 +194,7 @@ describe(`develop-static-entry`, () => { global.__BASE_PATH__ = `` global.__ASSET_PREFIX__ = `` global.BROWSER_ESM_ONLY = false + global.HAS_REACT_18 = false }) test(`SSR: onPreRenderHTML can be used to replace headComponents`, done => { diff --git a/packages/gatsby/cache-dir/app.js b/packages/gatsby/cache-dir/app.js index 45f439e57bfd7..cf366df13c9f2 100644 --- a/packages/gatsby/cache-dir/app.js +++ b/packages/gatsby/cache-dir/app.js @@ -1,3 +1,4 @@ +/* global HAS_REACT_18 */ import React from "react" import ReactDOM from "react-dom" import io from "socket.io-client" @@ -39,6 +40,19 @@ loader.setApiRunner(apiRunner) window.___loader = publicLoader +let reactRender +let reactHydrate +if (HAS_REACT_18) { + const reactDomClient = require(`react-dom/client`) + reactRender = (Component, el) => + reactDomClient.createRoot(el).render(Component) + reactHydrate = (Component, el) => reactDomClient.hydrateRoot(el, Component) +} else { + const reactDomClient = require(`react-dom`) + reactRender = reactDomClient.render + reactHydrate = reactDomClient.hydrate +} + // Do dummy dynamic import so the jsonp __webpack_require__.e is added to the commons.js // bundle. This ensures hot reloading doesn't break when someone first adds // a dynamic import. @@ -132,13 +146,9 @@ apiRunnerAsync(`onClientEntry`).then(() => { // Client only pages have any empty body so we just do a normal // render to avoid React complaining about hydration mis-matches. - let defaultRenderer = ReactDOM.render + let defaultRenderer = reactRender if (focusEl && focusEl.children.length) { - if (ReactDOM.hydrateRoot) { - defaultRenderer = ReactDOM.hydrateRoot - } else { - defaultRenderer = ReactDOM.hydrate - } + defaultRenderer = reactHydrate } const renderer = apiRunner( @@ -159,7 +169,7 @@ apiRunnerAsync(`onClientEntry`).then(() => { `first-render-loading-indicator` ) document.body.append(indicatorMountElement) - ReactDOM.render(, indicatorMountElement) + renderer(, indicatorMountElement) }, 1000) dismissLoadingIndicator = () => { @@ -193,16 +203,7 @@ apiRunnerAsync(`onClientEntry`).then(() => { ) document.body.append(indicatorMountElement) - if (renderer === ReactDOM.hydrateRoot) { - ReactDOM.createRoot(indicatorMountElement).render( - - ) - } else { - ReactDOM.render( - , - indicatorMountElement - ) - } + renderer(, indicatorMountElement) } } @@ -225,11 +226,7 @@ apiRunnerAsync(`onClientEntry`).then(() => { dismissLoadingIndicator() } - if (renderer === ReactDOM.hydrateRoot) { - renderer(rootElement, ) - } else { - renderer(, rootElement) - } + renderer(, rootElement) } // https://github.com/madrobby/zepto/blob/b5ed8d607f67724788ec9ff492be297f64d47dfc/src/zepto.js#L439-L450 diff --git a/packages/gatsby/cache-dir/production-app.js b/packages/gatsby/cache-dir/production-app.js index 228e3bdf50960..054962d70579d 100644 --- a/packages/gatsby/cache-dir/production-app.js +++ b/packages/gatsby/cache-dir/production-app.js @@ -1,6 +1,6 @@ +/* global HAS_REACT_18 */ import { apiRunner, apiRunnerAsync } from "./api-runner-browser" import React from "react" -import ReactDOM from "react-dom" import { Router, navigate, Location, BaseContext } from "@gatsbyjs/reach-router" import { ScrollContext } from "gatsby-react-router-scroll" import { StaticQueryContext } from "gatsby" @@ -29,6 +29,15 @@ const loader = new ProdLoader(asyncRequires, matchPaths, window.pageData) setLoader(loader) loader.setApiRunner(apiRunner) +let reactHydrate +if (HAS_REACT_18) { + const reactDomClient = require(`react-dom/client`) + reactHydrate = (Component, el) => reactDomClient.hydrateRoot(el, Component) +} else { + const reactDomClient = require(`react-dom`) + reactHydrate = reactDomClient.hydrate +} + window.asyncRequires = asyncRequires window.___emitter = emitter window.___loader = publicLoader @@ -252,7 +261,7 @@ apiRunnerAsync(`onClientEntry`).then(() => { const renderer = apiRunner( `replaceHydrateFunction`, undefined, - ReactDOM.hydrateRoot ? ReactDOM.hydrateRoot : ReactDOM.hydrate + reactHydrate )[0] function runRender() { @@ -261,11 +270,7 @@ apiRunnerAsync(`onClientEntry`).then(() => { ? document.getElementById(`___gatsby`) : null - if (renderer === ReactDOM.hydrateRoot) { - renderer(rootElement, ) - } else { - renderer(, rootElement) - } + renderer(, rootElement) } // https://github.com/madrobby/zepto/blob/b5ed8d607f67724788ec9ff492be297f64d47dfc/src/zepto.js#L439-L450 diff --git a/packages/gatsby/cache-dir/static-entry.js b/packages/gatsby/cache-dir/static-entry.js index 7fc99f09d213d..ea923dd43aba1 100644 --- a/packages/gatsby/cache-dir/static-entry.js +++ b/packages/gatsby/cache-dir/static-entry.js @@ -1,3 +1,4 @@ +/* global HAS_REACT_18 */ const React = require(`react`) const path = require(`path`) const { @@ -280,10 +281,10 @@ export default async function staticPage({ if (!bodyHtml) { try { // react 18 enabled - if (renderToPipeableStream) { + if (HAS_REACT_18) { const writableStream = new WritableAsPromise() const { pipe } = renderToPipeableStream(bodyComponent, { - onCompleteAll() { + onAllReady() { pipe(writableStream) }, onError() {}, diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index c1a3db37d2071..783e4d4213f41 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -231,8 +231,8 @@ "main": "cache-dir/commonjs/gatsby-browser-entry.js", "module": "cache-dir/gatsby-browser-entry.js", "peerDependencies": { - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "repository": { "type": "git", diff --git a/packages/gatsby/src/services/initialize.ts b/packages/gatsby/src/services/initialize.ts index 0b64b5b670628..5057325428a05 100644 --- a/packages/gatsby/src/services/initialize.ts +++ b/packages/gatsby/src/services/initialize.ts @@ -508,7 +508,9 @@ export async function initialize({ const siteDir = cacheDirectory const tryRequire = `${__dirname}/../utils/test-require-error.js` try { - await fs.copy(srcDir, siteDir) + await fs.copy(srcDir, siteDir, { + overwrite: true, + }) await fs.copy(tryRequire, `${siteDir}/test-require-error.js`) if (lmdbStoreIsUsed) { await fs.ensureDir(`${cacheDirectory}/${lmdbCacheDirectoryName}`) diff --git a/packages/gatsby/src/utils/webpack.config.js b/packages/gatsby/src/utils/webpack.config.js index 0c740a57f25d3..742ab6d6838ce 100644 --- a/packages/gatsby/src/utils/webpack.config.js +++ b/packages/gatsby/src/utils/webpack.config.js @@ -23,6 +23,7 @@ import { WebpackLoggingPlugin } from "./webpack/plugins/webpack-logging" import { hasES6ModuleSupport } from "./browserslist" import { builtinModules } from "module" import { shouldGenerateEngines } from "./engines-helpers" +import { major } from "semver" import { ROUTES_DIRECTORY } from "../constants" const { BabelConfigItemsCacheInvalidatorPlugin } = require(`./babel-loader`) @@ -229,6 +230,9 @@ module.exports = async ( __TRAILING_SLASH__: JSON.stringify(trailingSlash), // TODO Improve asset passing to pages BROWSER_ESM_ONLY: JSON.stringify(hasES6ModuleSupport(directory)), + HAS_REACT_18: JSON.stringify( + major(require(`react-dom/package.json`).version) >= 18 + ), }), plugins.virtualModules(), diff --git a/yarn.lock b/yarn.lock index 1ba82e2814409..873420362ae50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4717,10 +4717,10 @@ dependencies: "@types/react" "*" -"@types/react-dom@^16.9.8": - version "16.9.8" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423" - integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA== +"@types/react-dom@^17.0.13": + version "17.0.13" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.13.tgz#a3323b974ee4280070982b3112351bb1952a7809" + integrity sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ== dependencies: "@types/react" "*" @@ -4731,7 +4731,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.14.8", "@types/react@^16.9.56": +"@types/react@*", "@types/react@^16.14.8": version "16.14.8" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.8.tgz#4aee3ab004cb98451917c9b7ada3c7d7e52db3fe" integrity sha512-QN0/Qhmx+l4moe7WJuTxNiTsjBwlBGHqKGvInSQCBdo7Qio0VtOqwsC0Wq7q3PbJlB0cR4Y4CVo1OOe6BOsOmA== @@ -4740,6 +4740,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^17.0.40": + version "17.0.40" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.40.tgz#dc010cee6254d5239a138083f3799a16638e6bad" + integrity sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/readable-stream@^2.3.9": version "2.3.9" resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.9.tgz#40a8349e6ace3afd2dd1b6d8e9b02945de4566a9" @@ -8308,14 +8317,7 @@ cpy@^8.0.0: p-filter "^2.1.0" p-map "^3.0.0" -crc-32@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" - dependencies: - exit-on-epipe "~1.0.1" - printj "~1.1.0" - -crc-32@~1.2.1: +crc-32@~1.2.0, crc-32@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.1.tgz#436d2bcaad27bcb6bd073a2587139d3024a16460" integrity sha512-Dn/xm/1vFFgs3nfrpEVScHoIslO9NZRITWGz/1E/St6u4xw99vfZzVkW0OSnzx2h9egej9xwMCEut6sqwokM/w== @@ -19375,21 +19377,12 @@ preval.macro@^5.0.0: dependencies: babel-plugin-preval "^5.0.0" -printj@~1.1.0: - version "1.1.2" - resolved "http://registry.npmjs.org/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" - printj@~1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/printj/-/printj-1.2.3.tgz#2cfb2b192a1e5385dbbe5b46658ac34aa828508a" integrity sha512-sanczS6xOJOg7IKDvi4sGOUOe7c1tsEzjwlLFH/zgwx/uyImVM9/rgBkc8AfiQa/Vg54nRd8mkm9yI7WV/O+WA== -printj@~1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/printj/-/printj-1.3.0.tgz#9018a918a790e43707f10625d6e10187a367cff6" - integrity sha512-017o8YIaz8gLhaNxRB9eBv2mWXI2CtzhPJALnQTP+OPpuUfP0RMWqr/mHCzqVeu1AQxfzSfAtAq66vKB8y7Lzg== - -printj@~1.3.1: +printj@~1.3.0, printj@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/printj/-/printj-1.3.1.tgz#9af6b1d55647a1587ac44f4c1654a4b95b8e12cb" integrity sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg== @@ -22053,7 +22046,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==