Skip to content

Commit 6838f57

Browse files
huozhiztanner
authored andcommitted
Fix cjs client components tree-shaking (#64558)
## What Determine if the client module is a CJS file and `default` export is imported, then we include the whole module instead of using webpack magic comments to only extract `default` export. ## Why Unlike ESM, The `default` export of CJS module is not just `.default` property, we need to include `__esModule` mark along with `default` export to make it function properly with React client module proxy Fixes #64518 Closes NEXT-3119
1 parent 282de73 commit 6838f57

File tree

6 files changed

+60
-1
lines changed

6 files changed

+60
-1
lines changed

packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { normalizePathSep } from '../../../shared/lib/page-path/normalize-path-s
4141
import { getProxiedPluginState } from '../../build-context'
4242
import { PAGE_TYPES } from '../../../lib/page-types'
4343
import { isWebpackServerOnlyLayer } from '../../utils'
44+
import { getModuleBuildInfo } from '../loaders/get-module-build-info'
4445

4546
interface Options {
4647
dev: boolean
@@ -664,7 +665,16 @@ export class FlightClientEntryPlugin {
664665
if (!modRequest) return
665666
if (visited.has(modRequest)) {
666667
if (clientComponentImports[modRequest]) {
668+
const isCjsModule =
669+
getModuleBuildInfo(mod).rsc?.clientEntryType === 'cjs'
667670
for (const name of importedIdentifiers) {
671+
// For cjs module default import, we include the whole module since
672+
const isCjsDefaultImport = isCjsModule && name === 'default'
673+
// Always include __esModule along with cjs module default export,
674+
// to make sure it work with client module proxy from React.
675+
if (isCjsDefaultImport) {
676+
clientComponentImports[modRequest].add('__esModule')
677+
}
668678
clientComponentImports[modRequest].add(name)
669679
}
670680
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import CjsClientDefault from 'cjs-client-module'
2+
3+
export default function Page() {
4+
return <CjsClientDefault />
5+
}

test/production/app-dir/client-components-tree-shaking/index.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,32 @@ createNextDescribe(
7070
)
7171
).toBe(false)
7272
})
73+
74+
it('should only include the imported identifier of CJS module in browser bundle', async () => {
75+
const clientChunksDir = join(
76+
next.testDir,
77+
'.next',
78+
'static',
79+
'chunks',
80+
'app',
81+
'cjs-dep'
82+
)
83+
84+
const chunkContents = fs
85+
.readdirSync(clientChunksDir, {
86+
withFileTypes: true,
87+
})
88+
.filter((dirent) => dirent.isFile())
89+
.map((chunkDirent) =>
90+
fs.readFileSync(join(chunkDirent.path, chunkDirent.name), 'utf8')
91+
)
92+
93+
expect(
94+
chunkContents.some((content) => content.includes('cjs-client:default'))
95+
).toBe(true)
96+
expect(
97+
chunkContents.every((content) => content.includes('cjs-client:foo'))
98+
).toBe(false)
99+
})
73100
}
74101
)

test/production/app-dir/client-components-tree-shaking/node_modules/cjs-client-module/index.js

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/production/app-dir/client-components-tree-shaking/node_modules/cjs-client-module/package.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/turbopack-build-tests-manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14593,7 +14593,8 @@
1459314593
"passed": [],
1459414594
"failed": [
1459514595
"app-dir client-components-tree-shaking should only include imported components 3rd party package in browser bundle with direct imports",
14596-
"app-dir client-components-tree-shaking should only include imported relative components in browser bundle with direct imports"
14596+
"app-dir client-components-tree-shaking should only include imported relative components in browser bundle with direct imports",
14597+
"app-dir client-components-tree-shaking should only include the imported identifier of CJS module in browser bundle"
1459714598
],
1459814599
"pending": [],
1459914600
"flakey": [],

0 commit comments

Comments
 (0)