From 7e9627a39087732fda61576e14aa1a5f485756d2 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Mon, 17 Jul 2023 19:41:04 +0200 Subject: [PATCH] Fix tracking of ContextModule (#52795) When doing dynamic imports like `import(variable)`, Webpack _tries_ to statically analyze it and creates a regex like context module for it (which includes all possible modules). This `ContextModule` doesn't have a resource path so we need to use the identifier to track it. Tested with @alexkirsz's repro here https://github.com/vercel/next.js/issues/50243#issuecomment-1628675346 and confirmed that it fixes the problem. Closes #50243. --- .../build/webpack/plugins/flight-client-entry-plugin.ts | 7 ++++++- test/e2e/app-dir/rsc-basic/app/dynamic/_dynamic.js | 8 ++++++++ test/e2e/app-dir/rsc-basic/app/dynamic/page.js | 5 +++++ test/e2e/app-dir/rsc-basic/rsc-basic.test.ts | 5 +++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/e2e/app-dir/rsc-basic/app/dynamic/_dynamic.js create mode 100644 test/e2e/app-dir/rsc-basic/app/dynamic/page.js diff --git a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts index 78b0de8ef02ff..839b200627841 100644 --- a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts @@ -586,9 +586,14 @@ export class FlightClientEntryPlugin { // We have to always use the resolved request here to make sure the // server and client are using the same module path (required by RSC), as // the server compiler and client compiler have different resolve configs. - const modRequest: string | undefined = + let modRequest: string | undefined = mod.resourceResolveData?.path + mod.resourceResolveData?.query + // Context modules don't have a resource path, we use the identifier instead. + if (mod.constructor.name === 'ContextModule') { + modRequest = (mod as any)._identifier + } + if (!modRequest || visited.has(modRequest)) return visited.add(modRequest) diff --git a/test/e2e/app-dir/rsc-basic/app/dynamic/_dynamic.js b/test/e2e/app-dir/rsc-basic/app/dynamic/_dynamic.js new file mode 100644 index 0000000000000..6257434270014 --- /dev/null +++ b/test/e2e/app-dir/rsc-basic/app/dynamic/_dynamic.js @@ -0,0 +1,8 @@ +'use client' + +import { useState } from 'react' + +export default function Dynamic() { + const [data] = useState('dynamic data!') + return

{data}

+} diff --git a/test/e2e/app-dir/rsc-basic/app/dynamic/page.js b/test/e2e/app-dir/rsc-basic/app/dynamic/page.js new file mode 100644 index 0000000000000..da86960ade321 --- /dev/null +++ b/test/e2e/app-dir/rsc-basic/app/dynamic/page.js @@ -0,0 +1,5 @@ +export default async function Page() { + const dynamic = '_dynamic' + const { default: Component } = await import(`./${dynamic}.js`) + return +} diff --git a/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts b/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts index 2465e6d6a6b33..a3a29d3a8dfbe 100644 --- a/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts +++ b/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts @@ -193,6 +193,11 @@ createNextDescribe( expect(content).toMatchInlineSnapshot('"next_streaming_data"') }) + it('should track client components in dynamic imports', async () => { + const html = await next.render('/dynamic') + expect(html).toContain('dynamic data!') + }) + it('should support next/link in server components', async () => { const $ = await next.render$('/next-api/link') const linkText = $('body a[href="/root"]').text()