Skip to content

Commit

Permalink
Fall back to react-dom/server.browser in Pages router when React 18…
Browse files Browse the repository at this point in the history
… is installed

React 18 has no `react-dom/server.edge`
  • Loading branch information
eps1lon committed Sep 19, 2024
1 parent 2fab219 commit 66bb034
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/next/src/server/ReactDOMServerPages.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from 'react-dom/server.edge'
20 changes: 20 additions & 0 deletions packages/next/src/server/ReactDOMServerPages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
let ReactDOMServer

try {
ReactDOMServer = require('react-dom/server.edge')
} catch (error) {
if (
// TODO: copilot suggestion. Does this code actually exist?
error.code !== 'MODULE_NOT_FOUND' &&
// TODO: actually encountered that
error.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED'
) {
throw error
}
// TODO: Ensure App Router does not bundle this
// In React versions without react-dom/server.edge, the browser build works in Node.js.
// The Node.js build does not support renderToReadableStream.
ReactDOMServer = require('react-dom/server.browser')
}

module.exports = ReactDOMServer
7 changes: 3 additions & 4 deletions packages/next/src/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import type { Revalidate, SwrDelta } from './lib/revalidate'
import type { COMPILER_NAMES } from '../shared/lib/constants'

import React, { type JSX } from 'react'
import ReactDOMServerBrowser from 'react-dom/server.browser'
import ReactDOMServerPages from './ReactDOMServerPages'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'
import {
GSP_NO_RETURNED_VALUE,
Expand Down Expand Up @@ -127,8 +127,7 @@ function noRouter() {
}

async function renderToString(element: React.ReactElement) {
const renderStream =
await ReactDOMServerBrowser.renderToReadableStream(element)
const renderStream = await ReactDOMServerPages.renderToReadableStream(element)
await renderStream.allReady
return streamToString(renderStream)
}
Expand Down Expand Up @@ -1327,7 +1326,7 @@ export async function renderToHTMLImpl(
) => {
const content = renderContent(EnhancedApp, EnhancedComponent)
return await renderToInitialFizzStream({
ReactDOMServer: ReactDOMServerBrowser,
ReactDOMServer: ReactDOMServerPages,
element: content,
})
}
Expand Down
24 changes: 23 additions & 1 deletion test/development/basic/next-rs-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
import loadConfig from 'next/dist/server/config'
import path from 'path'

const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18

function normalizePath(path: string) {
return path
.replace(/\[project\].+\/node_modules\//g, '[project]/.../node_modules/')
Expand All @@ -41,6 +43,21 @@ function styledStringToMarkdown(styled: StyledString): string {
}
}

function isReactDOMServerEdgeConditionalBundlingIssue(issue: {
description?: Issue['description']
filePath: string
}) {
return (
isReact18 &&
issue.filePath ===
'[project]/.../node_modules/next/dist/esm/server/ReactDOMServerPages.js' &&
issue.description?.type === 'text' &&
issue.description?.value.includes(
'Import map: aliased to module "react-dom" with subpath "/server.edge" inside of [project]/'
)
)
}

function normalizeIssues(issues: Issue[]) {
return issues
.map((issue) => ({
Expand All @@ -53,6 +70,11 @@ function normalizeIssues(issues: Issue[]) {
source: normalizePath(issue.source.source.ident),
},
}))
.filter((issue) => {
// The conditional bundling is wrapped in a try-catch.
// It doesn't surface to the user, so it's safe to ignore here.
return !isReactDOMServerEdgeConditionalBundlingIssue(issue)
})
.sort((a, b) => {
const a_ = JSON.stringify(a)
const b_ = JSON.stringify(b)
Expand Down Expand Up @@ -504,7 +526,7 @@ describe('next.rs api', () => {
expect(result.done).toBe(false)
expect(result.value).toHaveProperty('resource', expect.toBeObject())
expect(result.value).toHaveProperty('type', 'issues')
expect(result.value).toHaveProperty('issues', expect.toBeEmpty())
expect(normalizeIssues(result.value.issues)).toEqual([])
expect(result.value).toHaveProperty(
'diagnostics',
expect.toBeEmpty()
Expand Down

0 comments on commit 66bb034

Please sign in to comment.