diff --git a/crates/next-core/src/next_import_map.rs b/crates/next-core/src/next_import_map.rs index 5cef244e1df1a..d7c4739868396 100644 --- a/crates/next-core/src/next_import_map.rs +++ b/crates/next-core/src/next_import_map.rs @@ -567,6 +567,15 @@ async fn insert_next_server_special_aliases( external_esm_if_node(project_path, "next/dist/compiled/@vercel/og/index.node.js"), ); + import_map.insert_exact_alias( + "next/dist/server/ReactDOMServerPages", + ImportMapping::Alternatives(vec![ + request_to_import_mapping(project_path, "react-dom/server.edge"), + request_to_import_mapping(project_path, "react-dom/server.browser"), + ]) + .cell(), + ); + import_map.insert_exact_alias( "@opentelemetry/api", // It needs to prefer the local version of @opentelemetry/api diff --git a/packages/next/src/build/create-compiler-aliases.ts b/packages/next/src/build/create-compiler-aliases.ts index 49788fde474d2..27f6d454616bf 100644 --- a/packages/next/src/build/create-compiler-aliases.ts +++ b/packages/next/src/build/create-compiler-aliases.ts @@ -1,4 +1,5 @@ import path from 'path' +import * as React from 'react' import { DOT_NEXT_ALIAS, PAGES_DIR_ALIAS, @@ -21,6 +22,8 @@ interface CompilerAliases { [alias: string]: string | string[] } +const isReact19 = typeof React.use === 'function' + export function createWebpackAliases({ distDir, isClient, @@ -90,6 +93,12 @@ export function createWebpackAliases({ return { '@vercel/og$': 'next/dist/server/og/image-response', + // Avoid bundling both entrypoints in React 19 when we just need one. + // Also avoids bundler warnings in React 18 where react-dom/server.edge doesn't exist. + 'next/dist/server/ReactDOMServerPages': isReact19 + ? 'react-dom/server.edge' + : 'react-dom/server.browser', + // Alias next/dist imports to next/dist/esm assets, // let this alias hit before `next` alias. ...(isEdgeServer diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index 7a1775b1efcc3..5dee552b02fa9 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -1974,14 +1974,6 @@ export default async function getBaseWebpackConfig( ) ), ].filter(Boolean as any as ExcludesFalse), - ignoreWarnings: [ - (warning) => { - // require('react-dom/server.edge') is wrapped in try-catch so save to ignore. - return warning.message.startsWith( - 'Module not found: Error: Package path ./server.edge is not exported from package' - ) - }, - ], } // Support tsconfig and jsconfig baseUrl diff --git a/packages/next/src/server/render.tsx b/packages/next/src/server/render.tsx index 1898a94065c8d..ae913fb6c3a64 100644 --- a/packages/next/src/server/render.tsx +++ b/packages/next/src/server/render.tsx @@ -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 ReactDOMServerPages from './ReactDOMServerPages' +import ReactDOMServerPages from 'next/dist/server/ReactDOMServerPages' import { StyleRegistry, createStyleRegistry } from 'styled-jsx' import { GSP_NO_RETURNED_VALUE, diff --git a/packages/next/types/$$compiled.internal.d.ts b/packages/next/types/$$compiled.internal.d.ts index 76832e459fb4e..6b0860ff32e1c 100644 --- a/packages/next/types/$$compiled.internal.d.ts +++ b/packages/next/types/$$compiled.internal.d.ts @@ -37,6 +37,10 @@ declare module 'VAR_USERLAND' declare module 'VAR_MODULE_DOCUMENT' declare module 'VAR_MODULE_APP' +declare module 'next/dist/server/ReactDOMServerPages' { + export * from 'react-dom/server.edge' +} + declare module 'next/dist/compiled/@napi-rs/triples' { export * from '@napi-rs/triples' } diff --git a/packages/next/types/react-dom.d.ts b/packages/next/types/react-dom.d.ts index 7c11484f3bb36..9b811922e4d55 100644 --- a/packages/next/types/react-dom.d.ts +++ b/packages/next/types/react-dom.d.ts @@ -70,10 +70,6 @@ declare module 'react-dom/server.edge' { > } -declare module 'react-dom/server.browser' { - export * from 'react-dom/server.edge' -} - declare module 'react-dom/static.edge' { import type { JSX } from 'react' /** diff --git a/test/development/basic/next-rs-api.test.ts b/test/development/basic/next-rs-api.test.ts index e4389cdc96856..bccce2a85216e 100644 --- a/test/development/basic/next-rs-api.test.ts +++ b/test/development/basic/next-rs-api.test.ts @@ -14,8 +14,6 @@ import type { 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/') @@ -42,21 +40,6 @@ 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) => ({ @@ -69,11 +52,6 @@ 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)