diff --git a/package.json b/package.json index 162cce7f1e6c..0b5117655871 100644 --- a/package.json +++ b/package.json @@ -144,9 +144,9 @@ "pretty-ms": "7.0.0", "random-seed": "0.3.0", "react": "17.0.2", - "react-18": "npm:react@18.0.0-rc.0", + "react-18": "npm:react@18.0.0-rc.1", "react-dom": "17.0.2", - "react-dom-18": "npm:react-dom@18.0.0-rc.0", + "react-dom-18": "npm:react-dom@18.0.0-rc.1", "react-ssr-prepass": "1.0.8", "relay-compiler": "13.0.2", "relay-runtime": "13.0.2", diff --git a/packages/next/build/webpack/loaders/next-flight-server-loader.ts b/packages/next/build/webpack/loaders/next-flight-server-loader.ts index d66405948626..dc8884da6fc3 100644 --- a/packages/next/build/webpack/loaders/next-flight-server-loader.ts +++ b/packages/next/build/webpack/loaders/next-flight-server-loader.ts @@ -69,11 +69,16 @@ async function parseImportsInfo({ transformedSource += JSON.stringify(`${importSource}?__sc_client__`) imports += `require(${JSON.stringify(importSource)})\n` } else { + // FIXME + // case: 'react' + // Avoid module resolution error like Cannot find `./?__rsc_server__` in react/package.json + + // cases: 'react/jsx-runtime', 'react/jsx-dev-runtime' // This is a special case to avoid the Duplicate React error. // Since we already include React in the SSR runtime, // here we can't create a new module with the ?__rsc_server__ query. if ( - ['react/jsx-runtime', 'react/jsx-dev-runtime'].includes( + ['react', 'react/jsx-runtime', 'react/jsx-dev-runtime'].includes( importSource ) ) { diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 6c2de5960162..39174b90bcdb 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -547,9 +547,10 @@ function renderReactElement( const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete) if (process.env.__NEXT_REACT_ROOT) { + const ReactDOMClient = require('react-dom/client') if (!reactRoot) { // Unlike with createRoot, you don't need a separate root.render() call here - reactRoot = (ReactDOM as any).hydrateRoot(domEl, reactEl) + reactRoot = (ReactDOMClient as any).hydrateRoot(domEl, reactEl) // TODO: Remove shouldHydrate variable when React 18 is stable as it can depend on `reactRoot` existing shouldHydrate = false } else { @@ -812,13 +813,11 @@ if (process.env.__NEXT_RSC) { return ( - - - + ) } diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index f86b9c3a10a3..06e4bec57b7f 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -395,11 +395,7 @@ function createServerComponentRenderer( } const Component = (props: any) => { - return ( - - - - ) + return } // Although it's not allowed to attach some static methods to Component, @@ -1353,7 +1349,6 @@ export async function renderToHTML( ))} ), - generateStaticHTML: true, }) const flushed = await streamToString(flushEffectStream) @@ -1365,7 +1360,6 @@ export async function renderToHTML( element: content, suffix, dataStream: serverComponentsInlinedTransformStream?.readable, - generateStaticHTML: generateStaticHTML || !hasConcurrentFeatures, flushEffectHandler, }) } @@ -1498,7 +1492,6 @@ export async function renderToHTML( const documentStream = await renderToStream({ ReactDOMServer, element: document, - generateStaticHTML: true, }) documentHTML = await streamToString(documentStream) } else { @@ -1753,23 +1746,21 @@ function renderToStream({ element, suffix, dataStream, - generateStaticHTML, flushEffectHandler, }: { ReactDOMServer: typeof import('react-dom/server') element: React.ReactElement suffix?: string dataStream?: ReadableStream - generateStaticHTML: boolean flushEffectHandler?: () => Promise }): Promise> { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { let resolved = false const closeTag = '' const suffixUnclosed = suffix ? suffix.split(closeTag)[0] : null - const doResolve = () => { + const doResolve = (renderStream: ReadableStream) => { if (!resolved) { resolved = true @@ -1798,7 +1789,7 @@ function renderToStream({ } } - const renderStream: ReadableStream = ( + const renderStream: ReadableStream = await ( ReactDOMServer as any ).renderToReadableStream(element, { onError(err: Error) { @@ -1807,15 +1798,9 @@ function renderToStream({ reject(err) } }, - onCompleteShell() { - if (!generateStaticHTML) { - doResolve() - } - }, - onCompleteAll() { - doResolve() - }, }) + + doResolve(renderStream) }) } diff --git a/test/integration/react-18/app/pages/suspense/unwrapped.js b/test/integration/react-18/app/pages/suspense/unwrapped.js deleted file mode 100644 index 93c32bbbc501..000000000000 --- a/test/integration/react-18/app/pages/suspense/unwrapped.js +++ /dev/null @@ -1,19 +0,0 @@ -import React, { Suspense } from 'react' -import dynamic from 'next/dynamic' - -// flag for testing -const wrapped = true - -const Hello = dynamic(() => import('../../components/hello'), { - suspense: true, -}) - -export default function Unwrapped() { - if (!wrapped) return - - return ( - - - - ) -} diff --git a/test/integration/react-18/test/index.test.js b/test/integration/react-18/test/index.test.js index 2660a9fd20b1..ab362789579f 100644 --- a/test/integration/react-18/test/index.test.js +++ b/test/integration/react-18/test/index.test.js @@ -25,7 +25,6 @@ const nodeArgs = ['-r', join(__dirname, 'require-hook.js')] const appDir = join(__dirname, '../app') const nextConfig = new File(join(appDir, 'next.config.js')) const dynamicHello = new File(join(appDir, 'components/dynamic-hello.js')) -const unwrappedPage = new File(join(appDir, 'pages/suspense/unwrapped.js')) const invalidPage = new File(join(appDir, 'pages/invalid.js')) const USING_CREATE_ROOT = 'Using the createRoot API for React' @@ -95,20 +94,6 @@ describe('React 18 Support', () => { describe('Basics', () => { runTests('default setting with react 18', (context) => basics(context)) - - it('suspense is not allowed in blocking rendering mode (dev)', async () => { - // set dynamic.suspense = true but not wrapping with - unwrappedPage.replace('wrapped = true', 'wrapped = false') - const appPort = await findPort() - const app = await launchApp(appDir, appPort, { nodeArgs }) - const html = await renderViaHTTP(appPort, '/suspense/unwrapped') - unwrappedPage.restore() - await killApp(app) - - expect(html).toContain( - 'A React component suspended while rendering, but no fallback UI was specified' - ) - }) }) // React 18 with Strict Mode enabled might cause double invocation of lifecycle methods. diff --git a/test/integration/react-18/test/with-react-18.js b/test/integration/react-18/test/with-react-18.js index f754eb801bc9..abbec9e348a4 100644 --- a/test/integration/react-18/test/with-react-18.js +++ b/test/integration/react-18/test/with-react-18.js @@ -1,13 +1,10 @@ module.exports = function withReact18(config) { config.webpack = (webpackConfig) => { const { alias } = webpackConfig.resolve - // FIXME: resolving react/jsx-runtime https://github.com/facebook/react/issues/20235 - alias['react/jsx-dev-runtime'] = 'react/jsx-dev-runtime.js' - alias['react/jsx-runtime'] = 'react/jsx-runtime.js' - // Use react 18 alias['react'] = 'react-18' alias['react-dom'] = 'react-dom-18' + alias['react-dom/client'] = 'react-dom-18/client' alias['react-dom/server'] = 'react-dom-18/server' alias['react-dom/server.browser'] = 'react-dom-18/server.browser' diff --git a/test/integration/react-streaming-and-server-components/app/next.config.js b/test/integration/react-streaming-and-server-components/app/next.config.js index a17299bc8c9e..add280d239bb 100644 --- a/test/integration/react-streaming-and-server-components/app/next.config.js +++ b/test/integration/react-streaming-and-server-components/app/next.config.js @@ -1,7 +1,6 @@ const withReact18 = require('../../react-18/test/with-react-18') module.exports = withReact18({ - trailingSlash: true, reactStrictMode: true, onDemandEntries: { maxInactiveAge: 1000 * 60 * 60, diff --git a/test/integration/react-streaming-and-server-components/test/rsc.js b/test/integration/react-streaming-and-server-components/test/rsc.js index da7130b826b3..e356a57b1479 100644 --- a/test/integration/react-streaming-and-server-components/test/rsc.js +++ b/test/integration/react-streaming-and-server-components/test/rsc.js @@ -78,10 +78,7 @@ export default function (context, { runtime, env }) { it('should support next/link in server components', async () => { const linkHTML = await renderViaHTTP(context.appPort, '/next-api/link') - const linkText = getNodeBySelector( - linkHTML, - 'div[hidden] > a[href="/"]' - ).text() + const linkText = getNodeBySelector(linkHTML, '#__next > a[href="/"]').text() expect(linkText).toContain('go home') @@ -115,7 +112,7 @@ export default function (context, { runtime, env }) { const imageHTML = await renderViaHTTP(context.appPort, '/next-api/image') const imageTag = getNodeBySelector( imageHTML, - 'div[hidden] > span > span > img' + '#__next > span > span > img' ) expect(imageTag.attr('src')).toContain('data:image') @@ -177,13 +174,13 @@ export default function (context, { runtime, env }) { expect( getNodeBySelector( clientExportsHTML, - 'div[hidden] > div > #named-exports' + '#__next > div > #named-exports' ).text() ).toBe('abcde') expect( getNodeBySelector( clientExportsHTML, - 'div[hidden] > div > #default-exports-arrow' + '#__next > div > #default-exports-arrow' ).text() ).toBe('client-default-export-arrow') diff --git a/test/production/react-18-streaming-ssr/index.test.ts b/test/production/react-18-streaming-ssr/index.test.ts index 7d1114b36a0b..0a8e355a7a49 100644 --- a/test/production/react-18-streaming-ssr/index.test.ts +++ b/test/production/react-18-streaming-ssr/index.test.ts @@ -24,8 +24,8 @@ describe('react 18 streaming SSR in minimal mode', () => { }, }, dependencies: { - react: '18.0.0-rc.0', - 'react-dom': '18.0.0-rc.0', + react: '18.0.0-rc.1', + 'react-dom': '18.0.0-rc.1', }, }) }) @@ -47,8 +47,8 @@ describe('react 18 streaming SSR with custom next configs', () => { next = await createNext({ files: { 'pages/hello.js': ` - export default function Page() { - return

hello

+ export default function Page() { + return

hello nextjs

} `, }, @@ -60,8 +60,8 @@ describe('react 18 streaming SSR with custom next configs', () => { }, }, dependencies: { - react: '18.0.0-rc.0', - 'react-dom': '18.0.0-rc.0', + react: '18.0.0-rc.1', + 'react-dom': '18.0.0-rc.1', }, }) }) @@ -69,10 +69,17 @@ describe('react 18 streaming SSR with custom next configs', () => { it('should redirect paths without trailing-slash and render when slash is appended', async () => { const page = '/hello' - const html = await renderViaHTTP(next.url, page + '/') - const res = await fetchViaHTTP(next.url, page, {}, { redirect: 'manual' }) + const redirectRes = await fetchViaHTTP( + next.url, + page, + {}, + { redirect: 'manual' } + ) + const res = await fetchViaHTTP(next.url, page + '/') + const html = await res.text() - expect(html).toContain('hello') - expect(res.status).toBe(308) + expect(redirectRes.status).toBe(308) + expect(res.status).toBe(200) + expect(html).toContain('hello nextjs') }) }) diff --git a/yarn.lock b/yarn.lock index 809d3a4e7732..ecf5d376f8b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17527,22 +17527,20 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -"react-18@npm:react@18.0.0-rc.0": - version "18.0.0-rc.0" - resolved "https://registry.yarnpkg.com/react/-/react-18.0.0-rc.0.tgz#60bfcf1edd0b35fbeeeca852515c6cc2ce06a6eb" - integrity sha512-PawosMBgF8k5Nlc3++ibzjFqPvo1XKv80MNtVYqz3abHHB2w3IpU65sSdSmBd2ooCwVhcp9b1vkx/twqhakNtA== +"react-18@npm:react@18.0.0-rc.1": + version "18.0.0-rc.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.0.0-rc.1.tgz#fbaef1cbf8b1eddb36a869ee1ea723044aa670c2" + integrity sha512-IdvjOtyeFhITZwDrj+GMvY7kbWnGklRzNByO7/pOVIt0o3VZi++BUybpK3Fen1gDPkOZDP/rag7lSh6RZuWOeQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" -"react-dom-18@npm:react-dom@18.0.0-rc.0": - version "18.0.0-rc.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0-rc.0.tgz#aa07044bdd6399ff94c664b2985e2e25948fbf3e" - integrity sha512-tdD1n0svTndHBQvVAq/f2Kx7FgQ30CpSLp87/neQKAHPW5WtdgW1sBSwmFAcMQOrmstTuP0M+zRlH86f9kMX/A== +"react-dom-18@npm:react-dom@18.0.0-rc.1": + version "18.0.0-rc.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0-rc.1.tgz#97296d5a5e7582e65ea0d82a9ccd5953aa05e7f6" + integrity sha512-BZ9NEwUp56MEguEwAzuh3u4bYE9Jv3QrzjaTmu11PV4C/lJCARTELEI16vjnHLq184GoJcCHMBrDILqlCrkZFQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.21.0-rc.0" + scheduler "^0.21.0-rc.1" react-dom@17.0.2: version "17.0.2" @@ -18543,13 +18541,12 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" -scheduler@^0.21.0-rc.0: - version "0.21.0-rc.0-next-f2a59df48-20211208" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-rc.0-next-f2a59df48-20211208.tgz#54e18e1d360194fd54b47a00616e46403fcabdf1" - integrity sha512-x0oLd3YIih9GHqWTaFYejVe6Au+4TadOWZciAq8m4+Fuo5qCi4/3M35a9irVSIP3+qcg/fCqHKJETT9G0ejD1A== +scheduler@^0.21.0-rc.1: + version "0.21.0-rc.1-next-f468816ef-20220225" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-rc.1-next-f468816ef-20220225.tgz#1909b9ce9e67de48f5ca06b52d98828401771e66" + integrity sha512-/maKldJ78Ba2o1e9kh2mUpqNZH7XrmSQ1o1aa3HUPN/OMotUB9+pBKX/y3Zihkjco21H4cujG9hK2sJOZUpzJw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" "schema-utils2@npm:schema-utils@2.7.1": version "2.7.1"