diff --git a/packages/next/src/build/handle-externals.ts b/packages/next/src/build/handle-externals.ts index 5b38dd26e0994..9ca94214035f3 100644 --- a/packages/next/src/build/handle-externals.ts +++ b/packages/next/src/build/handle-externals.ts @@ -10,7 +10,7 @@ import { NODE_ESM_RESOLVE_OPTIONS, NODE_RESOLVE_OPTIONS, } from './webpack-config' -import { isWebpackBundledLayer, isWebpackServerOnlyLayer } from './utils' +import { isWebpackAppLayer, isWebpackServerOnlyLayer } from './utils' import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep' const reactPackagesRegex = /^(react|react-dom|react-server-dom-webpack)($|\/)/ @@ -174,7 +174,7 @@ export function makeExternalHandler({ return `commonjs next/dist/lib/import-next-warning` } - const isAppLayer = isWebpackBundledLayer(layer) + const isAppLayer = isWebpackAppLayer(layer) // Relative requires don't need custom resolution, because they // are relative to requests we've already resolved here. diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index b4f583f08d461..9a1e1ae14b2d8 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -2241,15 +2241,6 @@ export function getSupportedBrowsers( return MODERN_BROWSERSLIST_TARGET } -// Use next/dist/compiled/react packages instead of installed react -export function isWebpackBuiltinReactLayer( - layer: WebpackLayerName | null | undefined -): boolean { - return Boolean( - layer && WEBPACK_LAYERS.GROUP.builtinReact.includes(layer as any) - ) -} - export function isWebpackServerOnlyLayer( layer: WebpackLayerName | null | undefined ): boolean { @@ -2272,8 +2263,8 @@ export function isWebpackDefaultLayer( return layer === null || layer === undefined } -export function isWebpackBundledLayer( +export function isWebpackAppLayer( layer: WebpackLayerName | null | undefined ): boolean { - return Boolean(layer && WEBPACK_LAYERS.GROUP.bundled.includes(layer as any)) + return Boolean(layer && WEBPACK_LAYERS.GROUP.app.includes(layer as any)) } diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index be7c79c6afc86..42ceb0368f877 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -9,8 +9,7 @@ import { escapeStringRegexp } from '../shared/lib/escape-regexp' import { WEBPACK_LAYERS, WEBPACK_RESOURCE_QUERIES } from '../lib/constants' import type { WebpackLayerName } from '../lib/constants' import { - isWebpackBuiltinReactLayer, - isWebpackBundledLayer, + isWebpackAppLayer, isWebpackClientOnlyLayer, isWebpackDefaultLayer, isWebpackServerOnlyLayer, @@ -544,7 +543,7 @@ export default async function getBaseWebpackConfig( // This will cause some performance overhead but // acceptable as Babel will not be recommended. getSwcLoader({ - serverComponents: true, + serverComponents: false, bundleLayer: WEBPACK_LAYERS.middleware, }), babelLoader, @@ -593,12 +592,13 @@ export default async function getBaseWebpackConfig( // Loader for API routes needs to be differently configured as it shouldn't // have RSC transpiler enabled, so syntax checks such as invalid imports won't // be performed. - const apiRoutesLayerLoaders = useSWCLoader - ? getSwcLoader({ - serverComponents: false, - bundleLayer: WEBPACK_LAYERS.api, - }) - : defaultLoaders.babel + const apiRoutesLayerLoaders = + hasAppDir && useSWCLoader + ? getSwcLoader({ + serverComponents: false, + bundleLayer: WEBPACK_LAYERS.api, + }) + : defaultLoaders.babel const pageExtensions = config.pageExtensions @@ -1304,7 +1304,7 @@ export default async function getBaseWebpackConfig( test: /next[\\/]dist[\\/](esm[\\/])?server[\\/]future[\\/]route-modules[\\/]app-page[\\/]module/, }, { - issuerLayer: isWebpackBundledLayer, + issuerLayer: isWebpackAppLayer, resolve: { alias: createNextApiEsmAliases(), }, @@ -1326,7 +1326,7 @@ export default async function getBaseWebpackConfig( ...(hasAppDir && !isClient ? [ { - issuerLayer: isWebpackBuiltinReactLayer, + issuerLayer: isWebpackServerOnlyLayer, test: { // Resolve it if it is a source code file, and it has NOT been // opted out of bundling. @@ -1388,7 +1388,7 @@ export default async function getBaseWebpackConfig( // Alias react for switching between default set and share subset. oneOf: [ { - issuerLayer: isWebpackBuiltinReactLayer, + issuerLayer: isWebpackServerOnlyLayer, test: { // Resolve it if it is a source code file, and it has NOT been // opted out of bundling. @@ -1469,17 +1469,11 @@ export default async function getBaseWebpackConfig( test: codeCondition.test, issuerLayer: WEBPACK_LAYERS.middleware, use: middlewareLayerLoaders, - resolve: { - conditionNames: reactServerCondition, - }, }, { test: codeCondition.test, issuerLayer: WEBPACK_LAYERS.instrument, use: instrumentLayerLoaders, - resolve: { - conditionNames: reactServerCondition, - }, }, ...(hasAppDir ? [ diff --git a/packages/next/src/lib/constants.ts b/packages/next/src/lib/constants.ts index c098c8da62499..c250a65b4afbb 100644 --- a/packages/next/src/lib/constants.ts +++ b/packages/next/src/lib/constants.ts @@ -159,11 +159,6 @@ export type WebpackLayerName = const WEBPACK_LAYERS = { ...WEBPACK_LAYERS_NAMES, GROUP: { - builtinReact: [ - WEBPACK_LAYERS_NAMES.reactServerComponents, - WEBPACK_LAYERS_NAMES.actionBrowser, - WEBPACK_LAYERS_NAMES.appMetadataRoute, - ], serverOnly: [ WEBPACK_LAYERS_NAMES.reactServerComponents, WEBPACK_LAYERS_NAMES.actionBrowser, @@ -179,7 +174,7 @@ const WEBPACK_LAYERS = { WEBPACK_LAYERS_NAMES.serverSideRendering, WEBPACK_LAYERS_NAMES.appPagesBrowser, ], - bundled: [ + app: [ WEBPACK_LAYERS_NAMES.reactServerComponents, WEBPACK_LAYERS_NAMES.actionBrowser, WEBPACK_LAYERS_NAMES.appMetadataRoute, diff --git a/test/e2e/module-layer/middleware.js b/test/e2e/module-layer/middleware.js index 894d7888512dd..8a4d11761dd78 100644 --- a/test/e2e/module-layer/middleware.js +++ b/test/e2e/module-layer/middleware.js @@ -7,10 +7,5 @@ export function middleware(request) { if (React.useState) { throw new Error('React.useState should not be defined in server layer') } - - if (request.nextUrl.pathname === '/react-version') { - return new Response(React.version) - } - return NextResponse.next() } diff --git a/test/e2e/module-layer/module-layer.test.ts b/test/e2e/module-layer/module-layer.test.ts index b872cb076054d..bf665e1428df8 100644 --- a/test/e2e/module-layer/module-layer.test.ts +++ b/test/e2e/module-layer/module-layer.test.ts @@ -4,10 +4,6 @@ import { getRedboxSource, hasRedbox, retry } from 'next-test-utils' describe('module layer', () => { const { next, isNextStart, isNextDev, isTurbopack } = nextTestSetup({ files: __dirname, - dependencies: { - react: '19.0.0-rc-915b914b3a-20240515', - 'react-dom': '19.0.0-rc-915b914b3a-20240515', - }, }) function runTests() { @@ -34,11 +30,6 @@ describe('module layer', () => { }) } - it('should render installed react version for middleware', async () => { - const text = await next.fetch('/react-version').then((res) => res.text()) - expect(text).toContain('19.0.0-rc') - }) - if (isNextStart) { it('should log the build info properly', async () => { const cliOutput = next.cliOutput @@ -90,15 +81,22 @@ describe('module layer', () => { .replace("// import './lib/mixed-lib'", "import './lib/mixed-lib'") ) + const existingCliOutputLength = next.cliOutput.length await retry(async () => { expect(await hasRedbox(browser)).toBe(true) const source = await getRedboxSource(browser) expect(source).toContain( - isTurbopack - ? `'client-only' cannot be imported from a Server Component module. It should only be used from a Client Component.` - : `You're importing a component that imports client-only. It only works in a Client Component but none of its parents are marked with "use client"` + `'client-only' cannot be imported from a Server Component module. It should only be used from a Client Component.` ) }) + + if (!isTurbopack) { + const newCliOutput = next.cliOutput.slice(existingCliOutputLength) + expect(newCliOutput).toContain('./middleware.js') + expect(newCliOutput).toContain( + `'client-only' cannot be imported from a Server Component module. It should only be used from a Client Component` + ) + } }) }) }