From 2ed7144e8dd6e9eaea2519914affa03443666cc9 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Thu, 21 Mar 2024 12:49:20 +0100 Subject: [PATCH 1/4] Install React 19 types --- package.json | 4 ++-- pnpm-lock.yaml | 60 +++++++++++++++++++++++--------------------------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index b12733f7562b7..335fe7fcae435 100644 --- a/package.json +++ b/package.json @@ -247,8 +247,8 @@ "@babel/parser": "7.22.5", "@babel/types": "7.22.5", "@babel/traverse": "7.22.5", - "@types/react": "18.2.37", - "@types/react-dom": "18.2.15" + "@types/react": "npm:types-react@19.0.0", + "@types/react-dom": "npm:types-react-dom@19.0.0" }, "engines": { "node": ">=18.17.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 672fc2037b347..08796bce3cc18 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,8 +13,8 @@ overrides: '@babel/parser': 7.22.5 '@babel/types': 7.22.5 '@babel/traverse': 7.22.5 - '@types/react': 18.2.37 - '@types/react-dom': 18.2.15 + '@types/react': npm:types-react@19.0.0 + '@types/react-dom': npm:types-react-dom@19.0.0 importers: @@ -52,7 +52,7 @@ importers: version: 11.11.0 '@emotion/react': specifier: 11.11.1 - version: 11.11.1(@types/react@18.2.37)(react@18.2.0) + version: 11.11.1(react@18.2.0)(types-react@19.0.0) '@fullhuman/postcss-purgecss': specifier: 1.3.0 version: 1.3.0 @@ -156,11 +156,11 @@ importers: specifier: 2.6.1 version: 2.6.1 '@types/react': - specifier: 18.2.37 - version: 18.2.37 + specifier: npm:types-react@19.0.0 + version: /types-react@19.0.0 '@types/react-dom': - specifier: 18.2.15 - version: 18.2.15 + specifier: npm:types-react-dom@19.0.0 + version: /types-react-dom@19.0.0 '@types/relay-runtime': specifier: 14.1.13 version: 14.1.13 @@ -1029,11 +1029,11 @@ importers: specifier: 1.3.4 version: 1.3.4 '@types/react': - specifier: 18.2.37 - version: 18.2.37 + specifier: npm:types-react@19.0.0 + version: /types-react@19.0.0 '@types/react-dom': - specifier: 18.2.15 - version: 18.2.15 + specifier: npm:types-react-dom@19.0.0 + version: /types-react-dom@19.0.0 '@types/react-is': specifier: 17.0.3 version: 17.0.3 @@ -3621,7 +3621,7 @@ packages: resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} dev: true - /@emotion/react@11.11.1(@types/react@18.2.37)(react@18.2.0): + /@emotion/react@11.11.1(react@18.2.0)(types-react@19.0.0): resolution: {integrity: sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==} peerDependencies: '@types/react': '*' @@ -3637,7 +3637,7 @@ packages: '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) '@emotion/utils': 1.2.1 '@emotion/weak-memoize': 0.3.1 - '@types/react': 18.2.37 + '@types/react': /types-react@19.0.0 hoist-non-react-statics: 3.3.2 react: 18.2.0 dev: true @@ -5320,7 +5320,7 @@ packages: react: '>=16' dependencies: '@types/mdx': 2.0.3 - '@types/react': 18.2.37 + '@types/react': /types-react@19.0.0 react: 18.2.0 /@mswjs/cookies@0.2.2: @@ -6607,7 +6607,7 @@ packages: dependencies: '@babel/runtime': 7.22.5 '@testing-library/dom': 8.20.0 - '@types/react-dom': 18.2.15 + '@types/react-dom': /types-react-dom@19.0.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true @@ -7042,9 +7042,6 @@ packages: kleur: 3.0.3 dev: true - /@types/prop-types@15.7.8: - resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==} - /@types/q@1.5.2: resolution: {integrity: sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==} dev: true @@ -7057,25 +7054,12 @@ packages: resolution: {integrity: sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==} dev: true - /@types/react-dom@18.2.15: - resolution: {integrity: sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==} - dependencies: - '@types/react': 18.2.37 - dev: true - /@types/react-is@17.0.3: resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} dependencies: - '@types/react': 18.2.37 + '@types/react': /types-react@19.0.0 dev: true - /@types/react@18.2.37: - resolution: {integrity: sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==} - dependencies: - '@types/prop-types': 15.7.8 - '@types/scheduler': 0.16.4 - csstype: 3.1.2 - /@types/relay-runtime@14.1.13: resolution: {integrity: sha512-NODqEnGjERJr02M0YQclUnXWCldmerNUkpFfuO317h/od1uXuwAW5131vpeiROE11BizPC/Qhup5VrwKsENazw==} dev: true @@ -24181,6 +24165,18 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true + /types-react-dom@19.0.0: + resolution: {integrity: sha512-qtWBYduBCmrgf4wqnTrGttEJJSZ6cTGmzuvmWd/34TJNKz/Pp69Islc1+txHF3nRxSuRvmhxztFXvLyxhp8G2w==} + dependencies: + '@types/react': /types-react@19.0.0 + dev: true + + /types-react@19.0.0: + resolution: {integrity: sha512-4hwzwaxNhdHXNhGJ819IMghtTlc/NcUGHcH1IhIU0WOQLKhzRldCJdTga+CvzTt2ugyLw0IBkhoaOsXssAPeFg==} + dependencies: + '@types/scheduler': 0.16.4 + csstype: 3.1.2 + /typescript@4.8.2: resolution: {integrity: sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==} engines: {node: '>=4.2.0'} From 3a9540c3954976416e0e154f0f894b0cd1b1bbc4 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Thu, 21 Mar 2024 13:35:19 +0100 Subject: [PATCH 2/4] Apply default `preset-19` to packages,test ```bash $ npx types-react-codemod preset-19 packages --ignore-pattern "**/{node_modules,dist,.turbo}/**" Processing 1304 files... Results: 0 errors 1278 unmodified 0 skipped 26 ok $ npx types-react-codemod preset-19 test --ignore-pattern "**/{node_modules,dist,.turbo}/**" Processing 1687 files... Results: 0 errors 1666 unmodified 1 skipped 20 ok ``` --- .../client/components/app-router-announcer.tsx | 2 +- .../src/client/components/error-boundary.tsx | 2 +- .../src/client/components/layout-router.tsx | 17 +++++++++-------- .../internal/helpers/use-websocket.ts | 2 +- .../components/render-from-template-context.tsx | 2 +- .../router-reducer/ppr-navigations.ts | 2 +- .../components/use-reducer-with-devtools.ts | 4 ++-- packages/next/src/client/head-manager.ts | 1 + packages/next/src/client/index.tsx | 2 +- packages/next/src/client/legacy/image.tsx | 3 ++- packages/next/src/client/script.tsx | 2 +- packages/next/src/client/use-intersection.tsx | 2 +- packages/next/src/client/with-router.tsx | 2 +- packages/next/src/pages/_document.tsx | 7 ++----- .../next/src/server/app-render/app-render.tsx | 2 +- .../make-get-server-inserted-html.tsx | 2 +- .../server/app-render/server-inserted-html.tsx | 2 +- .../server/app-render/static/static-renderer.ts | 2 ++ packages/next/src/server/render.tsx | 2 +- packages/next/src/shared/lib/app-dynamic.tsx | 2 +- packages/next/src/shared/lib/dynamic.tsx | 2 +- packages/next/src/shared/lib/get-img-props.ts | 2 ++ packages/next/src/shared/lib/head.tsx | 6 +++--- .../shared/lib/html-context.shared-runtime.ts | 4 ++-- packages/next/src/shared/lib/side-effect.tsx | 2 +- packages/next/src/shared/lib/utils.ts | 4 ++-- packages/next/types/react-dom.d.ts | 1 + test/e2e/app-dir/mdx/types/mdx.d.ts | 1 + .../new-link-behavior/typescript/pages/ref.tsx | 2 +- .../typescript-baseurl/components/hi.tsx | 2 +- .../typescript-baseurl/components/world.tsx | 2 +- .../typescript-baseurl/pages/hello.tsx | 2 +- .../project/components/world.tsx | 2 +- .../project/pages/index.tsx | 2 +- .../shared/components/counter.tsx | 2 +- .../typescript-paths/components/world.tsx | 2 +- .../typescript-paths/pages/alias-to-d-ts.tsx | 2 +- .../typescript-paths/pages/basic-alias.tsx | 2 +- .../typescript-paths/pages/resolve-fallback.tsx | 2 +- .../typescript-paths/pages/resolve-order.tsx | 2 +- .../packages/www/components/world.tsx | 2 +- .../packages/www/pages/alias-to-d-ts.tsx | 2 +- .../packages/www/pages/basic-alias.tsx | 2 +- .../packages/www/pages/resolve-fallback.tsx | 2 +- .../packages/www/pages/resolve-order.tsx | 2 +- .../integration/typescript/components/world.tsx | 2 +- test/integration/typescript/pages/hello.tsx | 2 +- 47 files changed, 64 insertions(+), 58 deletions(-) diff --git a/packages/next/src/client/components/app-router-announcer.tsx b/packages/next/src/client/components/app-router-announcer.tsx index b804e72d90add..d241be05ceea4 100644 --- a/packages/next/src/client/components/app-router-announcer.tsx +++ b/packages/next/src/client/components/app-router-announcer.tsx @@ -42,7 +42,7 @@ export function AppRouterAnnouncer({ tree }: { tree: FlightRouterState }) { }, []) const [routeAnnouncement, setRouteAnnouncement] = useState('') - const previousTitle = useRef() + const previousTitle = useRef(undefined) useEffect(() => { let currentTitle = '' diff --git a/packages/next/src/client/components/error-boundary.tsx b/packages/next/src/client/components/error-boundary.tsx index ad661efaac03c..6d323c00a4cbd 100644 --- a/packages/next/src/client/components/error-boundary.tsx +++ b/packages/next/src/client/components/error-boundary.tsx @@ -1,6 +1,6 @@ 'use client' -import React from 'react' +import React, { type JSX } from 'react' import { usePathname } from './navigation' import { isNextRouterError } from './is-next-router-error' diff --git a/packages/next/src/client/components/layout-router.tsx b/packages/next/src/client/components/layout-router.tsx index b182486d025ba..c9807a34e66f6 100644 --- a/packages/next/src/client/components/layout-router.tsx +++ b/packages/next/src/client/components/layout-router.tsx @@ -18,6 +18,7 @@ import React, { startTransition, Suspense, useDeferredValue, + type JSX, } from 'react' import ReactDOM from 'react-dom' import { @@ -563,14 +564,14 @@ export default function OuterLayoutRouter({ return ( /* - - Error boundary - - Only renders error boundary if error component is provided. - - Rendered for each segment to ensure they have their own error state. - - Loading boundary - - Only renders suspense boundary if loading components is provided. - - Rendered for each segment to ensure they have their own loading state. - - Passed to the router during rendering to ensure it can be immediately rendered when suspending on a Flight fetch. - */ + - Error boundary + - Only renders error boundary if error component is provided. + - Rendered for each segment to ensure they have their own error state. + - Loading boundary + - Only renders suspense boundary if loading components is provided. + - Rendered for each segment to ensure they have their own loading state. + - Passed to the router during rendering to ensure it can be immediately rendered when suspending on a Flight fetch. + */ () + const webSocketRef = useRef(undefined) useEffect(() => { if (webSocketRef.current) { diff --git a/packages/next/src/client/components/render-from-template-context.tsx b/packages/next/src/client/components/render-from-template-context.tsx index c1755cc5056bf..e07d353ee20e0 100644 --- a/packages/next/src/client/components/render-from-template-context.tsx +++ b/packages/next/src/client/components/render-from-template-context.tsx @@ -1,6 +1,6 @@ 'use client' -import React, { useContext } from 'react' +import React, { useContext, type JSX } from 'react' import { TemplateContext } from '../../shared/lib/app-router-context.shared-runtime' export default function RenderFromTemplateContext(): JSX.Element { diff --git a/packages/next/src/client/components/router-reducer/ppr-navigations.ts b/packages/next/src/client/components/router-reducer/ppr-navigations.ts index 35f975d0df587..891a343763eec 100644 --- a/packages/next/src/client/components/router-reducer/ppr-navigations.ts +++ b/packages/next/src/client/components/router-reducer/ppr-navigations.ts @@ -789,7 +789,7 @@ type FulfilledDeferredRsc = Promise & { tag: Symbol } -type RejectedDeferredRsc = Promise & { +type RejectedDeferredRsc = Promise> & { status: 'rejected' reason: any resolve: (value: React.ReactNode) => void diff --git a/packages/next/src/client/components/use-reducer-with-devtools.ts b/packages/next/src/client/components/use-reducer-with-devtools.ts index dd75194c63a3b..7df9d713547b6 100644 --- a/packages/next/src/client/components/use-reducer-with-devtools.ts +++ b/packages/next/src/client/components/use-reducer-with-devtools.ts @@ -103,8 +103,8 @@ function useReducerWithReduxDevtoolsImpl( throw new Error('Invariant: Missing ActionQueueContext') } - const devtoolsConnectionRef = useRef() - const enabledRef = useRef() + const devtoolsConnectionRef = useRef(undefined) + const enabledRef = useRef(undefined) useEffect(() => { if (devtoolsConnectionRef.current || enabledRef.current === false) { diff --git a/packages/next/src/client/head-manager.ts b/packages/next/src/client/head-manager.ts index 5d9bc835653af..4ae20e857950e 100644 --- a/packages/next/src/client/head-manager.ts +++ b/packages/next/src/client/head-manager.ts @@ -1,3 +1,4 @@ +import type { JSX } from 'react' export const DOMAttributeNames: Record = { acceptCharset: 'accept-charset', className: 'class', diff --git a/packages/next/src/client/index.tsx b/packages/next/src/client/index.tsx index dc5ba991c6450..b7731d3cd3d1b 100644 --- a/packages/next/src/client/index.tsx +++ b/packages/next/src/client/index.tsx @@ -8,7 +8,7 @@ import type { PrivateRouteInfo, } from '../shared/lib/router/router' -import React from 'react' +import React, { type JSX } from 'react' import ReactDOM from 'react-dom/client' import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' import mitt from '../shared/lib/mitt' diff --git a/packages/next/src/client/legacy/image.tsx b/packages/next/src/client/legacy/image.tsx index d982e9fa8a330..45eecc7fe89d4 100644 --- a/packages/next/src/client/legacy/image.tsx +++ b/packages/next/src/client/legacy/image.tsx @@ -7,6 +7,7 @@ import React, { useContext, useMemo, useState, + type JSX, } from 'react' import Head from '../../shared/lib/head' import { @@ -258,7 +259,7 @@ export type ImageProps = Omit< quality?: SafeNumber priority?: boolean loading?: LoadingValue - lazyRoot?: React.RefObject | null + lazyRoot?: React.RefObject | null lazyBoundary?: string placeholder?: PlaceholderValue blurDataURL?: string diff --git a/packages/next/src/client/script.tsx b/packages/next/src/client/script.tsx index 00c74defd15cb..069626b6d118f 100644 --- a/packages/next/src/client/script.tsx +++ b/packages/next/src/client/script.tsx @@ -1,7 +1,7 @@ 'use client' import ReactDOM from 'react-dom' -import React, { useEffect, useContext, useRef } from 'react' +import React, { useEffect, useContext, useRef, type JSX } from 'react' import type { ScriptHTMLAttributes } from 'react' import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' import { DOMAttributeNames } from './head-manager' diff --git a/packages/next/src/client/use-intersection.tsx b/packages/next/src/client/use-intersection.tsx index 27562ad966bbc..116198c66f663 100644 --- a/packages/next/src/client/use-intersection.tsx +++ b/packages/next/src/client/use-intersection.tsx @@ -10,7 +10,7 @@ type UseIntersectionObserverInit = Pick< > type UseIntersection = { disabled?: boolean } & UseIntersectionObserverInit & { - rootRef?: React.RefObject | null + rootRef?: React.RefObject | null } type ObserveCallback = (isVisible: boolean) => void type Identifier = { diff --git a/packages/next/src/client/with-router.tsx b/packages/next/src/client/with-router.tsx index 7dd0217ef4d03..d663d6194d61a 100644 --- a/packages/next/src/client/with-router.tsx +++ b/packages/next/src/client/with-router.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import type { BaseContext, NextComponentType, diff --git a/packages/next/src/pages/_document.tsx b/packages/next/src/pages/_document.tsx index 7924f36b6d711..0b57a846e0559 100644 --- a/packages/next/src/pages/_document.tsx +++ b/packages/next/src/pages/_document.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import type { ReactElement, ReactNode } from 'react' import { OPTIMIZED_FONT_PROVIDERS, @@ -101,7 +101,7 @@ function hasComponentProps(child: any): child is React.ReactElement { function AmpStyles({ styles, }: { - styles?: React.ReactElement[] | React.ReactFragment + styles?: React.ReactElement[] | Iterable }) { if (!styles) return null @@ -837,13 +837,10 @@ export class Head extends React.Component { content={React.Children.count(head || []).toString()} /> )} - {children} {optimizeFonts && } - {nextFontLinkTags.preconnect} {nextFontLinkTags.preload} - {process.env.NEXT_RUNTIME !== 'edge' && inAmpMode && ( <> postponed?: object | null diff --git a/packages/next/src/server/render.tsx b/packages/next/src/server/render.tsx index c57a2e3da5330..fce51e665b049 100644 --- a/packages/next/src/server/render.tsx +++ b/packages/next/src/server/render.tsx @@ -40,7 +40,7 @@ import type { NextParsedUrlQuery } from './request-meta' import type { Revalidate, SwrDelta } from './lib/revalidate' import type { COMPILER_NAMES } from '../shared/lib/constants' -import React from 'react' +import React, { type JSX } from 'react' import ReactDOMServer from 'react-dom/server.browser' import { StyleRegistry, createStyleRegistry } from 'styled-jsx' import { diff --git a/packages/next/src/shared/lib/app-dynamic.tsx b/packages/next/src/shared/lib/app-dynamic.tsx index 9efbc8aed56b6..d6990e27e7899 100644 --- a/packages/next/src/shared/lib/app-dynamic.tsx +++ b/packages/next/src/shared/lib/app-dynamic.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import Loadable from './lazy-dynamic/loadable' import type { diff --git a/packages/next/src/shared/lib/dynamic.tsx b/packages/next/src/shared/lib/dynamic.tsx index ae099f6904c95..52f68be1f73ec 100644 --- a/packages/next/src/shared/lib/dynamic.tsx +++ b/packages/next/src/shared/lib/dynamic.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import Loadable from './loadable.shared-runtime' const isServerSide = typeof window === 'undefined' diff --git a/packages/next/src/shared/lib/get-img-props.ts b/packages/next/src/shared/lib/get-img-props.ts index a4d38baa085e0..ac92bc3598a77 100644 --- a/packages/next/src/shared/lib/get-img-props.ts +++ b/packages/next/src/shared/lib/get-img-props.ts @@ -7,6 +7,8 @@ import type { ImageLoaderPropsWithConfig, } from './image-config' +import type { JSX } from 'react' + export interface StaticImageData { src: string height: number diff --git a/packages/next/src/shared/lib/head.tsx b/packages/next/src/shared/lib/head.tsx index 42f95767bfa4c..978dfd8401f13 100644 --- a/packages/next/src/shared/lib/head.tsx +++ b/packages/next/src/shared/lib/head.tsx @@ -1,6 +1,6 @@ 'use client' -import React, { useContext } from 'react' +import React, { useContext, type JSX } from 'react' import Effect from './side-effect' import { AmpStateContext } from './amp-context.shared-runtime' import { HeadManagerContext } from './head-manager-context.shared-runtime' @@ -21,7 +21,7 @@ export function defaultHead(inAmpMode = false): JSX.Element[] { function onlyReactElement( list: Array>, - child: React.ReactChild + child: React.ReactElement | number | string ): Array> { // React children can be "string" or "number" in this case we ignore them for backwards compat if (typeof child === 'string' || typeof child === 'number') { @@ -35,7 +35,7 @@ function onlyReactElement( // @ts-expect-error @types/react does not remove fragments but this could also return ReactPortal[] ( fragmentList: Array>, - fragmentChild: React.ReactChild + fragmentChild: React.ReactElement | number | string ): Array> => { if ( typeof fragmentChild === 'string' || diff --git a/packages/next/src/shared/lib/html-context.shared-runtime.ts b/packages/next/src/shared/lib/html-context.shared-runtime.ts index 33b2dce0f06ba..a2b2bac3a31fd 100644 --- a/packages/next/src/shared/lib/html-context.shared-runtime.ts +++ b/packages/next/src/shared/lib/html-context.shared-runtime.ts @@ -4,7 +4,7 @@ import type { NEXT_DATA } from './utils' import type { FontConfig } from '../../server/font-utils' import type { NextFontManifest } from '../../build/webpack/plugins/next-font-manifest-plugin' -import { createContext, useContext } from 'react' +import { createContext, useContext, type JSX } from 'react' export type HtmlProps = { __NEXT_DATA__: NEXT_DATA @@ -35,7 +35,7 @@ export type HtmlProps = { } locale?: string disableOptimizedLoading?: boolean - styles?: React.ReactElement[] | React.ReactFragment + styles?: React.ReactElement[] | Iterable head?: Array crossOrigin?: 'anonymous' | 'use-credentials' | '' | undefined optimizeCss?: any diff --git a/packages/next/src/shared/lib/side-effect.tsx b/packages/next/src/shared/lib/side-effect.tsx index aaa85a1db4f66..a20ebd26348fc 100644 --- a/packages/next/src/shared/lib/side-effect.tsx +++ b/packages/next/src/shared/lib/side-effect.tsx @@ -1,5 +1,5 @@ import type React from 'react' -import { Children, useEffect, useLayoutEffect } from 'react' +import { Children, useEffect, useLayoutEffect, type JSX } from 'react' type State = JSX.Element[] | undefined diff --git a/packages/next/src/shared/lib/utils.ts b/packages/next/src/shared/lib/utils.ts index 98098d8128a27..418bd8f1635f1 100644 --- a/packages/next/src/shared/lib/utils.ts +++ b/packages/next/src/shared/lib/utils.ts @@ -1,5 +1,5 @@ import type { HtmlProps } from './html-context.shared-runtime' -import type { ComponentType } from 'react' +import type { ComponentType, JSX } from 'react' import type { DomainLocale } from '../../server/config' import type { Env } from '@next/env' import type { IncomingMessage, ServerResponse } from 'http' @@ -191,7 +191,7 @@ export type DocumentContext = NextPageContext & { } export type DocumentInitialProps = RenderPageResult & { - styles?: React.ReactElement[] | React.ReactFragment | JSX.Element + styles?: React.ReactElement[] | Iterable | JSX.Element } export type DocumentProps = DocumentInitialProps & HtmlProps diff --git a/packages/next/types/react-dom.d.ts b/packages/next/types/react-dom.d.ts index a6b75b20a2b33..5d8d19435e3c6 100644 --- a/packages/next/types/react-dom.d.ts +++ b/packages/next/types/react-dom.d.ts @@ -1,3 +1,4 @@ +import type { JSX } from 'react' declare module 'react-dom/server-rendering-stub' declare module 'react-dom/server.browser' diff --git a/test/e2e/app-dir/mdx/types/mdx.d.ts b/test/e2e/app-dir/mdx/types/mdx.d.ts index a9831edba2ed2..ed47965c7afc6 100644 --- a/test/e2e/app-dir/mdx/types/mdx.d.ts +++ b/test/e2e/app-dir/mdx/types/mdx.d.ts @@ -1,3 +1,4 @@ +import type { JSX } from 'react' // types/mdx.d.ts declare module '*.mdx' { let MDXComponent: (props) => JSX.Element diff --git a/test/e2e/new-link-behavior/typescript/pages/ref.tsx b/test/e2e/new-link-behavior/typescript/pages/ref.tsx index 6eacfa52b0211..2bc111b57d9fa 100644 --- a/test/e2e/new-link-behavior/typescript/pages/ref.tsx +++ b/test/e2e/new-link-behavior/typescript/pages/ref.tsx @@ -2,7 +2,7 @@ import { useRef, useEffect, useState } from 'react' import Link from 'next/link' export default function Page() { - const ref = useRef() + const ref = useRef(undefined) const [anchorText, setAnchorText] = useState(null) useEffect(() => { diff --git a/test/integration/typescript-baseurl/components/hi.tsx b/test/integration/typescript-baseurl/components/hi.tsx index 3283eafde3e36..435c1dd9a7535 100644 --- a/test/integration/typescript-baseurl/components/hi.tsx +++ b/test/integration/typescript-baseurl/components/hi.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' export function Hi(): JSX.Element { return <>Hi diff --git a/test/integration/typescript-baseurl/components/world.tsx b/test/integration/typescript-baseurl/components/world.tsx index d7d1f66258c77..997ed67e8929d 100644 --- a/test/integration/typescript-baseurl/components/world.tsx +++ b/test/integration/typescript-baseurl/components/world.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' export function World(): JSX.Element { return <>World diff --git a/test/integration/typescript-baseurl/pages/hello.tsx b/test/integration/typescript-baseurl/pages/hello.tsx index 1129fe708833b..d8697396ec89e 100644 --- a/test/integration/typescript-baseurl/pages/hello.tsx +++ b/test/integration/typescript-baseurl/pages/hello.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import { World } from 'components/world' export default function HelloPage(): JSX.Element { return ( diff --git a/test/integration/typescript-external-dir/project/components/world.tsx b/test/integration/typescript-external-dir/project/components/world.tsx index d7d1f66258c77..997ed67e8929d 100644 --- a/test/integration/typescript-external-dir/project/components/world.tsx +++ b/test/integration/typescript-external-dir/project/components/world.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' export function World(): JSX.Element { return <>World diff --git a/test/integration/typescript-external-dir/project/pages/index.tsx b/test/integration/typescript-external-dir/project/pages/index.tsx index c391ff332ff2a..fcf5e96200832 100644 --- a/test/integration/typescript-external-dir/project/pages/index.tsx +++ b/test/integration/typescript-external-dir/project/pages/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import { World } from 'components/world' diff --git a/test/integration/typescript-external-dir/shared/components/counter.tsx b/test/integration/typescript-external-dir/shared/components/counter.tsx index 1740cf121827b..b1d1c4361c8f3 100644 --- a/test/integration/typescript-external-dir/shared/components/counter.tsx +++ b/test/integration/typescript-external-dir/shared/components/counter.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useState, type JSX } from 'react' import inc from '../libs/inc' diff --git a/test/integration/typescript-paths/components/world.tsx b/test/integration/typescript-paths/components/world.tsx index d7d1f66258c77..997ed67e8929d 100644 --- a/test/integration/typescript-paths/components/world.tsx +++ b/test/integration/typescript-paths/components/world.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' export function World(): JSX.Element { return <>World diff --git a/test/integration/typescript-paths/pages/alias-to-d-ts.tsx b/test/integration/typescript-paths/pages/alias-to-d-ts.tsx index d24ac047d2f73..57bb61a809e1e 100644 --- a/test/integration/typescript-paths/pages/alias-to-d-ts.tsx +++ b/test/integration/typescript-paths/pages/alias-to-d-ts.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import NotAliasedToDTS from 'd-ts-alias' export default function HelloPage(): JSX.Element { diff --git a/test/integration/typescript-paths/pages/basic-alias.tsx b/test/integration/typescript-paths/pages/basic-alias.tsx index f9b23a1829702..df6c89abd6bbc 100644 --- a/test/integration/typescript-paths/pages/basic-alias.tsx +++ b/test/integration/typescript-paths/pages/basic-alias.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import { World } from '@c/world' export default function HelloPage(): JSX.Element { return ( diff --git a/test/integration/typescript-paths/pages/resolve-fallback.tsx b/test/integration/typescript-paths/pages/resolve-fallback.tsx index 8b1dc93cd6bf4..9c52c9f55ffc8 100644 --- a/test/integration/typescript-paths/pages/resolve-fallback.tsx +++ b/test/integration/typescript-paths/pages/resolve-fallback.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import api from '@lib/b-only' export default function ResolveOrder(): JSX.Element { return
{api()}
diff --git a/test/integration/typescript-paths/pages/resolve-order.tsx b/test/integration/typescript-paths/pages/resolve-order.tsx index dd83cd62e8a8a..318772f49516f 100644 --- a/test/integration/typescript-paths/pages/resolve-order.tsx +++ b/test/integration/typescript-paths/pages/resolve-order.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import api from '@lib/api' export default function ResolveOrder(): JSX.Element { return
{api()}
diff --git a/test/integration/typescript-workspaces-paths/packages/www/components/world.tsx b/test/integration/typescript-workspaces-paths/packages/www/components/world.tsx index d7d1f66258c77..997ed67e8929d 100644 --- a/test/integration/typescript-workspaces-paths/packages/www/components/world.tsx +++ b/test/integration/typescript-workspaces-paths/packages/www/components/world.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' export function World(): JSX.Element { return <>World diff --git a/test/integration/typescript-workspaces-paths/packages/www/pages/alias-to-d-ts.tsx b/test/integration/typescript-workspaces-paths/packages/www/pages/alias-to-d-ts.tsx index d24ac047d2f73..57bb61a809e1e 100644 --- a/test/integration/typescript-workspaces-paths/packages/www/pages/alias-to-d-ts.tsx +++ b/test/integration/typescript-workspaces-paths/packages/www/pages/alias-to-d-ts.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import NotAliasedToDTS from 'd-ts-alias' export default function HelloPage(): JSX.Element { diff --git a/test/integration/typescript-workspaces-paths/packages/www/pages/basic-alias.tsx b/test/integration/typescript-workspaces-paths/packages/www/pages/basic-alias.tsx index f9b23a1829702..df6c89abd6bbc 100644 --- a/test/integration/typescript-workspaces-paths/packages/www/pages/basic-alias.tsx +++ b/test/integration/typescript-workspaces-paths/packages/www/pages/basic-alias.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import { World } from '@c/world' export default function HelloPage(): JSX.Element { return ( diff --git a/test/integration/typescript-workspaces-paths/packages/www/pages/resolve-fallback.tsx b/test/integration/typescript-workspaces-paths/packages/www/pages/resolve-fallback.tsx index 8b1dc93cd6bf4..9c52c9f55ffc8 100644 --- a/test/integration/typescript-workspaces-paths/packages/www/pages/resolve-fallback.tsx +++ b/test/integration/typescript-workspaces-paths/packages/www/pages/resolve-fallback.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import api from '@lib/b-only' export default function ResolveOrder(): JSX.Element { return
{api()}
diff --git a/test/integration/typescript-workspaces-paths/packages/www/pages/resolve-order.tsx b/test/integration/typescript-workspaces-paths/packages/www/pages/resolve-order.tsx index dd83cd62e8a8a..318772f49516f 100644 --- a/test/integration/typescript-workspaces-paths/packages/www/pages/resolve-order.tsx +++ b/test/integration/typescript-workspaces-paths/packages/www/pages/resolve-order.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' import api from '@lib/api' export default function ResolveOrder(): JSX.Element { return
{api()}
diff --git a/test/integration/typescript/components/world.tsx b/test/integration/typescript/components/world.tsx index d7d1f66258c77..997ed67e8929d 100644 --- a/test/integration/typescript/components/world.tsx +++ b/test/integration/typescript/components/world.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { type JSX } from 'react' export function World(): JSX.Element { return <>World diff --git a/test/integration/typescript/pages/hello.tsx b/test/integration/typescript/pages/hello.tsx index acfd061ee4109..bfc5e3081e506 100644 --- a/test/integration/typescript/pages/hello.tsx +++ b/test/integration/typescript/pages/hello.tsx @@ -1,5 +1,5 @@ import { useRouter } from 'next/router' -import React from 'react' +import React, { type JSX } from 'react' import { hello } from '../components/hello' import Image from '../components/image' import Link from '../components/link' From cd92cff52ceb3b476ce5c178907295f21252be3c Mon Sep 17 00:00:00 2001 From: eps1lon Date: Thu, 21 Mar 2024 14:56:01 +0100 Subject: [PATCH 3/4] Resolve rest of breaking changes --- packages/next/src/client/components/layout-router.tsx | 4 ++++ .../next/src/client/components/react-dev-overlay/shared.ts | 2 +- .../src/client/components/router-reducer/ppr-navigations.ts | 6 ++++-- packages/next/types/react-dom.d.ts | 5 ++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/next/src/client/components/layout-router.tsx b/packages/next/src/client/components/layout-router.tsx index c9807a34e66f6..0f2647d73912f 100644 --- a/packages/next/src/client/components/layout-router.tsx +++ b/packages/next/src/client/components/layout-router.tsx @@ -91,7 +91,9 @@ function walkAddRefetch( * Wraps ReactDOM.findDOMNode with additional logic to hide React Strict Mode warning */ function findDOMNode( + // @ts-expect-error FIXME: `findDOMNode` was removed in React 19. instance: Parameters[0] + // @ts-expect-error FIXME: `findDOMNode` was removed in React 19. ): ReturnType { // Tree-shake for server bundle if (typeof window === 'undefined') return null @@ -105,11 +107,13 @@ function findDOMNode( originalConsoleError(...messages) } } + // @ts-expect-error FIXME: `findDOMNode` was removed in React 19. return ReactDOM.findDOMNode(instance) } finally { console.error = originalConsoleError! } } + // @ts-expect-error FIXME: `findDOMNode` was removed in React 19. return ReactDOM.findDOMNode(instance) } diff --git a/packages/next/src/client/components/react-dev-overlay/shared.ts b/packages/next/src/client/components/react-dev-overlay/shared.ts index 3151721c09ca6..cd7c7c8884bbe 100644 --- a/packages/next/src/client/components/react-dev-overlay/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/shared.ts @@ -94,7 +94,7 @@ export const INITIAL_OVERLAY_STATE: OverlayState = { } export function useErrorOverlayReducer() { - return useReducer>((_state, action) => { + return useReducer((_state: OverlayState, action: BusEvent): OverlayState => { switch (action.type) { case ACTION_BUILD_OK: { return { ..._state, buildError: null } diff --git a/packages/next/src/client/components/router-reducer/ppr-navigations.ts b/packages/next/src/client/components/router-reducer/ppr-navigations.ts index 891a343763eec..cc191e978b38e 100644 --- a/packages/next/src/client/components/router-reducer/ppr-navigations.ts +++ b/packages/next/src/client/components/router-reducer/ppr-navigations.ts @@ -524,8 +524,10 @@ function createPendingCacheNode( // Create a deferred promise. This will be fulfilled once the dynamic // response is received from the server. - rsc: createDeferredRsc(), - head: isLeafSegment ? createDeferredRsc() : null, + // TODO: Double check why we need to type-cast here. + // It's likely some conflict between the bounded `ReactNode = AwaitedReactNode | Promise` and the unbounded `Promise` + rsc: createDeferredRsc() as React.ReactNode, + head: isLeafSegment ? (createDeferredRsc() as React.ReactNode) : null, lazyDataResolved: false, } } diff --git a/packages/next/types/react-dom.d.ts b/packages/next/types/react-dom.d.ts index 5d8d19435e3c6..79d1b9026ec79 100644 --- a/packages/next/types/react-dom.d.ts +++ b/packages/next/types/react-dom.d.ts @@ -1,8 +1,9 @@ -import type { JSX } from 'react' declare module 'react-dom/server-rendering-stub' declare module 'react-dom/server.browser' declare module 'react-dom/server.edge' { + import type { JSX } from 'react' + /** * https://github.com/facebook/react/blob/aec521a96d3f1bebc2ba38553d14f4989c6e88e0/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js#L329-L333 */ @@ -76,6 +77,8 @@ declare module 'react-dom/server.edge' { } declare module 'react-dom/static.edge' { + import type { JSX } from 'react' + /** * https://github.com/facebook/react/blob/aec521a96d3f1bebc2ba38553d14f4989c6e88e0/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js#L329-L333 */ From 97fcba326ef465d134862feb1990f875d360675e Mon Sep 17 00:00:00 2001 From: eps1lon Date: Thu, 21 Mar 2024 15:00:11 +0100 Subject: [PATCH 4/4] Resolve `ReactElement` breaking changes The codemod affected 15 files while we only had to change a single file. Confirms that this codemod should really be off by default. Cautionary advise against it should be stronger. --- packages/next/src/pages/_document.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/next/src/pages/_document.tsx b/packages/next/src/pages/_document.tsx index 0b57a846e0559..c8a8e1913aa2d 100644 --- a/packages/next/src/pages/_document.tsx +++ b/packages/next/src/pages/_document.tsx @@ -94,7 +94,7 @@ function getPolyfillScripts(context: HtmlProps, props: OriginProps) { )) } -function hasComponentProps(child: any): child is React.ReactElement { +function hasComponentProps(child: any): child is React.ReactElement { return !!child && !!child.props } @@ -106,7 +106,7 @@ function AmpStyles({ if (!styles) return null // try to parse styles from fragment for backwards compat - const curStyles: React.ReactElement[] = Array.isArray(styles) + const curStyles: React.ReactElement[] = Array.isArray(styles) ? (styles as React.ReactElement[]) : [] if ( @@ -115,7 +115,7 @@ function AmpStyles({ // @ts-ignore Property 'props' does not exist on type ReactElement Array.isArray(styles.props.children) ) { - const hasStyles = (el: React.ReactElement) => + const hasStyles = (el: React.ReactElement) => el?.props?.dangerouslySetInnerHTML?.__html // @ts-ignore Property 'props' does not exist on type ReactElement styles.props.children.forEach((child: React.ReactElement) => {