Skip to content

Commit c4bf4ac

Browse files
authored
backport: force module format for virtual client-proxy (#74162) (#74590)
Fixes #74062 (`jotai` ran into this error [when they added `"type": "commonjs"` to their package.json](pmndrs/jotai#2579 (reply in thread))) > this is a bug in the transform we do when a `'use client'` directive is encountered. I think what's happening is that we're creating a virtual file that uses ESM import/export syntax, but it's called proxy.js (not proxy.mjs), so the `"type": "commonjs" `makes turbopack "correctly" upset at the ESM syntax. #74062 (comment) The (slightly kludgy) solution is to use `proxy.mjs` or `proxy.cjs` to force the module format, bypassing the issue where `proxy.js` changes meaning depending on `package.json#type`.
1 parent d60bb1b commit c4bf4ac

File tree

14 files changed

+131
-1
lines changed

14 files changed

+131
-1
lines changed

packages/next-swc/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_proxy_module.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@ impl EcmascriptClientReferenceProxyModule {
6767
#[turbo_tasks::function]
6868
async fn proxy_module(&self) -> Result<Vc<EcmascriptModuleAsset>> {
6969
let mut code = CodeBuilder::default();
70+
let is_esm: bool;
7071

7172
let server_module_path = &*self.server_module_ident.path().to_string().await?;
7273

7374
// Adapted from https://github.com/facebook/react/blob/c5b9375767e2c4102d7e5559d383523736f1c902/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js#L323-L354
7475
if let EcmascriptExports::EsmExports(exports) = &*self.client_module.get_exports().await? {
76+
is_esm = true;
7577
let exports = exports.expand_exports().await?;
7678

7779
if !exports.dynamic_exports.is_empty() {
@@ -127,6 +129,7 @@ impl EcmascriptClientReferenceProxyModule {
127129
}
128130
}
129131
} else {
132+
is_esm = false;
130133
writedoc!(
131134
code,
132135
r#"
@@ -143,7 +146,13 @@ impl EcmascriptClientReferenceProxyModule {
143146
AssetContent::file(File::from(code.source_code().clone()).into());
144147

145148
let proxy_source = VirtualSource::new(
146-
self.server_module_ident.path().join("proxy.js".to_string()),
149+
self.server_module_ident.path().join(
150+
// Depending on the original format, we call the file `proxy.mjs` or `proxy.cjs`.
151+
// This is because we're placing the virtual module next to the original code, so
152+
// its parsing will be affected by `type` fields in package.json --
153+
// a bare `proxy.js` may end up being unexpectedly parsed as the wrong format.
154+
format!("proxy.{}", if is_esm { "mjs" } else { "cjs" }),
155+
),
147156
proxy_module_content,
148157
);
149158

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from 'react'
2+
3+
import EsmFromCjs from 'lib-cjs'
4+
5+
export default function Page() {
6+
return (
7+
<p>
8+
lib-cjs: <EsmFromCjs />
9+
</p>
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from 'react'
2+
3+
import EsmFromEsm from 'lib-esm'
4+
5+
export default function Page() {
6+
return (
7+
<p>
8+
lib-esm: <EsmFromEsm />
9+
</p>
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ReactNode } from 'react'
2+
export default function Root({ children }: { children: ReactNode }) {
3+
return (
4+
<html>
5+
<body>{children}</body>
6+
</html>
7+
)
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from 'react'
2+
3+
const CjsFromCjs = require('lib-cjs')
4+
5+
export default function Page() {
6+
return (
7+
<p>
8+
lib-cjs: <CjsFromCjs />
9+
</p>
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from 'react'
2+
3+
const CjsFromEsm = require('lib-esm')
4+
5+
export default function Page() {
6+
return (
7+
<p>
8+
lib-esm: <CjsFromEsm />
9+
</p>
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
3+
describe('esm-client-module-without-exports', () => {
4+
const { next } = nextTestSetup({
5+
files: __dirname,
6+
})
7+
8+
describe('"type": "commonjs" in package.json', () => {
9+
it('should render without errors: import cjs', async () => {
10+
const $ = await next.render$('/import-cjs')
11+
expect($('p').text()).toContain('lib-cjs: esm')
12+
})
13+
14+
it('should render without errors: require cjs', async () => {
15+
const $ = await next.render$('/require-cjs')
16+
expect($('p').text()).toContain('lib-cjs: cjs')
17+
})
18+
})
19+
20+
describe('"type": "module" in package.json', () => {
21+
it('should render without errors: import esm', async () => {
22+
const $ = await next.render$('/import-esm')
23+
expect($('p').text()).toContain('lib-esm: esm')
24+
})
25+
26+
it('should render without errors: require esm', async () => {
27+
const $ = await next.render$('/require-esm')
28+
expect($('p').text()).toContain('lib-esm: cjs')
29+
})
30+
})
31+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* @type {import('next').NextConfig}
3+
*/
4+
const nextConfig = {}
5+
6+
module.exports = nextConfig

test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/index.js

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/index.mjs

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/package.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/index.cjs

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/index.js

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/package.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)