From 34f5b8cb115fa7575bfe88f7436c00e1b3c4dc24 Mon Sep 17 00:00:00 2001 From: GatsbyJS Bot Date: Sun, 28 Feb 2021 12:33:29 -0500 Subject: [PATCH] fix(hmr): accept hot updates for modules above page templates (#29752) (#29835) * fix(hmr): accept hot updates for modules above page templates * actually use fast-refresh for gatsby-browser and shadowed theme-ui config * exclude actually need to be instance of RegExp * tmp * more tmp * init navigation after loader is set * cleanup tmp code * init navigation after resources for current page are loaded * remove commented out code * revert devtool change (cherry picked from commit 55778eb11e816ceaf29ad20d6ff05192cdf68f4c) Co-authored-by: Michal Piechowiak --- .../hot-reloading/gatsby-browser.js | 24 +++++++++++++++++++ .../development-runtime/gatsby-browser.js | 14 ++++++----- .../src/wrap-root-element.js | 3 +++ packages/gatsby/cache-dir/app.js | 13 +++++----- packages/gatsby/cache-dir/root.js | 24 +++++++++---------- packages/gatsby/src/utils/webpack-utils.ts | 23 ++++++++++++++++-- packages/gatsby/src/utils/webpack.config.js | 2 +- 7 files changed, 74 insertions(+), 29 deletions(-) create mode 100644 e2e-tests/development-runtime/cypress/integration/hot-reloading/gatsby-browser.js diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/gatsby-browser.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/gatsby-browser.js new file mode 100644 index 0000000000000..f1007cde510df --- /dev/null +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/gatsby-browser.js @@ -0,0 +1,24 @@ +const TEST_ID = `gatsby-browser-hmr` + +describe(`hot reloading above page template (gatsby-browser)`, () => { + beforeEach(() => { + cy.visit(`/`).waitForRouteChange() + }) + it(`displays placeholder content on launch`, () => { + cy.getTestElement(TEST_ID).should( + `contain.text`, + `%TEST_HMR_IN_GATSBY_BROWSER%` + ) + }) + + it(`hot reloads with new content`, () => { + const text = `HMR_IN_GATSBY_BROWSER_WORKS` + cy.exec( + `npm run update -- --file src/wrap-root-element.js --replacements "TEST_HMR_IN_GATSBY_BROWSER:${text}"` + ) + + cy.waitForHmr() + + cy.getTestElement(TEST_ID).should(`contain.text`, text) + }) +}) diff --git a/e2e-tests/development-runtime/gatsby-browser.js b/e2e-tests/development-runtime/gatsby-browser.js index 1709cf2c75113..d8a65b3cc6595 100644 --- a/e2e-tests/development-runtime/gatsby-browser.js +++ b/e2e-tests/development-runtime/gatsby-browser.js @@ -1,4 +1,5 @@ -const Wrapper = require(`./src/wrap-root-element`).default +import WrapRootElement from "./src/wrap-root-element" +import * as React from "react" if (typeof window !== `undefined`) { window.___PageComponentLifecycleCallsLog = [] @@ -11,16 +12,17 @@ const addLogEntry = (action, location) => { }) } -exports.onPreRouteUpdate = ({ location }) => { +export const onPreRouteUpdate = ({ location }) => { addLogEntry(`onPreRouteUpdate`, location) } -exports.onRouteUpdate = ({ location }) => { +export const onRouteUpdate = ({ location }) => { addLogEntry(`onRouteUpdate`, location) } - -exports.onPrefetchPathname = ({ pathname }) => { +export const onPrefetchPathname = ({ pathname }) => { addLogEntry(`onPrefetchPathname`, pathname) } -exports.wrapRootElement = Wrapper +export const wrapRootElement = ({ element }) => ( + +) diff --git a/e2e-tests/development-runtime/src/wrap-root-element.js b/e2e-tests/development-runtime/src/wrap-root-element.js index fb0a02975b6d9..a15637a9d2710 100644 --- a/e2e-tests/development-runtime/src/wrap-root-element.js +++ b/e2e-tests/development-runtime/src/wrap-root-element.js @@ -22,6 +22,9 @@ const WrapRootElement = ({ element }) => (
StaticQuery in wrapRootElement test (should show site title): {title} +
+ %TEST_HMR_IN_GATSBY_BROWSER% +
)} diff --git a/packages/gatsby/cache-dir/app.js b/packages/gatsby/cache-dir/app.js index 3676dd72be5e0..569cdb9d751e3 100644 --- a/packages/gatsby/cache-dir/app.js +++ b/packages/gatsby/cache-dir/app.js @@ -13,16 +13,15 @@ import asyncRequires from "$virtual/async-requires" // Generated during bootstrap import matchPaths from "$virtual/match-paths.json" import { LoadingIndicatorEventHandler } from "./loading-indicator" - +import Root from "./root" +import { init as navigationInit } from "./navigation" // ensure in develop we have at least some .css (even if it's empty). // this is so there is no warning about not matching content-type when site doesn't include any regular css (for example when css-in-js is used) // this also make sure that if all css is removed in develop we are not left with stale commons.css that have stale content import "./blank.css" -// Enable fast-refresh for virtual sync-requires -module.hot.accept(`$virtual/async-requires`, () => { - // Manually reload -}) +// Enable fast-refresh for virtual sync-requires and gatsby-browser +module.hot.accept([`$virtual/async-requires`, `./api-runner-browser`]) window.___emitter = emitter @@ -160,8 +159,8 @@ apiRunnerAsync(`onClientEntry`).then(() => { loader.loadPage(`/404.html`), loader.loadPage(window.location.pathname), ]).then(() => { - const preferDefault = m => (m && m.default) || m - const Root = preferDefault(require(`./root`)) + navigationInit() + domReady(() => { if (dismissLoadingIndicator) { dismissLoadingIndicator() diff --git a/packages/gatsby/cache-dir/root.js b/packages/gatsby/cache-dir/root.js index 95e541de4084b..09220ca79fcf7 100644 --- a/packages/gatsby/cache-dir/root.js +++ b/packages/gatsby/cache-dir/root.js @@ -2,19 +2,13 @@ import React from "react" import { Router, Location, BaseContext } from "@reach/router" import { ScrollContext } from "gatsby-react-router-scroll" -import { - shouldUpdateScroll, - init as navigationInit, - RouteUpdates, -} from "./navigation" +import { shouldUpdateScroll, RouteUpdates } from "./navigation" import { apiRunner } from "./api-runner-browser" import loader from "./loader" import { PageQueryStore, StaticQueryStore } from "./query-result-store" import EnsureResources from "./ensure-resources" import FastRefreshOverlay from "./fast-refresh-overlay" -navigationInit() - // In gatsby v2 if Router is used in page using matchPaths // paths need to contain full path. // For example: @@ -103,7 +97,7 @@ const Root = () => ( ) // Let site, plugins wrap the site e.g. for Redux. -const WrappedRoot = apiRunner( +const rootWrappedWithWrapRootElement = apiRunner( `wrapRootElement`, { element: }, , @@ -112,8 +106,12 @@ const WrappedRoot = apiRunner( } ).pop() -export default () => ( - - {WrappedRoot} - -) +function RootWrappedWithOverlayAndProvider() { + return ( + + {rootWrappedWithWrapRootElement} + + ) +} + +export default RootWrappedWithOverlayAndProvider diff --git a/packages/gatsby/src/utils/webpack-utils.ts b/packages/gatsby/src/utils/webpack-utils.ts index 53c973731133d..47ecafe3f96f6 100644 --- a/packages/gatsby/src/utils/webpack-utils.ts +++ b/packages/gatsby/src/utils/webpack-utils.ts @@ -731,13 +731,32 @@ export const createWebpackUtils = ( } ): CssMinimizerPlugin => new CssMinimizerPlugin(options) - plugins.fastRefresh = (): Plugin => - new ReactRefreshWebpackPlugin({ + plugins.fastRefresh = ({ modulesThatUseGatsby }): Plugin => { + const regExpToHack = /node_modules/ + regExpToHack.test = (modulePath: string): boolean => { + // when it's not coming from node_modules we treat it as a source file. + if (!vendorRegex.test(modulePath)) { + return false + } + + // If the module uses Gatsby as a dependency + // we want to treat it as src because of shadowing + return !modulesThatUseGatsby.some(module => + modulePath.includes(module.path) + ) + } + + return new ReactRefreshWebpackPlugin({ overlay: { sockIntegration: `whm`, module: path.join(__dirname, `fast-refresh-module`), }, + // this is a bit hacky - exclude expect string or regexp or array of those + // so this is tricking ReactRefreshWebpackPlugin with providing regexp with + // overwritten .test method + exclude: regExpToHack, }) + } plugins.extractText = (options: any): Plugin => new MiniCssExtractPlugin({ diff --git a/packages/gatsby/src/utils/webpack.config.js b/packages/gatsby/src/utils/webpack.config.js index af93ae208e58c..18b21a4e15e3b 100644 --- a/packages/gatsby/src/utils/webpack.config.js +++ b/packages/gatsby/src/utils/webpack.config.js @@ -216,7 +216,7 @@ module.exports = async ( case `develop`: { configPlugins = configPlugins .concat([ - plugins.fastRefresh(), + plugins.fastRefresh({ modulesThatUseGatsby }), plugins.hotModuleReplacement(), plugins.noEmitOnErrors(), plugins.eslintGraphqlSchemaReload(),