diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 82d9cb2d9e6..b6326c8c4ad 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1598,7 +1598,12 @@ export default async function getBaseWebpackConfig( if (!webpack5Config.optimization) { webpack5Config.optimization = {} } - webpack5Config.optimization.providedExports = false + + // For Server Components, it's necessary to have provided exports collected + // to generate the correct flight manifest. + if (!hasServerComponents) { + webpack5Config.optimization.providedExports = false + } webpack5Config.optimization.usedExports = false } diff --git a/packages/next/build/webpack/loaders/next-flight-client-loader.ts b/packages/next/build/webpack/loaders/next-flight-client-loader.ts index afe614e8590..c08689af661 100644 --- a/packages/next/build/webpack/loaders/next-flight-client-loader.ts +++ b/packages/next/build/webpack/loaders/next-flight-client-loader.ts @@ -75,6 +75,11 @@ async function parseExportNamesInto( } } continue + case 'ExportDeclaration': + if (node.declaration?.identifier) { + addExportNames(names, node.declaration.identifier) + } + continue default: break } diff --git a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts index 29cbd09aac8..dbc3ecc70f5 100644 --- a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts @@ -82,12 +82,15 @@ export class FlightManifestPlugin { const moduleExports: any = json[resource] || {} const exportsInfo = compilation.moduleGraph.getExportsInfo(mod) - const providedExports = exportsInfo.getProvidedExports() const moduleExportedKeys = ['', '*'].concat( - // TODO: improve exports detection - providedExports === true || providedExports == null - ? 'default' - : providedExports + [...exportsInfo.exports] + .map((exportInfo) => { + if (exportInfo.provided) { + return exportInfo.name + } + return null + }) + .filter(Boolean) ) moduleExportedKeys.forEach((name) => { diff --git a/test/integration/react-streaming-and-server-components/app/components/named.client.js b/test/integration/react-streaming-and-server-components/app/components/named.client.js new file mode 100644 index 00000000000..9f087bbc83c --- /dev/null +++ b/test/integration/react-streaming-and-server-components/app/components/named.client.js @@ -0,0 +1,3 @@ +export function Named() { + return 'named export: named.client' +} diff --git a/test/integration/react-streaming-and-server-components/app/pages/index.server.js b/test/integration/react-streaming-and-server-components/app/pages/index.server.js index cec07820c64..d5e157ca6a1 100644 --- a/test/integration/react-streaming-and-server-components/app/pages/index.server.js +++ b/test/integration/react-streaming-and-server-components/app/pages/index.server.js @@ -1,4 +1,6 @@ import Foo from '../components/foo.client' +import { Named } from '../components/named.client' + import Link from 'next/link' const envVar = process.env.ENV_VAR_TEST @@ -11,6 +13,9 @@ export default function Index({ header, router }) {
{'path:' + router.pathname}
{'env:' + envVar}
{'header:' + header}
+
+ +
diff --git a/test/integration/react-streaming-and-server-components/test/rsc.js b/test/integration/react-streaming-and-server-components/test/rsc.js index 6e8b76d8c2a..da7130b826b 100644 --- a/test/integration/react-streaming-and-server-components/test/rsc.js +++ b/test/integration/react-streaming-and-server-components/test/rsc.js @@ -28,6 +28,7 @@ export default function (context, { runtime, env }) { expect(homeHTML).toContain('header:test-util') expect(homeHTML).toContain('path:/') expect(homeHTML).toContain('foo.client') + expect(homeHTML).toContain('named.client') }) it('should reuse the inline flight response without sending extra requests', async () => {