diff --git a/.circleci/config.yml b/.circleci/config.yml index 6cc1a86c8e5f6..5dbe61c0b83d0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -325,14 +325,6 @@ jobs: e2e_tests_development_runtime: <<: *e2e_tests_development_runtime_alias - e2e_tests_development_runtime_fast_refresh: - <<: *e2e_tests_development_runtime_alias - environment: - GATSBY_HOT_LOADER: fast-refresh - CYPRESS_HOT_LOADER: fast-refresh - CYPRESS_PROJECT_ID: 917bea - CYPRESS_RECORD_KEY: 4750fb36-4576-4638-a617-d243a381acef - e2e_tests_development_runtime_with_experimental_react: <<: *e2e_tests_development_runtime_alias @@ -616,8 +608,6 @@ workflows: <<: *e2e-test-workflow - e2e_tests_development_runtime: <<: *e2e-test-workflow - - e2e_tests_development_runtime_fast_refresh: - <<: *e2e-test-workflow - e2e_tests_production_runtime: <<: *e2e-test-workflow - themes_e2e_tests_production_runtime: diff --git a/docs/docs/conceptual/gatsby-lifecycle-apis.md b/docs/docs/conceptual/gatsby-lifecycle-apis.md index 3e0709d58ad4d..ce9580365e1ad 100644 --- a/docs/docs/conceptual/gatsby-lifecycle-apis.md +++ b/docs/docs/conceptual/gatsby-lifecycle-apis.md @@ -44,7 +44,7 @@ During the main bootstrap sequence, Gatsby (in this order): - writes page redirects (if any) to `.cache/redirects.json` - the [onPostBootstrap](/docs/reference/config-files/gatsby-node/#onPostBootstrap) lifecycle is executed -In development this is a running process powered by [webpack](https://github.com/gatsbyjs/gatsby/blob/dd91b8dceb3b8a20820b15acae36529799217ae4/packages/gatsby/package.json#L128) and [`react-hot-loader`](https://github.com/gatsbyjs/gatsby/blob/dd91b8dceb3b8a20820b15acae36529799217ae4/packages/gatsby/package.json#L104), so changes to any files get re-run through the sequence again, with [smart cache invalidation](https://github.com/gatsbyjs/gatsby/blob/ffd8b2d691c995c760fe380769852bcdb26a2278/packages/gatsby/src/bootstrap/index.js#L141). For example, `gatsby-source-filesystem` watches files for changes, and each change triggers re-running queries. Other plugins may also perform this service. Queries are also watched, so if you modify a query, your development app is hot reloaded. +In development this is a running process powered by [webpack](https://github.com/gatsbyjs/gatsby/blob/dd91b8dceb3b8a20820b15acae36529799217ae4/packages/gatsby/package.json#L128) and [`react-refresh`](https://github.com/gatsbyjs/gatsby/blob/4dff7550a29f4635bf47a068a05f634470eb9ef1/packages/gatsby/package.json#L134)), so changes to any files get re-run through the sequence again, with [smart cache invalidation](https://github.com/gatsbyjs/gatsby/blob/ffd8b2d691c995c760fe380769852bcdb26a2278/packages/gatsby/src/bootstrap/index.js#L141). For example, `gatsby-source-filesystem` watches files for changes, and each change triggers re-running queries. Other plugins may also perform this service. Queries are also watched, so if you modify a query, your development app is hot reloaded. The core of the bootstrap process is the "api-runner", which helps to execute APIs in sequence, with state managed in Redux. Gatsby exposes a number of lifecycle APIs which can either be implemented by you (or any of your configured plugins) in `gatsby-node.js`, `gatsby-browser.js` or `gatsby-ssr.js`. diff --git a/e2e-tests/development-runtime/cypress/integration/eslint-rules/limited-exports-page-templates.js b/e2e-tests/development-runtime/cypress/integration/eslint-rules/limited-exports-page-templates.js index 5c32d2bd47cb6..2cc1ad530ee1d 100644 --- a/e2e-tests/development-runtime/cypress/integration/eslint-rules/limited-exports-page-templates.js +++ b/e2e-tests/development-runtime/cypress/integration/eslint-rules/limited-exports-page-templates.js @@ -1,21 +1,19 @@ -if (Cypress.env("HOT_LOADER") === `fast-refresh`) { - describe(`limited-exports-page-templates`, () => { - // Skipped because HMR not show warnings because of https://github.com/webpack-contrib/webpack-hot-middleware/pull/397 - it.skip(`should log warning to console for invalid export`, () => { - cy.visit(`/eslint-rules/limited-exports-page-templates`, { - onBeforeLoad(win) { - cy.stub(win.console, "log").as(`consoleLog`) - }, - }).waitForRouteChange() +describe(`limited-exports-page-templates`, () => { + // Skipped because HMR not show warnings because of https://github.com/webpack-contrib/webpack-hot-middleware/pull/397 + it.skip(`should log warning to console for invalid export`, () => { + cy.visit(`/eslint-rules/limited-exports-page-templates`, { + onBeforeLoad(win) { + cy.stub(win.console, "log").as(`consoleLog`) + }, + }).waitForRouteChange() - cy.get(`@consoleLog`).should( - `be.calledWithMatch`, - /15:1 warning In page templates only a default export of a valid React component and the named export of a page query is allowed./i - ) - cy.get(`@consoleLog`).should( - `not.be.calledWithMatch`, - /17:1 warning In page templates only a default export of a valid React component and the named export of a page query is allowed./i - ) - }) + cy.get(`@consoleLog`).should( + `be.calledWithMatch`, + /15:1 warning In page templates only a default export of a valid React component and the named export of a page query is allowed./i + ) + cy.get(`@consoleLog`).should( + `not.be.calledWithMatch`, + /17:1 warning In page templates only a default export of a valid React component and the named export of a page query is allowed./i + ) }) -} +}) diff --git a/e2e-tests/development-runtime/cypress/integration/eslint-rules/no-anonymous-exports-page-templates.js b/e2e-tests/development-runtime/cypress/integration/eslint-rules/no-anonymous-exports-page-templates.js index 04d7a521b5d87..2b09f3b455d30 100644 --- a/e2e-tests/development-runtime/cypress/integration/eslint-rules/no-anonymous-exports-page-templates.js +++ b/e2e-tests/development-runtime/cypress/integration/eslint-rules/no-anonymous-exports-page-templates.js @@ -1,29 +1,27 @@ -if (Cypress.env("HOT_LOADER") === `fast-refresh`) { - describe(`no-anonymous-exports-page-templates`, () => { - // Skipped because HMR not show warnings because of https://github.com/webpack-contrib/webpack-hot-middleware/pull/397 - it.skip(`should log warning to console for arrow functions`, () => { - cy.visit(`/eslint-rules/no-anonymous-exports-page-templates`, { - onBeforeLoad(win) { - cy.stub(win.console, "log").as(`consoleLog`) - }, - }).waitForRouteChange() +describe(`no-anonymous-exports-page-templates`, () => { + // Skipped because HMR not show warnings because of https://github.com/webpack-contrib/webpack-hot-middleware/pull/397 + it.skip(`should log warning to console for arrow functions`, () => { + cy.visit(`/eslint-rules/no-anonymous-exports-page-templates`, { + onBeforeLoad(win) { + cy.stub(win.console, "log").as(`consoleLog`) + }, + }).waitForRouteChange() - cy.get(`@consoleLog`).should( - `be.calledWithMatch`, - /Anonymous arrow functions cause Fast Refresh to not preserve local component state./i - ) - }) - it.skip(`should log warning to console for function declarations`, () => { - cy.visit(`/eslint-rules/no-anonymous-exports-page-templates-function`, { - onBeforeLoad(win) { - cy.stub(win.console, "log").as(`consoleLog`) - }, - }).waitForRouteChange() + cy.get(`@consoleLog`).should( + `be.calledWithMatch`, + /Anonymous arrow functions cause Fast Refresh to not preserve local component state./i + ) + }) + it.skip(`should log warning to console for function declarations`, () => { + cy.visit(`/eslint-rules/no-anonymous-exports-page-templates-function`, { + onBeforeLoad(win) { + cy.stub(win.console, "log").as(`consoleLog`) + }, + }).waitForRouteChange() - cy.get(`@consoleLog`).should( - `be.calledWithMatch`, - /Anonymous function declarations cause Fast Refresh to not preserve local component state./i - ) - }) + cy.get(`@consoleLog`).should( + `be.calledWithMatch`, + /Anonymous function declarations cause Fast Refresh to not preserve local component state./i + ) }) -} +}) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/arrow-functions.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/arrow-functions.js index 2bb158180585c..4b97fe3e7b9af 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/arrow-functions.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/arrow-functions.js @@ -19,6 +19,8 @@ describe(`hot-reloading anonymous arrow functions`, () => { `npm run update -- --file src/components/title.tsx --replacements "TITLE:${text}"` ) + cy.waitForHmr() + cy.getTestElement(IDS.title).should(`have.text`, text) }) }) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/class-component.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/class-component.js index ef7b10fa6203b..411479dd1f0bc 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/class-component.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/class-component.js @@ -23,6 +23,8 @@ describe(`reloading class component`, () => { `npm run update -- --file src/components/class-component.js --replacements "CUSTOM_STATE:${value}"` ) + cy.waitForHmr() + cy.getTestElement(`stateful-${TEST_ID}`).should( `have.text`, `Custom Message` diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/hooks.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/hooks.js index 00f34f5d69df4..a398819c6c4d2 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/hooks.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/hooks.js @@ -1,16 +1,23 @@ const COUNT_ID = `count` +const amount = 100 describe(`hot-reloading hooks`, () => { beforeEach(() => { + cy.exec( + `npm run update -- --file src/pages/hooks.js --replacements "count + ${amount}:count + 1" --exact` + ) + cy.wait(1000) + cy.visit(`/hooks`).waitForRouteChange() }) - it.skip(`can update component`, () => { - const amount = 100 + it(`can update component`, () => { cy.exec( `npm run update -- --file src/pages/hooks.js --replacements "count + 1:count + ${amount}" --exact` ) + cy.waitForHmr() + cy.getTestElement(`increment`).click() cy.getTestElement(COUNT_ID).invoke(`text`).should(`eq`, `${amount}`) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/new-file.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/new-file.js index a38d15f58616a..c98ba303436cd 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/new-file.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/new-file.js @@ -18,12 +18,14 @@ describe(`hot reloading new page component`, () => { }) it(`can hot reload a new page file`, () => { + cy.visit(`/sample`).waitForRouteChange() + const text = `World` cy.exec( `npm run update -- --file src/pages/sample.js --replacements "REPLACEMENT:${text}"` ) - cy.visit(`/sample`).waitForRouteChange() + cy.waitForHmr() cy.getTestElement(`message`).invoke(`text`).should(`eq`, `Hello ${text}`) }) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/non-js-file.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/non-js-file.js index 287886a3ac714..61b1961150c5c 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/non-js-file.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/non-js-file.js @@ -1,21 +1,33 @@ const TEMPLATE = `SUB_TITLE` const TEST_ID = `sub-title` +const message = `This is a sub-title` describe(`hot reloading non-js file`, () => { beforeEach(() => { + cy.exec( + `npm run update -- --file content/2018-12-14-hello-world.md --replacements "${message}:%${TEMPLATE}%" --exact` + ) + cy.wait(1000) + cy.visit(`/2018-12-14-hello-world/`).waitForRouteChange() + + cy.wait(1000) }) it(`displays placeholder content on launch`, () => { cy.getTestElement(TEST_ID).invoke(`text`).should(`contain`, TEMPLATE) }) - it.skip(`hot reloads with new content`, () => { - const message = `This is a sub-title` + it(`hot reloads with new content`, () => { + cy.getTestElement(TEST_ID).invoke(`text`).should(`contain`, TEMPLATE) + cy.exec( `npm run update -- --file content/2018-12-14-hello-world.md --replacements "${TEMPLATE}:${message}"` ) + // wati for socket.io to update + cy.wait(5000) + cy.getTestElement(TEST_ID).invoke(`text`).should(`eq`, message) }) }) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/page-component.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/page-component.js index 63d82c8c1baae..c554457e31647 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/page-component.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/page-component.js @@ -14,6 +14,8 @@ describe(`hot reloading page component`, () => { `npm run update -- --file src/pages/index.js --replacements "GATSBY_SITE:${text}"` ) + cy.waitForHmr() + cy.getTestElement(TEST_ID).should(`contain.text`, text) }) }) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/page-queries.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/page-queries.js index 9c1b5bae104a8..7e20536d40d3c 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/page-queries.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/page-queries.js @@ -18,6 +18,8 @@ describe(`hot-reloading page queries`, () => { `npm run update -- --file src/pages/page-query.js --replacements "# %AUTHOR%:author" --exact` ) + cy.waitForHmr() + cy.getTestElement(`hot`).invoke(`text`).should(`contain`, author) }) }) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/static-queries.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/static-queries.js index 926f2a623e58a..bf4e806ec6fc9 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/static-queries.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/static-queries.js @@ -29,6 +29,8 @@ describe(`hot-reloading static queries`, () => { `npm run update -- --file src/components/static-query/use-static-query/hot.js --replacements "# %AUTHOR%:author" --exact` ) + cy.waitForHmr() + cy.getTestElement(`use-static-query-hot`) .invoke(`text`) .should(`contain`, author) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/template-component.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/template-component.js index 3abf79121c17d..a06d39ca1fca6 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/template-component.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/template-component.js @@ -15,6 +15,8 @@ describe(`hot reloading template component`, () => { `npm run update -- --file src/templates/blog-post.js --replacements "${TEMPLATE}:${message}"` ) + cy.waitForHmr() + cy.getTestElement(TEST_ID).should(`have.text`, `Hello ${message}`) }) }) diff --git a/e2e-tests/development-runtime/cypress/integration/navigation/linking.js b/e2e-tests/development-runtime/cypress/integration/navigation/linking.js index 450d61d9fa8ae..46e7d7763505b 100644 --- a/e2e-tests/development-runtime/cypress/integration/navigation/linking.js +++ b/e2e-tests/development-runtime/cypress/integration/navigation/linking.js @@ -143,76 +143,36 @@ describe(`navigation`, () => { }) }) - if (Cypress.env("HOT_LOADER") !== `fast-refresh`) { - describe(`All location changes should trigger an effect (react-hot-loader)`, () => { - beforeEach(() => { - cy.visit(`/navigation-effects`).waitForRouteChange() - }) - - it(`should trigger an effect after a search param has changed`, () => { - cy.findByTestId(`effect-message`).should( - `have.text`, - `Waiting for effect` - ) - cy.findByTestId(`send-search-message`).click().waitForRouteChange() - cy.findByTestId(`effect-message`).should( - `have.text`, - `?message=searchParam` - ) - }) - - it(`should trigger an effect after the hash has changed`, () => { - cy.findByTestId(`effect-message`).should( - `have.text`, - `Waiting for effect` - ) - cy.findByTestId(`send-hash-message`).click().waitForRouteChange() - cy.findByTestId(`effect-message`).should(`have.text`, `#message-hash`) - }) - - it(`should trigger an effect after the state has changed`, () => { - cy.findByTestId(`effect-message`).should(`have.text`, ``) - cy.findByTestId(`send-state-message`).click().waitForRouteChange() - cy.findByTestId(`effect-message`).should( - `have.text`, - `this is a message using the state` - ) - }) - }) - } - // TODO: Check if this is the correct behavior - if (Cypress.env("HOT_LOADER") === `fast-refresh`) { - describe(`All location changes should trigger an effect (fast-refresh)`, () => { - beforeEach(() => { - cy.visit(`/navigation-effects`).waitForRouteChange() - }) - - it(`should trigger an effect after a search param has changed`, () => { - cy.findByTestId(`effect-message`).should(`have.text`, ``) - cy.findByTestId(`send-search-message`).click().waitForRouteChange() - cy.findByTestId(`effect-message`).should( - `have.text`, - `?message=searchParam` - ) - }) - - it(`should trigger an effect after the hash has changed`, () => { - cy.findByTestId(`effect-message`).should(`have.text`, ``) - cy.findByTestId(`send-hash-message`).click().waitForRouteChange() - cy.findByTestId(`effect-message`).should(`have.text`, `#message-hash`) - }) - - it(`should trigger an effect after the state has changed`, () => { - cy.findByTestId(`effect-message`).should(`have.text`, ``) - cy.findByTestId(`send-state-message`).click().waitForRouteChange() - cy.findByTestId(`effect-message`).should( - `have.text`, - `this is a message using the state` - ) - }) - }) - } + describe(`All location changes should trigger an effect (fast-refresh)`, () => { + beforeEach(() => { + cy.visit(`/navigation-effects`).waitForRouteChange() + }) + + it(`should trigger an effect after a search param has changed`, () => { + cy.findByTestId(`effect-message`).should(`have.text`, ``) + cy.findByTestId(`send-search-message`).click().waitForRouteChange() + cy.findByTestId(`effect-message`).should( + `have.text`, + `?message=searchParam` + ) + }) + + it(`should trigger an effect after the hash has changed`, () => { + cy.findByTestId(`effect-message`).should(`have.text`, ``) + cy.findByTestId(`send-hash-message`).click().waitForRouteChange() + cy.findByTestId(`effect-message`).should(`have.text`, `#message-hash`) + }) + + it(`should trigger an effect after the state has changed`, () => { + cy.findByTestId(`effect-message`).should(`have.text`, ``) + cy.findByTestId(`send-state-message`).click().waitForRouteChange() + cy.findByTestId(`effect-message`).should( + `have.text`, + `this is a message using the state` + ) + }) + }) describe(`Route lifecycle update order`, () => { it(`calls onPreRouteUpdate, render and onRouteUpdate the correct amount of times on route change`, () => { diff --git a/e2e-tests/development-runtime/cypress/support/commands.js b/e2e-tests/development-runtime/cypress/support/commands.js index c30ad592e9874..b109d1e1eb937 100644 --- a/e2e-tests/development-runtime/cypress/support/commands.js +++ b/e2e-tests/development-runtime/cypress/support/commands.js @@ -24,7 +24,7 @@ Cypress.Commands.add(`lifecycleCallOrder`, expectedActionCallOrder => if (expectedActionCallOrderLength > actionsLength) { return false } - + let prevActionIndex = -1 for (let i = 0; i < actionsLength; i += 1) { const nextActionIndex = prevActionIndex + 1 @@ -81,6 +81,27 @@ Cypress.Commands.add( } ) -Cypress.Commands.add(`assertRoute`, (route) => { +Cypress.Commands.add(`assertRoute`, route => { cy.url().should(`equal`, `${window.location.origin}${route}`) }) + +// overwriting visit and creating a waitForHmr function to help us deal with HMR +Cypress.Commands.overwrite("visit", (orig, url, options = {}) => { + const newOptions = { + ...options, + onBeforeLoad: win => { + if (options.onBeforeLoad) { + optiosn.onBeforeLoad(win) + } + + cy.spy(win.console, "log").as(`hmrConsoleLog`) + }, + } + + return orig(url, newOptions) +}) + +Cypress.Commands.add(`waitForHmr`, (message = `App is up to date`) => { + cy.get(`@hmrConsoleLog`).should(`be.calledWithMatch`, message) + cy.wait(1000) +}) diff --git a/e2e-tests/development-runtime/package.json b/e2e-tests/development-runtime/package.json index 8728b24aa482a..f1a1f077235d5 100644 --- a/e2e-tests/development-runtime/package.json +++ b/e2e-tests/development-runtime/package.json @@ -30,7 +30,6 @@ "scripts": { "build": "gatsby build", "develop": "cross-env CYPRESS_SUPPORT=y ENABLE_GATSBY_REFRESH_ENDPOINT=true gatsby develop", - "develop:fast-refresh": "cross-env CYPRESS_SUPPORT=y ENABLE_GATSBY_REFRESH_ENDPOINT=true GATSBY_HOT_LOADER=fast-refresh gatsby develop", "serve": "gatsby serve", "start": "npm run develop", "format": "prettier --write \"src/**/*.js\"", @@ -65,4 +64,4 @@ "resolutions": { "cypress": "6.1.0" } -} \ No newline at end of file +} diff --git a/packages/gatsby-plugin-preact/src/gatsby-node.js b/packages/gatsby-plugin-preact/src/gatsby-node.js index 5e25c8e97c70d..f6805ee2f11d0 100644 --- a/packages/gatsby-plugin-preact/src/gatsby-node.js +++ b/packages/gatsby-plugin-preact/src/gatsby-node.js @@ -1,10 +1,5 @@ const PreactRefreshPlugin = require(`@prefresh/webpack`) -exports.onPreInit = () => { - // force fast-refresh in gatsby - process.env.GATSBY_HOT_LOADER = `fast-refresh` -} - exports.onCreateBabelConfig = ({ actions, stage }) => { if (stage === `develop`) { // enable react-refresh babel plugin to enable hooks diff --git a/packages/gatsby/cache-dir/__tests__/error-overlay-handler.js b/packages/gatsby/cache-dir/__tests__/error-overlay-handler.js deleted file mode 100644 index 34a195ef61f87..0000000000000 --- a/packages/gatsby/cache-dir/__tests__/error-overlay-handler.js +++ /dev/null @@ -1,58 +0,0 @@ -const { - reportError, - clearError, - errorMap, -} = require(`../error-overlay-handler`) - -import * as ErrorOverlay from "react-error-overlay" - -jest.mock(`react-error-overlay`, () => { - return { - reportBuildError: jest.fn(), - dismissBuildError: jest.fn(), - startReportingRuntimeErrors: jest.fn(), - setEditorHandler: jest.fn(), - } -}) - -beforeEach(() => { - ErrorOverlay.reportBuildError.mockClear() - ErrorOverlay.dismissBuildError.mockClear() -}) - -describe(`errorOverlayHandler`, () => { - describe(`clearError()`, () => { - beforeEach(() => { - reportError(`foo`, `error`) - reportError(`bar`, `error`) - }) - afterAll(() => { - clearError(`foo`) - clearError(`bar`) - }) - it(`should clear specific error type`, () => { - expect(Object.keys(errorMap)).toHaveLength(2) - clearError(`foo`) - expect(Object.keys(errorMap)).toHaveLength(1) - expect(ErrorOverlay.dismissBuildError).not.toHaveBeenCalled() - }) - - it(`should call ErrorOverlay to dismiss build errors`, () => { - clearError(`foo`) - clearError(`bar`) - expect(ErrorOverlay.dismissBuildError).toHaveBeenCalled() - }) - }) - describe(`reportErrorOverlay()`, () => { - it(`should not add error if it's empty and not call ErrorOverlay`, () => { - reportError(`foo`, null) - expect(Object.keys(errorMap)).toHaveLength(0) - expect(ErrorOverlay.reportBuildError).not.toHaveBeenCalled() - }) - it(`should add error if it has a truthy value and call ErrorOverlay`, () => { - reportError(`foo`, `bar`) - expect(Object.keys(errorMap)).toHaveLength(1) - expect(ErrorOverlay.reportBuildError).toHaveBeenCalled() - }) - }) -}) diff --git a/packages/gatsby/cache-dir/app.js b/packages/gatsby/cache-dir/app.js index 0927b5999202f..32fc4f87aca9f 100644 --- a/packages/gatsby/cache-dir/app.js +++ b/packages/gatsby/cache-dir/app.js @@ -13,11 +13,10 @@ import syncRequires from "$virtual/sync-requires" // Generated during bootstrap import matchPaths from "$virtual/match-paths.json" -if (process.env.GATSBY_HOT_LOADER === `fast-refresh` && module.hot) { - module.hot.accept(`$virtual/sync-requires`, () => { - // Manually reload - }) -} +// Enable fast-refresh for virtual sync-requires +module.hot.accept(`$virtual/sync-requires`, () => { + // Manually reload +}) window.___emitter = emitter diff --git a/packages/gatsby/cache-dir/error-overlay-handler.js b/packages/gatsby/cache-dir/error-overlay-handler.js index c4dea30377c63..cf9361d02e6e2 100644 --- a/packages/gatsby/cache-dir/error-overlay-handler.js +++ b/packages/gatsby/cache-dir/error-overlay-handler.js @@ -1,33 +1,8 @@ -const overlayPackage = - process.env.GATSBY_HOT_LOADER !== `fast-refresh` - ? require(`react-error-overlay`) - : require(`@pmmmwh/react-refresh-webpack-plugin/overlay`) +const overlayPackage = require(`@pmmmwh/react-refresh-webpack-plugin/overlay`) const ErrorOverlay = { - showCompileError: - process.env.GATSBY_HOT_LOADER !== `fast-refresh` - ? overlayPackage.reportBuildError - : overlayPackage.showCompileError, - clearCompileError: - process.env.GATSBY_HOT_LOADER !== `fast-refresh` - ? overlayPackage.dismissBuildError - : overlayPackage.clearCompileError, -} - -if (process.env.GATSBY_HOT_LOADER !== `fast-refresh`) { - // Report runtime errors - overlayPackage.startReportingRuntimeErrors({ - onError: () => {}, - filename: `/commons.js`, - }) - overlayPackage.setEditorHandler(errorLocation => - window.fetch( - `/__open-stack-frame-in-editor?fileName=` + - window.encodeURIComponent(errorLocation.fileName) + - `&lineNumber=` + - window.encodeURIComponent(errorLocation.lineNumber || 1) - ) - ) + showCompileError: overlayPackage.showCompileError, + clearCompileError: overlayPackage.clearCompileError, } const errorMap = {} diff --git a/packages/gatsby/cache-dir/root.js b/packages/gatsby/cache-dir/root.js index 1a62430b4ce08..1e3fb8c2674fe 100644 --- a/packages/gatsby/cache-dir/root.js +++ b/packages/gatsby/cache-dir/root.js @@ -12,32 +12,8 @@ import loader from "./loader" import { PageQueryStore, StaticQueryStore } from "./query-result-store" import EnsureResources from "./ensure-resources" import FastRefreshOverlay from "./fast-refresh-overlay" - -import { reportError, clearError } from "./error-overlay-handler" import { LoadingIndicatorEventHandler } from "./loading-indicator" -// TODO: Remove entire block when we make fast-refresh the default -// In fast-refresh, this logic is all moved into the `error-overlay-handler` -if ( - window.__webpack_hot_middleware_reporter__ !== undefined && - process.env.GATSBY_HOT_LOADER !== `fast-refresh` -) { - const overlayErrorID = `webpack` - // Report build errors - window.__webpack_hot_middleware_reporter__.useCustomOverlay({ - showProblems(type, obj) { - if (type !== `errors`) { - clearError(overlayErrorID) - return - } - reportError(overlayErrorID, obj[0]) - }, - clear() { - clearError(overlayErrorID) - }, - }) -} - navigationInit() // In gatsby v2 if Router is used in page using matchPaths @@ -137,20 +113,12 @@ const WrappedRoot = apiRunner( } ).pop() -const ConditionalFastRefreshOverlay = ({ children }) => { - if (process.env.GATSBY_HOT_LOADER === `fast-refresh`) { - return {children} - } - - return {children} -} - export default () => ( - + {WrappedRoot} {process.env.GATSBY_EXPERIMENTAL_QUERY_ON_DEMAND && process.env.GATSBY_QUERY_ON_DEMAND_LOADING_INDICATOR === `true` && ( )} - + ) diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index fc708739a45d5..17c51fd15ccb8 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -260,4 +260,4 @@ "yargs": { "boolean-negation": false } -} +} \ No newline at end of file diff --git a/packages/gatsby/src/bootstrap/requires-writer.ts b/packages/gatsby/src/bootstrap/requires-writer.ts index 93268a065e698..e384fd311b27d 100644 --- a/packages/gatsby/src/bootstrap/requires-writer.ts +++ b/packages/gatsby/src/bootstrap/requires-writer.ts @@ -208,26 +208,18 @@ export const writeAll = async (state: IGatsbyState): Promise => { lastHash = newHash - // TODO: Remove all "hot" references in this `syncRequires` variable when fast-refresh is the default - const hotImport = - process.env.GATSBY_HOT_LOADER !== `fast-refresh` - ? `const { hot } = require("react-hot-loader/root")` - : `` - const hotMethod = - process.env.GATSBY_HOT_LOADER !== `fast-refresh` ? `hot` : `` - if (process.env.GATSBY_EXPERIMENTAL_DEV_SSR) { // Create file with sync requires of visited page components files. - let lazySyncRequires = `${hotImport} + let lazySyncRequires = ` // prefer default export if available const preferDefault = m => (m && m.default) || m \n\n` lazySyncRequires += `exports.ssrComponents = {\n${cleanedSSRVisitedPageComponents .map( (c: IGatsbyPageComponent): string => - ` "${ - c.componentChunkName - }": ${hotMethod}(preferDefault(require("${joinPath(c.component)}")))` + ` "${c.componentChunkName}": preferDefault(require("${joinPath( + c.component + )}"))` ) .join(`,\n`)} }\n\n` @@ -236,17 +228,16 @@ export const writeAll = async (state: IGatsbyState): Promise => { } // Create file with sync requires of components/json files. - let syncRequires = `${hotImport} - + let syncRequires = ` // prefer default export if available const preferDefault = m => (m && m.default) || m \n\n` syncRequires += `exports.components = {\n${components .map( (c: IGatsbyPageComponent): string => - ` "${ - c.componentChunkName - }": ${hotMethod}(preferDefault(require("${joinPath(c.component)}")))` + ` "${c.componentChunkName}": preferDefault(require("${joinPath( + c.component + )}"))` ) .join(`,\n`)} }\n\n` diff --git a/packages/gatsby/src/services/initialize.ts b/packages/gatsby/src/services/initialize.ts index 03c5f1f07b3f2..a8e9f404aea35 100644 --- a/packages/gatsby/src/services/initialize.ts +++ b/packages/gatsby/src/services/initialize.ts @@ -19,7 +19,6 @@ import { loadPlugins } from "../bootstrap/load-plugins" import { store, emitter } from "../redux" import loadThemes from "../bootstrap/load-themes" import reporter from "gatsby-cli/lib/reporter" -import { getReactHotLoaderStrategy } from "../utils/get-react-hot-loader-strategy" import { getConfigFile } from "../bootstrap/get-config-file" import { removeStaleJobs } from "../bootstrap/remove-stale-jobs" import { IPluginInfoOptions } from "../bootstrap/load-plugins/types" @@ -165,24 +164,6 @@ export async function initialize({ // Setup flags if (config) { - // TODO: this should be handled in FAST_REFRESH configuration and not be one-off here. - if ( - config.flags?.FAST_REFRESH && - process.env.GATSBY_HOT_LOADER && - process.env.GATSBY_HOT_LOADER !== `fast-refresh` - ) { - delete config.flags.FAST_REFRESH - reporter.warn( - reporter.stripIndent(` - Both FAST_REFRESH gatsby-config flag and GATSBY_HOT_LOADER environment variable is used with conflicting setting ("${process.env.GATSBY_HOT_LOADER}"). - - Will use react-hot-loader. - - To use Fast Refresh either do not use GATSBY_HOT_LOADER environment variable or set it to "fast-refresh". - `) - ) - } - // Get flags const { enabledConfigFlags, unknownFlagMessage, message } = handleFlags( availableFlags, @@ -216,8 +197,6 @@ export async function initialize({ } } - process.env.GATSBY_HOT_LOADER = getReactHotLoaderStrategy() - // TODO: figure out proper way of disabling loading indicator // for now GATSBY_QUERY_ON_DEMAND_LOADING_INDICATOR=false gatsby develop // will work, but we don't want to force users into using env vars diff --git a/packages/gatsby/src/utils/babel-loader-helpers.js b/packages/gatsby/src/utils/babel-loader-helpers.js index 081f5058f0829..db580da2c186c 100644 --- a/packages/gatsby/src/utils/babel-loader-helpers.js +++ b/packages/gatsby/src/utils/babel-loader-helpers.js @@ -50,21 +50,11 @@ const prepareOptions = (babel, options = {}, resolve = require.resolve) => { } if (stage === `develop`) { - if (process.env.GATSBY_HOT_LOADER === `fast-refresh`) { - requiredPlugins.push( - babel.createConfigItem([resolve(`react-refresh/babel`)], { - type: `plugin`, - }) - ) - } - // TODO: Remove entire block when we make fast-refresh the default - else { - requiredPlugins.push( - babel.createConfigItem([resolve(`react-hot-loader/babel`)], { - type: `plugin`, - }) - ) - } + requiredPlugins.push( + babel.createConfigItem([resolve(`react-refresh/babel`)], { + type: `plugin`, + }) + ) } // Fallback preset diff --git a/packages/gatsby/src/utils/eslint/required.js b/packages/gatsby/src/utils/eslint/required.js index 7ac22d57db856..ab05990ed383a 100644 --- a/packages/gatsby/src/utils/eslint/required.js +++ b/packages/gatsby/src/utils/eslint/required.js @@ -1,9 +1,7 @@ module.exports = { rules: { // Custom ESLint rules from Gatsby - "no-anonymous-exports-page-templates": - process.env.GATSBY_HOT_LOADER === `fast-refresh` ? `warn` : `off`, - "limited-exports-page-templates": - process.env.GATSBY_HOT_LOADER === `fast-refresh` ? `warn` : `off`, + "no-anonymous-exports-page-templates": `warn`, + "limited-exports-page-templates": `warn`, }, } diff --git a/packages/gatsby/src/utils/flags.ts b/packages/gatsby/src/utils/flags.ts index d3b44affea052..c295a5a824207 100644 --- a/packages/gatsby/src/utils/flags.ts +++ b/packages/gatsby/src/utils/flags.ts @@ -156,16 +156,6 @@ const activeFlags: Array = [ umbrellaIssue: `https://gatsby.dev/cache-clearing-feedback`, testFitness: (): fitnessEnum => true, }, - { - name: `FAST_REFRESH`, - env: `GATSBY_FAST_REFRESH`, - command: `develop`, - telemetryId: `FastRefresh`, - experimental: false, - description: `Use React Fast Refresh instead of the legacy react-hot-loader for instantaneous feedback in your development server. Recommended for versions of React >= 17.0.`, - umbrellaIssue: `https://gatsby.dev/fast-refresh-feedback`, - testFitness: (): fitnessEnum => true, - }, { name: `PARALLEL_SOURCING`, env: `GATSBY_EXPERIMENTAL_PARALLEL_SOURCING`, diff --git a/packages/gatsby/src/utils/get-react-hot-loader-strategy.ts b/packages/gatsby/src/utils/get-react-hot-loader-strategy.ts deleted file mode 100644 index 3b817d05b6978..0000000000000 --- a/packages/gatsby/src/utils/get-react-hot-loader-strategy.ts +++ /dev/null @@ -1,27 +0,0 @@ -import semver from "semver" - -// Fast refresh is supported as of React 16.9. -// This package will do some sniffing to see if the current version of -// React installed is greater than 17.0. -export function getReactHotLoaderStrategy(): string { - // If the user has defined this, we don't want to do any package sniffing - if (process.env.GATSBY_HOT_LOADER) return process.env.GATSBY_HOT_LOADER - - // If the config flag is true, return fast-refresh - if (process.env.GATSBY_FAST_REFRESH) return `fast-refresh` - - // Do some package sniffing to see if we can use fast-refresh if the user didn't - // specify a specific hot loader with the environment variable. - - try { - const reactVersion = require(`react/package.json`).version - - if (semver.satisfies(reactVersion, `>=17.0.0`)) { - return `fast-refresh` - } - } catch (e) { - return `react-hot-loader` - } - - return `react-hot-loader` -} diff --git a/packages/gatsby/src/utils/webpack-hmr-hooks-patch.js b/packages/gatsby/src/utils/webpack-hmr-hooks-patch.js deleted file mode 100644 index 7aca2012242c2..0000000000000 --- a/packages/gatsby/src/utils/webpack-hmr-hooks-patch.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * This file should remain as JS because the migration to TypeScript break the patch. - * For more details, https://github.com/gatsbyjs/gatsby/pull/22280 - */ -const originalFetch = global.fetch -delete global.fetch - -module.exports = require(`react-hot-loader/webpack`) - -global.fetch = originalFetch diff --git a/packages/gatsby/src/utils/webpack.config.js b/packages/gatsby/src/utils/webpack.config.js index eb56d9ada0f25..9f847ede24eb2 100644 --- a/packages/gatsby/src/utils/webpack.config.js +++ b/packages/gatsby/src/utils/webpack.config.js @@ -172,13 +172,7 @@ module.exports = async ( case `develop`: return { polyfill: directoryPath(`.cache/polyfill-entry`), - commons: [ - process.env.GATSBY_HOT_LOADER !== `fast-refresh` && - `${require.resolve( - `webpack-hot-middleware/client` - )}?path=${getHmrPath()}`, - directoryPath(`.cache/app`), - ].filter(Boolean), + commons: [directoryPath(`.cache/app`)], } case `develop-html`: return { @@ -222,8 +216,7 @@ module.exports = async ( case `develop`: configPlugins = configPlugins .concat([ - process.env.GATSBY_HOT_LOADER === `fast-refresh` && - plugins.fastRefresh(), + plugins.fastRefresh(), plugins.hotModuleReplacement(), plugins.noEmitOnErrors(), plugins.eslintGraphqlSchemaReload(), @@ -265,9 +258,7 @@ module.exports = async ( function getDevtool() { switch (stage) { case `develop`: - return process.env.GATSBY_HOT_LOADER !== `fast-refresh` - ? `cheap-module-source-map` - : `eval-cheap-module-source-map` + return `eval-cheap-module-source-map` // use a normal `source-map` for the html phases since // it gives better line and column numbers case `develop-html`: @@ -350,10 +341,7 @@ module.exports = async ( } // Enforce fast-refresh rules even with local eslint config - if ( - isCustomEslint && - process.env.GATSBY_HOT_LOADER === `fast-refresh` - ) { + if (isCustomEslint) { configRules = configRules.concat([rules.eslintRequired()]) } @@ -363,19 +351,6 @@ module.exports = async ( }, ]) - // RHL will patch React, replace React-DOM by React-🔥-DOM and work with fiber directly - // It's necessary to remove the warning in console (https://github.com/gatsbyjs/gatsby/issues/11934) - // TODO: Remove entire block when we make fast-refresh the default - if (process.env.GATSBY_HOT_LOADER !== `fast-refresh`) { - configRules.push({ - include: /node_modules\/react-dom/, - test: /\.jsx?$/, - use: { - loader: require.resolve(`./webpack-hmr-hooks-patch`), - }, - }) - } - break } case `build-html`: @@ -433,12 +408,6 @@ module.exports = async ( // relative path imports are used sometimes // See https://stackoverflow.com/a/49455609/6420957 for more details "@babel/runtime": getPackageRoot(`@babel/runtime`), - // TODO: Remove entire block when we make fast-refresh the default - ...(process.env.GATSBY_HOT_LOADER !== `fast-refresh` - ? { - "react-hot-loader": getPackageRoot(`react-hot-loader`), - } - : {}), "react-lifecycles-compat": directoryPath( `.cache/react-lifecycles-compat.js` ),