Skip to content

Commit

Permalink
Fix metadata layer webpack rule for server-only (#52403)
Browse files Browse the repository at this point in the history
After we separating the metadata routes to a separate layer, we didn't apply the webpack alias rules properly to it as it's should still be treated as pure "server" side

This PR fixes the aliasing for that new metadata layer and make it working properly with "server-only"

Fixes #52390
  • Loading branch information
huozhi committed Jul 9, 2023
1 parent 99490de commit 632a582
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 56 deletions.
100 changes: 46 additions & 54 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ const NEXT_PROJECT_ROOT_DIST_CLIENT = path.join(
'client'
)

const isWebpackServerLayer = (layer: string | null) =>
Boolean(layer && WEBPACK_LAYERS.GROUP.server.includes(layer))

if (parseInt(React.version) < 18) {
throw new Error('Next.js requires react >= 18.2.0 to be installed.')
}
Expand Down Expand Up @@ -1400,7 +1403,7 @@ export default async function getBaseWebpackConfig(
// so that the DefinePlugin can inject process.env values.

// Treat next internals as non-external for server layer
if (layer === WEBPACK_LAYERS.server || layer === WEBPACK_LAYERS.action) {
if (isWebpackServerLayer(layer)) {
return
}

Expand Down Expand Up @@ -1430,7 +1433,7 @@ export default async function getBaseWebpackConfig(
// Don't bundle @vercel/og nodejs bundle for nodejs runtime.
// TODO-APP: bundle route.js with different layer that externals common node_module deps.
if (
layer === WEBPACK_LAYERS.server &&
isWebpackServerLayer(layer) &&
request === 'next/dist/compiled/@vercel/og/index.node.js'
) {
return `module ${request}`
Expand Down Expand Up @@ -1574,7 +1577,7 @@ export default async function getBaseWebpackConfig(
(isEsm && isAppLayer)

if (/node_modules[/\\].*\.[mc]?js$/.test(res)) {
if (layer === WEBPACK_LAYERS.server || layer === WEBPACK_LAYERS.action) {
if (isWebpackServerLayer(layer)) {
// All packages should be bundled for the server layer if they're not opted out.
// This option takes priority over the transpilePackages option.

Expand Down Expand Up @@ -1944,6 +1947,13 @@ export default async function getBaseWebpackConfig(
layer: WEBPACK_LAYERS.shared,
test: asyncStoragesRegex,
},
// Convert metadata routes to separate layer
{
resourceQuery: new RegExp(
WEBPACK_RESOURCE_QUERIES.metadataRoute
),
layer: WEBPACK_LAYERS.metadataRoute,
},
{
// All app dir layers need to use this configured resolution logic
issuerLayer: {
Expand Down Expand Up @@ -1979,7 +1989,7 @@ export default async function getBaseWebpackConfig(
? [
{
issuerLayer: {
or: [WEBPACK_LAYERS.server, WEBPACK_LAYERS.action],
or: [isWebpackServerLayer],
},
test: {
// Resolve it if it is a source code file, and it has NOT been
Expand All @@ -1993,18 +2003,16 @@ export default async function getBaseWebpackConfig(
},
resolve: {
conditionNames: reactServerCondition,
alias: {
// 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.
...createRSCAliases(bundledReactChannel, {
reactSharedSubset: true,
reactDomServerRenderingStub: true,
reactServerCondition: true,
// No server components profiling
reactProductionProfiling,
}),
},
// 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.
alias: createRSCAliases(bundledReactChannel, {
reactSharedSubset: true,
reactDomServerRenderingStub: true,
reactServerCondition: true,
// No server components profiling
reactProductionProfiling,
}),
},
use: {
loader: 'next-flight-loader',
Expand All @@ -2024,16 +2032,6 @@ export default async function getBaseWebpackConfig(
} as any,
]
: []),
...(hasAppDir
? [
{
resourceQuery: new RegExp(
WEBPACK_RESOURCE_QUERIES.metadataRoute
),
layer: WEBPACK_LAYERS.metadataImage,
},
]
: []),
...(hasAppDir && isEdgeServer
? [
// The Edge bundle includes the server in its entrypoint, so it has to
Expand All @@ -2056,7 +2054,7 @@ export default async function getBaseWebpackConfig(
{
exclude: [asyncStoragesRegex],
issuerLayer: {
or: [WEBPACK_LAYERS.server, WEBPACK_LAYERS.action],
or: [isWebpackServerLayer],
},
test: {
// Resolve it if it is a source code file, and it has NOT been
Expand All @@ -2071,28 +2069,24 @@ export default async function getBaseWebpackConfig(
resolve: {
// It needs `conditionNames` here to require the proper asset,
// when react is acting as dependency of compiled/react-dom.
alias: {
...createRSCAliases(bundledReactChannel, {
reactSharedSubset: true,
reactDomServerRenderingStub: true,
reactServerCondition: true,
reactProductionProfiling,
}),
},
alias: createRSCAliases(bundledReactChannel, {
reactSharedSubset: true,
reactDomServerRenderingStub: true,
reactServerCondition: true,
reactProductionProfiling,
}),
},
},
{
test: codeCondition.test,
issuerLayer: WEBPACK_LAYERS.client,
resolve: {
alias: {
...createRSCAliases(bundledReactChannel, {
reactSharedSubset: false,
reactDomServerRenderingStub: true,
reactServerCondition: false,
reactProductionProfiling,
}),
},
alias: createRSCAliases(bundledReactChannel, {
reactSharedSubset: false,
reactDomServerRenderingStub: true,
reactServerCondition: false,
reactProductionProfiling,
}),
},
},
],
Expand All @@ -2101,15 +2095,13 @@ export default async function getBaseWebpackConfig(
test: codeCondition.test,
issuerLayer: WEBPACK_LAYERS.appClient,
resolve: {
alias: {
...createRSCAliases(bundledReactChannel, {
// Only alias server rendering stub in client SSR layer.
reactSharedSubset: false,
reactDomServerRenderingStub: false,
reactServerCondition: false,
reactProductionProfiling,
}),
},
alias: createRSCAliases(bundledReactChannel, {
// Only alias server rendering stub in client SSR layer.
reactSharedSubset: false,
reactDomServerRenderingStub: false,
reactServerCondition: false,
reactProductionProfiling,
}),
},
},
]
Expand All @@ -2135,7 +2127,7 @@ export default async function getBaseWebpackConfig(
{
test: codeCondition.test,
issuerLayer: {
or: [WEBPACK_LAYERS.server, WEBPACK_LAYERS.action],
or: [isWebpackServerLayer],
},
exclude: [asyncStoragesRegex],
use: swcLoaderForServerLayer,
Expand Down Expand Up @@ -2315,7 +2307,7 @@ export default async function getBaseWebpackConfig(
test: /(node_modules|next[/\\]dist[/\\]compiled)[/\\]client-only[/\\]error.js/,
loader: 'next-invalid-import-error-loader',
issuerLayer: {
or: [WEBPACK_LAYERS.server, WEBPACK_LAYERS.action],
or: [isWebpackServerLayer],
},
options: {
message:
Expand Down
15 changes: 13 additions & 2 deletions packages/next/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const SERVER_RUNTIME: Record<string, ServerRuntime> = {
nodejs: 'nodejs',
}

export const WEBPACK_LAYERS = {
const WEBPACK_LAYERS_NAMES = {
shared: 'sc_shared',
server: 'sc_server',
client: 'sc_client',
Expand All @@ -95,7 +95,18 @@ export const WEBPACK_LAYERS = {
middleware: 'middleware',
edgeAsset: 'edge-asset',
appClient: 'app-client',
metadataImage: 'app-metadata-image',
metadataRoute: 'app-metadata-route',
}

export const WEBPACK_LAYERS = {
...WEBPACK_LAYERS_NAMES,
GROUP: {
server: [
WEBPACK_LAYERS_NAMES.server,
WEBPACK_LAYERS_NAMES.action,
WEBPACK_LAYERS_NAMES.metadataRoute,
],
},
}

export const WEBPACK_RESOURCE_QUERIES = {
Expand Down
1 change: 1 addition & 0 deletions test/e2e/app-dir/metadata-dynamic-routes/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'server-only'
import { MetadataRoute } from 'next'

export default function sitemap(): MetadataRoute.Sitemap {
Expand Down

0 comments on commit 632a582

Please sign in to comment.