vercel / next.js

The React Framework
https://nextjs.org
MIT License
121.28k stars 25.95k forks source link

fix(next/image): detect react@19 for `fetchPriority` prop #65235

Closed styfle closed 2 weeks ago

styfle commented 2 weeks ago

In a previous PR, https://github.com/vercel/next.js/pull/47302, detection for fetchPriority assumed that https://github.com/facebook/react/pull/25927 would land in react@18.3.0 because that was the react@canary version at the time. However, it didn't land in react@18.3.0 and it is expected to land in react@19.0.0 due to the breaking change.

This means that users upgrading to react@18.3.0 will see a warning.

The fix is to stop looking at the React.version string and instead check for React.use, a feature that will land in react@19.0.0 but is also available in react@canary and react@beta today.

Note: There were tests added for App Router and Pages Router in a previous PR https://github.com/vercel/next.js/pull/47302 but they seem to run on react@18.2.0 which is why we don't see failures.

Fixes https://github.com/vercel/next.js/issues/65161

Related https://github.com/facebook/react/issues/28946

ijjk commented 2 weeks ago

Stats from current PR

Default Build (Increase detected ⚠️)
General | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | buildDuration | 19.3s | 17.7s | N/A | | buildDurationCached | 10.1s | 9.3s | N/A | | nodeModulesSize | 360 MB | 360 MB | N/A | | nextStartRea..uration (ms) | 456ms | 450ms | N/A |
Client Bundles (main, webpack) | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | 1103-HASH.js gzip | 31.9 kB | 31.9 kB | N/A | | 1a9f679d-HASH.js gzip | 53.5 kB | 53.5 kB | N/A | | 335-HASH.js gzip | 5.09 kB | 5.05 kB | N/A | | 7953.HASH.js gzip | 169 B | 169 B | ✓ | | framework-HASH.js gzip | 45.2 kB | 45.2 kB | ✓ | | main-app-HASH.js gzip | 230 B | 228 B | N/A | | main-HASH.js gzip | 31.5 kB | 31.5 kB | N/A | | webpack-HASH.js gzip | 1.65 kB | 1.65 kB | N/A | | Overall change | 45.4 kB | 45.4 kB | ✓ |
Legacy Client Bundles (polyfills) | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | polyfills-HASH.js gzip | 31 kB | 31 kB | ✓ | | Overall change | 31 kB | 31 kB | ✓ |
Client Pages | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | _app-HASH.js gzip | 192 B | 193 B | N/A | | _error-HASH.js gzip | 192 B | 193 B | N/A | | amp-HASH.js gzip | 507 B | 511 B | N/A | | css-HASH.js gzip | 341 B | 343 B | N/A | | dynamic-HASH.js gzip | 2.52 kB | 2.52 kB | ✓ | | edge-ssr-HASH.js gzip | 266 B | 265 B | N/A | | head-HASH.js gzip | 362 B | 365 B | N/A | | hooks-HASH.js gzip | 392 B | 392 B | ✓ | | image-HASH.js gzip | 4.32 kB | 4.27 kB | N/A | | index-HASH.js gzip | 268 B | 268 B | ✓ | | link-HASH.js gzip | 2.69 kB | 2.7 kB | N/A | | routerDirect..HASH.js gzip | 329 B | 328 B | N/A | | script-HASH.js gzip | 397 B | 397 B | ✓ | | withRouter-HASH.js gzip | 324 B | 324 B | ✓ | | 1afbb74e6ecf..834.css gzip | 106 B | 106 B | ✓ | | Overall change | 4 kB | 4 kB | ✓ |
Client Build Manifests | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | _buildManifest.js gzip | 483 B | 484 B | N/A | | Overall change | 0 B | 0 B | ✓ |
Rendered Page Sizes | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | index.html gzip | 529 B | 528 B | N/A | | link.html gzip | 541 B | 540 B | N/A | | withRouter.html gzip | 524 B | 524 B | ✓ | | Overall change | 524 B | 524 B | ✓ |
Edge SSR bundle Size | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | edge-ssr.js gzip | 94.7 kB | 94.7 kB | ✓ | | page.js gzip | 181 kB | 181 kB | N/A | | Overall change | 94.7 kB | 94.7 kB | ✓ |
Middleware size | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | middleware-b..fest.js gzip | 623 B | 623 B | ✓ | | middleware-r..fest.js gzip | 156 B | 156 B | ✓ | | middleware.js gzip | 25.7 kB | 25.7 kB | N/A | | edge-runtime..pack.js gzip | 839 B | 839 B | ✓ | | Overall change | 1.62 kB | 1.62 kB | ✓ |
Next Runtimes | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | app-page-exp...dev.js gzip | 171 kB | 171 kB | ✓ | | app-page-exp..prod.js gzip | 98.5 kB | 98.5 kB | ✓ | | app-page-tur..prod.js gzip | 100 kB | 100 kB | ✓ | | app-page-tur..prod.js gzip | 94.4 kB | 94.4 kB | ✓ | | app-page.run...dev.js gzip | 157 kB | 157 kB | ✓ | | app-page.run..prod.js gzip | 93.1 kB | 93.1 kB | ✓ | | app-route-ex...dev.js gzip | 21.5 kB | 21.5 kB | ✓ | | app-route-ex..prod.js gzip | 15.2 kB | 15.2 kB | ✓ | | app-route-tu..prod.js gzip | 15.2 kB | 15.2 kB | ✓ | | app-route-tu..prod.js gzip | 15 kB | 15 kB | ✓ | | app-route.ru...dev.js gzip | 21.3 kB | 21.3 kB | ✓ | | app-route.ru..prod.js gzip | 15 kB | 15 kB | ✓ | | pages-api-tu..prod.js gzip | 9.55 kB | 9.55 kB | ✓ | | pages-api.ru...dev.js gzip | 9.82 kB | 9.82 kB | ✓ | | pages-api.ru..prod.js gzip | 9.55 kB | 9.55 kB | ✓ | | pages-turbo...prod.js gzip | 21.5 kB | 21.5 kB | ✓ | | pages.runtim...dev.js gzip | 22.1 kB | 22.1 kB | ✓ | | pages.runtim..prod.js gzip | 21.5 kB | 21.5 kB | ✓ | | server.runti..prod.js gzip | 51.6 kB | 51.6 kB | ✓ | | Overall change | 963 kB | 963 kB | ✓ |
build cache Overall increase ⚠️ | | vercel/next.js canary | vercel/next.js styfle/next-3273-react-does-not-recognize-the-fetchpriority-prop-on-a-dom | Change | | - | - | - | - | | 0.pack gzip | 1.62 MB | 1.62 MB | ⚠️ +4.26 kB | | index.pack gzip | 113 kB | 113 kB | ⚠️ +365 B | | Overall change | 1.73 MB | 1.74 MB | ⚠️ +4.63 kB |
Diff details
Diff for edge-ssr.js Diff too large to display
Diff for image-HASH.js ```diff @@ -1,7 +1,7 @@ (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([ [8358], { - /***/ 7935: /***/ ( + /***/ 1058: /***/ ( __unused_webpack_module, __unused_webpack_exports, __webpack_require__ @@ -9,7 +9,7 @@ (window.__NEXT_P = window.__NEXT_P || []).push([ "/image", function () { - return __webpack_require__(7013); + return __webpack_require__(4286); }, ]); if (false) { @@ -18,7 +18,7 @@ /***/ }, - /***/ 7890: /***/ (module, exports, __webpack_require__) => { + /***/ 6659: /***/ (module, exports, __webpack_require__) => { "use strict"; /* __next_internal_client_entry_do_not_use__ cjs */ Object.defineProperty(exports, "__esModule", { @@ -40,15 +40,15 @@ __webpack_require__(967) ); const _head = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(5820) + __webpack_require__(8338) ); - const _getimgprops = __webpack_require__(829); - const _imageconfig = __webpack_require__(1247); - const _imageconfigcontextsharedruntime = __webpack_require__(6709); - const _warnonce = __webpack_require__(928); - const _routercontextsharedruntime = __webpack_require__(3546); + const _getimgprops = __webpack_require__(1664); + const _imageconfig = __webpack_require__(6442); + const _imageconfigcontextsharedruntime = __webpack_require__(983); + const _warnonce = __webpack_require__(2615); + const _routercontextsharedruntime = __webpack_require__(397); const _imageloader = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(7301) + __webpack_require__(4085) ); // This is replaced by webpack define plugin const configEnv = { @@ -129,11 +129,8 @@ }); } function getDynamicProps(fetchPriority) { - const [majorStr, minorStr] = _react.version.split(".", 2); - const major = parseInt(majorStr, 10); - const minor = parseInt(minorStr, 10); - if (major > 18 || (major === 18 && minor >= 3)) { - // In React 18.3.0 or newer, we must use camelCase + if (Boolean(_react.use)) { + // In React 19.0.0 or newer, we must use camelCase // prop to avoid "Warning: Invalid DOM property". // See https://github.com/facebook/react/pull/25927 return { @@ -379,7 +376,7 @@ /***/ }, - /***/ 829: /***/ ( + /***/ 1664: /***/ ( __unused_webpack_module, exports, __webpack_require__ @@ -395,9 +392,9 @@ return getImgProps; }, }); - const _warnonce = __webpack_require__(928); - const _imageblursvg = __webpack_require__(6319); - const _imageconfig = __webpack_require__(1247); + const _warnonce = __webpack_require__(2615); + const _imageblursvg = __webpack_require__(1808); + const _imageconfig = __webpack_require__(6442); const VALID_LOADING_VALUES = /* unused pure expression or super */ null && [ "lazy", @@ -772,7 +769,7 @@ /***/ }, - /***/ 6319: /***/ (__unused_webpack_module, exports) => { + /***/ 1808: /***/ (__unused_webpack_module, exports) => { "use strict"; /** * A shared function, used on both client and server, to generate a SVG blur placeholder. @@ -827,7 +824,7 @@ /***/ }, - /***/ 3484: /***/ ( + /***/ 8265: /***/ ( __unused_webpack_module, exports, __webpack_require__ @@ -854,10 +851,10 @@ }, }); const _interop_require_default = __webpack_require__(1478); - const _getimgprops = __webpack_require__(829); - const _imagecomponent = __webpack_require__(7890); + const _getimgprops = __webpack_require__(1664); + const _imagecomponent = __webpack_require__(6659); const _imageloader = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(7301) + __webpack_require__(4085) ); function getImageProps(imgProps) { const { props } = (0, _getimgprops.getImgProps)(imgProps, { @@ -889,7 +886,7 @@ /***/ }, - /***/ 7301: /***/ (__unused_webpack_module, exports) => { + /***/ 4085: /***/ (__unused_webpack_module, exports) => { "use strict"; Object.defineProperty(exports, "__esModule", { @@ -924,7 +921,7 @@ /***/ }, - /***/ 7013: /***/ ( + /***/ 4286: /***/ ( __unused_webpack_module, __webpack_exports__, __webpack_require__ @@ -941,8 +938,8 @@ // EXTERNAL MODULE: ./node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js var jsx_runtime = __webpack_require__(2676); - // EXTERNAL MODULE: ./node_modules/.pnpm/file+..+main-repo+packages+next+next-packed.tgz_react-dom@18.3.1_react@18.3.1/node_modules/next/image.js - var next_image = __webpack_require__(4841); + // EXTERNAL MODULE: ./node_modules/.pnpm/file+..+diff-repo+packages+next+next-packed.tgz_react-dom@18.3.1_react@18.3.1/node_modules/next/image.js + var next_image = __webpack_require__(7053); var image_default = /*#__PURE__*/ __webpack_require__.n(next_image); // CONCATENATED MODULE: ./pages/nextjs.png /* harmony default export */ const nextjs = { src: "/_next/static/media/nextjs.cae0b805.png", @@ -972,12 +969,12 @@ /***/ }, - /***/ 4841: /***/ ( + /***/ 7053: /***/ ( module, __unused_webpack_exports, __webpack_require__ ) => { - module.exports = __webpack_require__(3484); + module.exports = __webpack_require__(8265); /***/ }, @@ -987,7 +984,7 @@ /******/ var __webpack_exec__ = (moduleId) => __webpack_require__((__webpack_require__.s = moduleId)); /******/ __webpack_require__.O(0, [2888, 9774, 179], () => - __webpack_exec__(7935) + __webpack_exec__(1058) ); /******/ var __webpack_exports__ = __webpack_require__.O(); /******/ _N_E = __webpack_exports__; ```
Diff for 335-HASH.js ```diff @@ -1,8 +1,8 @@ "use strict"; (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([ - [335], + [8982], { - /***/ 335: /***/ (module, exports, __webpack_require__) => { + /***/ 8982: /***/ (module, exports, __webpack_require__) => { /* __next_internal_client_entry_do_not_use__ cjs */ Object.defineProperty(exports, "__esModule", { value: true, @@ -13,25 +13,25 @@ return Image; }, }); - const _interop_require_default = __webpack_require__(5113); - const _interop_require_wildcard = __webpack_require__(9036); - const _jsxruntime = __webpack_require__(7059); + const _interop_require_default = __webpack_require__(1741); + const _interop_require_wildcard = __webpack_require__(4397); + const _jsxruntime = __webpack_require__(8588); const _react = /*#__PURE__*/ _interop_require_wildcard._( - __webpack_require__(6307) + __webpack_require__(8230) ); const _reactdom = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(4430) + __webpack_require__(4690) ); const _head = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(1500) + __webpack_require__(7332) ); - const _getimgprops = __webpack_require__(281); - const _imageconfig = __webpack_require__(5808); - const _imageconfigcontextsharedruntime = __webpack_require__(4647); - const _warnonce = __webpack_require__(646); - const _routercontextsharedruntime = __webpack_require__(5861); + const _getimgprops = __webpack_require__(1308); + const _imageconfig = __webpack_require__(4709); + const _imageconfigcontextsharedruntime = __webpack_require__(4366); + const _warnonce = __webpack_require__(3834); + const _routercontextsharedruntime = __webpack_require__(1436); const _imageloader = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(6526) + __webpack_require__(2235) ); // This is replaced by webpack define plugin const configEnv = { @@ -113,11 +113,8 @@ }); } function getDynamicProps(fetchPriority) { - const [majorStr, minorStr] = _react.version.split(".", 2); - const major = parseInt(majorStr, 10); - const minor = parseInt(minorStr, 10); - if (major > 18 || (major === 18 && minor >= 3)) { - // In React 18.3.0 or newer, we must use camelCase + if (Boolean(_react.use)) { + // In React 19.0.0 or newer, we must use camelCase // prop to avoid "Warning: Invalid DOM property". // See https://github.com/facebook/react/pull/25927 return { @@ -363,7 +360,7 @@ /***/ }, - /***/ 9032: /***/ ( + /***/ 3211: /***/ ( __unused_webpack_module, exports, __webpack_require__ @@ -377,9 +374,9 @@ return AmpStateContext; }, }); - const _interop_require_default = __webpack_require__(5113); + const _interop_require_default = __webpack_require__(1741); const _react = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(6307) + __webpack_require__(8230) ); const AmpStateContext = _react.default.createContext({}); if (false) { @@ -388,7 +385,7 @@ /***/ }, - /***/ 5607: /***/ (__unused_webpack_module, exports) => { + /***/ 7873: /***/ (__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", { value: true, }); @@ -410,7 +407,7 @@ /***/ }, - /***/ 281: /***/ ( + /***/ 1308: /***/ ( __unused_webpack_module, exports, __webpack_require__ @@ -424,9 +421,9 @@ return getImgProps; }, }); - const _warnonce = __webpack_require__(646); - const _imageblursvg = __webpack_require__(1258); - const _imageconfig = __webpack_require__(5808); + const _warnonce = __webpack_require__(3834); + const _imageblursvg = __webpack_require__(8181); + const _imageconfig = __webpack_require__(4709); const VALID_LOADING_VALUES = /* unused pure expression or super */ null && [ "lazy", @@ -801,7 +798,7 @@ /***/ }, - /***/ 1500: /***/ (module, exports, __webpack_require__) => { + /***/ 7332: /***/ (module, exports, __webpack_require__) => { /* __next_internal_client_entry_do_not_use__ cjs */ Object.defineProperty(exports, "__esModule", { value: true, @@ -822,19 +819,19 @@ return defaultHead; }, }); - const _interop_require_default = __webpack_require__(5113); - const _interop_require_wildcard = __webpack_require__(9036); - const _jsxruntime = __webpack_require__(7059); + const _interop_require_default = __webpack_require__(1741); + const _interop_require_wildcard = __webpack_require__(4397); + const _jsxruntime = __webpack_require__(8588); const _react = /*#__PURE__*/ _interop_require_wildcard._( - __webpack_require__(6307) + __webpack_require__(8230) ); const _sideeffect = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(2511) + __webpack_require__(2032) ); - const _ampcontextsharedruntime = __webpack_require__(9032); - const _headmanagercontextsharedruntime = __webpack_require__(4779); - const _ampmode = __webpack_require__(5607); - const _warnonce = __webpack_require__(646); + const _ampcontextsharedruntime = __webpack_require__(3211); + const _headmanagercontextsharedruntime = __webpack_require__(9165); + const _ampmode = __webpack_require__(7873); + const _warnonce = __webpack_require__(3834); function defaultHead(inAmpMode) { if (inAmpMode === void 0) inAmpMode = false; const head = [ @@ -1010,7 +1007,7 @@ /***/ }, - /***/ 1258: /***/ (__unused_webpack_module, exports) => { + /***/ 8181: /***/ (__unused_webpack_module, exports) => { /** * A shared function, used on both client and server, to generate a SVG blur placeholder. */ @@ -1064,7 +1061,7 @@ /***/ }, - /***/ 4647: /***/ ( + /***/ 4366: /***/ ( __unused_webpack_module, exports, __webpack_require__ @@ -1078,11 +1075,11 @@ return ImageConfigContext; }, }); - const _interop_require_default = __webpack_require__(5113); + const _interop_require_default = __webpack_require__(1741); const _react = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(6307) + __webpack_require__(8230) ); - const _imageconfig = __webpack_require__(5808); + const _imageconfig = __webpack_require__(4709); const ImageConfigContext = _react.default.createContext( _imageconfig.imageConfigDefault ); @@ -1092,7 +1089,7 @@ /***/ }, - /***/ 5808: /***/ (__unused_webpack_module, exports) => { + /***/ 4709: /***/ (__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", { value: true, }); @@ -1139,7 +1136,7 @@ /***/ }, - /***/ 6526: /***/ (__unused_webpack_module, exports) => { + /***/ 2235: /***/ (__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", { value: true, }); @@ -1172,7 +1169,7 @@ /***/ }, - /***/ 5861: /***/ ( + /***/ 1436: /***/ ( __unused_webpack_module, exports, __webpack_require__ @@ -1186,9 +1183,9 @@ return RouterContext; }, }); - const _interop_require_default = __webpack_require__(5113); + const _interop_require_default = __webpack_require__(1741); const _react = /*#__PURE__*/ _interop_require_default._( - __webpack_require__(6307) + __webpack_require__(8230) ); const RouterContext = _react.default.createContext(null); if (false) { @@ -1197,7 +1194,7 @@ /***/ }, - /***/ 2511: /***/ ( + /***/ 2032: /***/ ( __unused_webpack_module, exports, __webpack_require__ @@ -1211,7 +1208,7 @@ return SideEffect; }, }); - const _react = __webpack_require__(6307); + const _react = __webpack_require__(8230); const isServer = typeof window === "undefined"; const useClientOnlyLayoutEffect = isServer ? () => {} ```

Commit: 91434c6f2d217b931849d7d7739a6ae9e14a4a67

ijjk commented 2 weeks ago

Failing test suites

Commit: 0c0f8329096a6e9f54fe3a66d00d04224e93b461

pnpm test-start test/e2e/basepath.test.ts

Expand output ● basePath › should navigate to external site and back page.waitForSelector: Timeout 60000ms exceeded. Call log: - waiting for locator('input') 421 | return this.chain(() => { 422 | return page > 423 | .waitForSelector(selector, { timeout, state: 'attached' }) | ^ 424 | .then(async (el) => { 425 | // it seems selenium waits longer and tests rely on this behavior 426 | // so we wait for the load event fire before returning at waitForSelector (lib/browsers/playwright.ts:423:10) at Object. (e2e/basepath.test.ts:135:7)

Read more about building and testing Next.js in contributing.md.

TURBOPACK=1 pnpm test-start test/production/pages-dir/production/test/index.test.ts (turbopack)

Expand output ● Production Usage › should navigate to external site and back thrown: "Exceeded timeout of 60000 ms for a test. Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout." 730 | }) 731 | > 732 | it('should navigate to external site and back', async () => { | ^ 733 | const browser = await webdriver(next.appPort, '/external-and-back') 734 | const initialText = await browser.elementByCss('p').text() 735 | expect(initialText).toBe('server') at it (production/pages-dir/production/test/index.test.ts:732:3) at Object.describe (production/pages-dir/production/test/index.test.ts:32:1) ● Production Usage › should navigate to external site and back (with query) thrown: "Exceeded timeout of 60000 ms for a test. Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout." 764 | }) 765 | > 766 | it('should navigate to external site and back (with query)', async () => { | ^ 767 | const browser = await webdriver( 768 | next.appPort, 769 | '/external-and-back?hello=world' at it (production/pages-dir/production/test/index.test.ts:766:3) at Object.describe (production/pages-dir/production/test/index.test.ts:32:1)

Read more about building and testing Next.js in contributing.md.

styfle commented 2 weeks ago

Looks like lint is failing because PR https://github.com/vercel/next.js/pull/61291 was force merged.

I'm going to force merge this one to unblock and lint can be fixed in a new PR.

ingridkindem commented 2 days ago

I still get the error with next 14.1.4 and react ^18.2.0. Running next dev, not jest.

styfle commented 2 days ago

See https://github.com/vercel/next.js/issues/65161#issuecomment-2093626579 and also https://github.com/vercel/next.js/issues/65161#issuecomment-2101485312