Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pages react alias #50128

Merged
merged 16 commits into from
May 22, 2023
108 changes: 71 additions & 37 deletions packages/next/src/build/webpack-config.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great improvement, thanks!

Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ import { NextFontManifestPlugin } from './webpack/plugins/next-font-manifest-plu
import { getSupportedBrowsers } from './utils'
import { METADATA_RESOURCE_QUERY } from './webpack/loaders/metadata/discover'

type ExcludesFalse = <T>(x: T | false) => x is T
type ClientEntries = {
[key: string]: string | string[]
}

const EXTERNAL_PACKAGES =
require('../lib/server-external-packages.json') as string[]

Expand Down Expand Up @@ -384,7 +389,42 @@ export function getDefineEnv({
}
}

type ExcludesFalse = <T>(x: T | false) => x is T
function createReactAliases(
bundledReactChannel: string,
opts: {
reactSharedSubset: boolean
reactDomServerRenderingStub: boolean
}
) {
const alias = {
react$: `next/dist/compiled/react${bundledReactChannel}`,
'react-dom$': `next/dist/compiled/react-dom${bundledReactChannel}`,
'react/jsx-runtime$': `next/dist/compiled/react${bundledReactChannel}/jsx-runtime`,
'react/jsx-dev-runtime$': `next/dist/compiled/react${bundledReactChannel}/jsx-dev-runtime`,
'react-dom/server$': `next/dist/compiled/react-dom${bundledReactChannel}/server$`,
'react-dom/server.edge$': `next/dist/compiled/react-dom${bundledReactChannel}/server.edge`,
'react-dom/server.browser$': `next/dist/compiled/react-dom${bundledReactChannel}/server.browser`,
'react-server-dom-webpack/client$': `next/dist/compiled/react-server-dom-webpack${bundledReactChannel}/client`,
'react-server-dom-webpack/client.edge$': `next/dist/compiled/react-server-dom-webpack${bundledReactChannel}/client.edge`,
'react-server-dom-webpack/server.edge$': `next/dist/compiled/react-server-dom-webpack${bundledReactChannel}/server.edge`,
'react-server-dom-webpack/server.node$': `next/dist/compiled/react-server-dom-webpack${bundledReactChannel}/server.node`,
}

if (opts.reactSharedSubset) {
alias[
'react$'
] = `next/dist/compiled/react${bundledReactChannel}/react.shared-subset`
}
// Use server rendering stub for RSC
// x-ref: https://github.com/facebook/react/pull/25436
if (opts.reactDomServerRenderingStub) {
alias[
'react-dom$'
] = `next/dist/compiled/react-dom${bundledReactChannel}/server-rendering-stub`
}

return alias
}
huozhi marked this conversation as resolved.
Show resolved Hide resolved

const devtoolRevertWarning = execOnce(
(devtool: webpack.Configuration['devtool']) => {
Expand Down Expand Up @@ -436,10 +476,6 @@ function getOptimizedAliases(): { [pkg: string]: string } {
)
}

type ClientEntries = {
[key: string]: string | string[]
}

export function attachReactRefresh(
webpackConfig: webpack.Configuration,
targetLoader: webpack.RuleSetUseItem
Expand Down Expand Up @@ -1519,10 +1555,6 @@ export default async function getBaseWebpackConfig(
'@builder.io/partytown': '{}',
'next/dist/compiled/etag': '{}',
'next/dist/compiled/chalk': '{}',
'./cjs/react-dom-server-legacy.browser.production.min.js':
'{}',
'./cjs/react-dom-server-legacy.browser.development.js':
'{}',
},
getEdgePolyfilledModules(),
handleWebpackExternalForEdgeRuntime,
Expand Down Expand Up @@ -1822,13 +1854,10 @@ export default async function getBaseWebpackConfig(
[require.resolve('next/dynamic')]: require.resolve(
'next/dist/shared/lib/app-dynamic'
),
'react/jsx-runtime$': `next/dist/compiled/react${bundledReactChannel}/jsx-runtime`,
'react/jsx-dev-runtime$': `next/dist/compiled/react${bundledReactChannel}/jsx-dev-runtime`,
'react-dom/server.edge$': `next/dist/compiled/react-dom${bundledReactChannel}/server.edge`,
'react-server-dom-webpack/client$': `next/dist/compiled/react-server-dom-webpack${bundledReactChannel}/client`,
'react-server-dom-webpack/client.edge$': `next/dist/compiled/react-server-dom-webpack${bundledReactChannel}/client.edge`,
'react-server-dom-webpack/server.edge$': `next/dist/compiled/react-server-dom-webpack${bundledReactChannel}/server.edge`,
'react-server-dom-webpack/server.node$': `next/dist/compiled/react-server-dom-webpack${bundledReactChannel}/server.node`,
...createReactAliases(bundledReactChannel, {
reactSharedSubset: false,
reactDomServerRenderingStub: false,
}),
},
},
},
Expand Down Expand Up @@ -1856,8 +1885,10 @@ export default async function getBaseWebpackConfig(
// If missing the alias override here, the default alias will be used which aliases
// react to the direct file path, not the package name. In that case the condition
// will be ignored completely.
react: `next/dist/compiled/react${bundledReactChannel}/react.shared-subset`,
'react-dom$': `next/dist/compiled/react-dom${bundledReactChannel}/server-rendering-stub`,
...createReactAliases(bundledReactChannel, {
reactSharedSubset: true,
reactDomServerRenderingStub: true,
}),
},
},
use: {
Expand Down Expand Up @@ -1920,37 +1951,40 @@ export default async function getBaseWebpackConfig(
// It needs `conditionNames` here to require the proper asset,
// when react is acting as dependency of compiled/react-dom.
alias: {
react: `next/dist/compiled/react${bundledReactChannel}/react.shared-subset`,
// Use server rendering stub for RSC
// x-ref: https://github.com/facebook/react/pull/25436
'react-dom$': `next/dist/compiled/react-dom${bundledReactChannel}/server-rendering-stub`,
},
},
},
{
issuerLayer: WEBPACK_LAYERS.client,
test: codeCondition.test,
resolve: {
alias: {
react: `next/dist/compiled/react${bundledReactChannel}`,
'react-dom$': `next/dist/compiled/react-dom${bundledReactChannel}/server-rendering-stub`,
...createReactAliases(bundledReactChannel, {
reactSharedSubset: true,
reactDomServerRenderingStub: true,
}),
},
},
},
{
test: codeCondition.test,
issuerLayer: WEBPACK_LAYERS.client,
resolve: {
alias: {
react: `next/dist/compiled/react${bundledReactChannel}`,
'react-dom$': reactProductionProfiling
? `next/dist/compiled/react-dom${bundledReactChannel}/cjs/react-dom.profiling.min`
: `next/dist/compiled/react-dom${bundledReactChannel}`,
'react-dom/client$': `next/dist/compiled/react-dom${bundledReactChannel}/client`,
...createReactAliases(bundledReactChannel, {
reactSharedSubset: false,
reactDomServerRenderingStub: true,
}),
},
},
},
],
},
{
test: codeCondition.test,
issuerLayer: WEBPACK_LAYERS.appClient,
resolve: {
alias: {
...createReactAliases(bundledReactChannel, {
// Only alias server rendering stub in client SSR layer.
reactSharedSubset: false,
reactDomServerRenderingStub: false,
}),
},
},
},
]
: []),
{
Expand Down
24 changes: 24 additions & 0 deletions packages/next/src/server/dev/hot-reloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export default class HotReloader {
private clientError: Error | null = null
private serverError: Error | null = null
private serverPrevDocumentHash: string | null
private serverChunkNames?: Set<string>
private prevChunkNames?: Set<any>
private onDemandEntries?: ReturnType<typeof onDemandEntryHandler>
private previewProps: __ApiPreviewProps
Expand Down Expand Up @@ -1050,6 +1051,7 @@ export default class HotReloader {
(err: Error) => {
this.serverError = err
this.serverStats = null
this.serverChunkNames = undefined
}
)

Expand Down Expand Up @@ -1093,16 +1095,38 @@ export default class HotReloader {
return
}

// As document chunk will change if new app pages are joined,
// since react bundle is different it will effect the chunk hash.
// So we diff the chunk changes, if there's only new app page chunk joins,
// then we don't trigger a reload by checking pages/_document chunk change.
if (this.appDir) {
const chunkNames = new Set(compilation.namedChunks.keys())
const diffChunkNames = difference<string>(
this.serverChunkNames || new Set(),
chunkNames
)

if (
diffChunkNames.length === 0 ||
diffChunkNames.every((chunkName) => chunkName.startsWith('app/'))
) {
return
}
this.serverChunkNames = chunkNames
}

// Notify reload to reload the page, as _document.js was changed (different hash)
this.send('reloadPage')
this.serverPrevDocumentHash = documentChunk.hash || null
}
)

this.multiCompiler.hooks.done.tap('NextjsHotReloaderForServer', () => {
const serverOnlyChanges = difference<string>(
changedServerPages,
changedClientPages
)

const edgeServerOnlyChanges = difference<string>(
changedEdgeServerPages,
changedClientPages
Expand Down
18 changes: 18 additions & 0 deletions packages/next/src/server/require-hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ function overrideReact() {
`next/dist/compiled/react-dom-experimental/server-rendering-stub`
),
],
[
'react/package.json',
require.resolve(`next/dist/compiled/react-experimental/package.json`),
],
[
'react-dom/package.json',
require.resolve(
`next/dist/compiled/react-dom-experimental/package.json`
),
],
[
'react-dom/client',
require.resolve(`next/dist/compiled/react-dom-experimental/client`),
Expand Down Expand Up @@ -102,6 +112,10 @@ function overrideReact() {
} else {
addHookAliases([
['react', require.resolve(`next/dist/compiled/react`)],
[
'react/package.json',
require.resolve(`next/dist/compiled/react/package.json`),
],
[
'react/jsx-runtime',
require.resolve(`next/dist/compiled/react/jsx-runtime`),
Expand All @@ -114,6 +128,10 @@ function overrideReact() {
'react-dom',
require.resolve(`next/dist/compiled/react-dom/server-rendering-stub`),
],
[
'react-dom/package.json',
require.resolve(`next/dist/compiled/react-dom/package.json`),
],
[
'react-dom/client',
require.resolve(`next/dist/compiled/react-dom/client`),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const runtime = 'experimental-edge'
export const runtime = 'edge'

export default (req) => {
return Response.json(Object.fromEntries(req.headers.entries()), {
Expand Down
12 changes: 11 additions & 1 deletion test/e2e/app-dir/rsc-basic/app/app-react/page.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import React from 'react'
import ReactDOM from 'react-dom'
import ReactDOMServer from 'react-dom/server.browser'

export default function Page() {
return <div>{'version=' + React.version}</div>
return (
<div>
<p id="react">{'React.version=' + React.version}</p>
<p id="react-dom">{'ReactDOM.version=' + ReactDOM.version}</p>
<p id="react-dom-server">
{'ReactDOMServer.version=' + ReactDOMServer.version}
</p>
</div>
)
}
17 changes: 17 additions & 0 deletions test/e2e/app-dir/rsc-basic/pages/edge-pages-react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'
import ReactDOM from 'react-dom'
import ReactDOMServer from 'react-dom/server'

export default function Page() {
return (
<div>
<p id="react">{'React.version=' + React.version}</p>
<p id="react-dom">{'ReactDOM.version=' + ReactDOM.version}</p>
<p id="react-dom-server">
{'ReactDOMServer.version=' + ReactDOMServer.version}
</p>
</div>
)
}

export const runtime = 'experimental-edge'
12 changes: 11 additions & 1 deletion test/e2e/app-dir/rsc-basic/pages/pages-react.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import React from 'react'
import ReactDOM from 'react-dom'
import ReactDOMServer from 'react-dom/server'

export default function Page() {
return <div>{'version=' + React.version}</div>
return (
<div>
<p id="react">{'React.version=' + React.version}</p>
<p id="react-dom">{'ReactDOM.version=' + ReactDOM.version}</p>
<p id="react-dom-server">
{'ReactDOMServer.version=' + ReactDOMServer.version}
</p>
</div>
)
}