Skip to content

Commit e650047

Browse files
authored
Fix package resolution issue in app dir (vercel#43349)
The server and client compilers have different external dependency resolution logic. The server prefers CJS while the client prefers ESM by default. This causes an issue when that dependency is a client component entry. We get the module reference during RSC render, which is the CJS file path. And that module reference can't be located in the flight manifest as it was generated via the client compiler (has the ESM path). This PR changes the entry creation logic to use resolved requests, instead of the raw requests. So both SSR and client will have the same resolved import paths to ensure the module reference consistency. ## Bug - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
1 parent bcb0e28 commit e650047

File tree

6 files changed

+32
-16
lines changed

6 files changed

+32
-16
lines changed

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

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
entries,
1313
EntryTypes,
1414
} from '../../../server/dev/on-demand-entry-handler'
15-
import { APP_DIR_ALIAS, WEBPACK_LAYERS } from '../../../lib/constants'
15+
import { WEBPACK_LAYERS } from '../../../lib/constants'
1616
import {
1717
APP_CLIENT_INTERNALS,
1818
COMPILER_NAMES,
@@ -379,23 +379,14 @@ export class FlightClientEntryPlugin {
379379
compilation.moduleGraph.getResolvedModule(dependencyToFilter)
380380
if (!mod) return
381381

382-
// Keep client imports as simple
383-
// native or installed js module: -> raw request, e.g. next/head
384-
// client js or css: -> user request
385382
const rawRequest = mod.rawRequest
386-
// Request could be undefined or ''
387-
if (!rawRequest) return
388-
389383
const isCSS = regexCSS.test(rawRequest)
390-
const isLocal =
391-
!isCSS &&
392-
!rawRequest.startsWith('.') &&
393-
!rawRequest.startsWith('/') &&
394-
!rawRequest.startsWith(APP_DIR_ALIAS)
395-
396-
const modRequest: string | undefined = isLocal
397-
? rawRequest
398-
: mod.resourceResolveData?.path + mod.resourceResolveData?.query
384+
385+
// We have to always use the resolved request here to make sure the
386+
// server and client are using the same module path (required by RSC), as
387+
// the server compiler and client compiler have different resolve configs.
388+
const modRequest: string | undefined =
389+
mod.resourceResolveData?.path + mod.resourceResolveData?.query
399390

400391
// Ensure module is not walked again if it's already been visited
401392
if (!visitedBySegment[entryRequest]) {

test/e2e/app-dir/app-external.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ describe('app dir - external dependency', () => {
147147
).toBe('rgb(255, 0, 0)')
148148
})
149149

150+
it('should use the same export type for packages in both ssr and client', async () => {
151+
const browser = await webdriver(next.url, '/client-dep')
152+
expect(await browser.eval(`window.document.body.innerText`)).toBe('hello')
153+
})
154+
150155
it('should handle external css modules in pages', async () => {
151156
const browser = await webdriver(next.url, '/test-pages')
152157

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Foo from 'client-module'
2+
3+
export default function Index() {
4+
return (
5+
<div>
6+
<Foo />
7+
</div>
8+
)
9+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use client'
2+
3+
module.exports = () => 'hello'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use client'
2+
3+
export default () => 'hello'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "client-module",
3+
"main": "index.js",
4+
"module": "index.mjs"
5+
}

0 commit comments

Comments
 (0)