diff --git a/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_proxy_module.rs b/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_proxy_module.rs index 52a2237e4c233..aca812523ee92 100644 --- a/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_proxy_module.rs +++ b/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_proxy_module.rs @@ -70,11 +70,13 @@ impl EcmascriptClientReferenceProxyModule { #[turbo_tasks::function] async fn proxy_module(&self) -> Result>> { let mut code = CodeBuilder::default(); + let is_esm: bool; let server_module_path = &*self.server_module_ident.to_string().await?; // Adapted from https://github.com/facebook/react/blob/c5b9375767e2c4102d7e5559d383523736f1c902/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js#L323-L354 if let EcmascriptExports::EsmExports(exports) = &*self.client_module.get_exports().await? { + is_esm = true; let exports = exports.expand_exports().await?; if !exports.dynamic_exports.is_empty() { @@ -130,6 +132,7 @@ impl EcmascriptClientReferenceProxyModule { } } } else { + is_esm = false; writedoc!( code, r#" @@ -146,7 +149,13 @@ impl EcmascriptClientReferenceProxyModule { AssetContent::file(File::from(code.source_code().clone()).into()); let proxy_source = VirtualSource::new( - self.server_module_ident.path().join("proxy.js".into()), + self.server_module_ident.path().join( + // Depending on the original format, we call the file `proxy.mjs` or `proxy.cjs`. + // This is because we're placing the virtual module next to the original code, so + // its parsing will be affected by `type` fields in package.json -- + // a bare `proxy.js` may end up being unexpectedly parsed as the wrong format. + format!("proxy.{}", if is_esm { "mjs" } else { "cjs" }).into(), + ), proxy_module_content, ); diff --git a/test/e2e/app-dir/client-module-with-package-type/app/import-cjs/page.tsx b/test/e2e/app-dir/client-module-with-package-type/app/import-cjs/page.tsx new file mode 100644 index 0000000000000..60befa195889b --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/app/import-cjs/page.tsx @@ -0,0 +1,11 @@ +import * as React from 'react' + +import EsmFromCjs from 'lib-cjs' + +export default function Page() { + return ( +

+ lib-cjs: +

+ ) +} diff --git a/test/e2e/app-dir/client-module-with-package-type/app/import-esm/page.tsx b/test/e2e/app-dir/client-module-with-package-type/app/import-esm/page.tsx new file mode 100644 index 0000000000000..4469d96d5dc26 --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/app/import-esm/page.tsx @@ -0,0 +1,11 @@ +import * as React from 'react' + +import EsmFromEsm from 'lib-esm' + +export default function Page() { + return ( +

+ lib-esm: +

+ ) +} diff --git a/test/e2e/app-dir/client-module-with-package-type/app/layout.tsx b/test/e2e/app-dir/client-module-with-package-type/app/layout.tsx new file mode 100644 index 0000000000000..888614deda3ba --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/app/layout.tsx @@ -0,0 +1,8 @@ +import { ReactNode } from 'react' +export default function Root({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/test/e2e/app-dir/client-module-with-package-type/app/require-cjs/page.tsx b/test/e2e/app-dir/client-module-with-package-type/app/require-cjs/page.tsx new file mode 100644 index 0000000000000..1dc7a4bf66786 --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/app/require-cjs/page.tsx @@ -0,0 +1,11 @@ +import * as React from 'react' + +const CjsFromCjs = require('lib-cjs') + +export default function Page() { + return ( +

+ lib-cjs: +

+ ) +} diff --git a/test/e2e/app-dir/client-module-with-package-type/app/require-esm/page.tsx b/test/e2e/app-dir/client-module-with-package-type/app/require-esm/page.tsx new file mode 100644 index 0000000000000..fb20339de17b0 --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/app/require-esm/page.tsx @@ -0,0 +1,11 @@ +import * as React from 'react' + +const CjsFromEsm = require('lib-esm') + +export default function Page() { + return ( +

+ lib-esm: +

+ ) +} diff --git a/test/e2e/app-dir/client-module-with-package-type/index.test.ts b/test/e2e/app-dir/client-module-with-package-type/index.test.ts new file mode 100644 index 0000000000000..7b70d442bab65 --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/index.test.ts @@ -0,0 +1,31 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('esm-client-module-without-exports', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + describe('"type": "commonjs" in package.json', () => { + it('should render without errors: import cjs', async () => { + const $ = await next.render$('/import-cjs') + expect($('p').text()).toContain('lib-cjs: esm') + }) + + it('should render without errors: require cjs', async () => { + const $ = await next.render$('/require-cjs') + expect($('p').text()).toContain('lib-cjs: cjs') + }) + }) + + describe('"type": "module" in package.json', () => { + it('should render without errors: import esm', async () => { + const $ = await next.render$('/import-esm') + expect($('p').text()).toContain('lib-esm: esm') + }) + + it('should render without errors: require esm', async () => { + const $ = await next.render$('/require-esm') + expect($('p').text()).toContain('lib-esm: cjs') + }) + }) +}) diff --git a/test/e2e/app-dir/client-module-with-package-type/next.config.js b/test/e2e/app-dir/client-module-with-package-type/next.config.js new file mode 100644 index 0000000000000..807126e4cf0bf --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/next.config.js @@ -0,0 +1,6 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/index.js b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/index.js new file mode 100644 index 0000000000000..271c41e8ce833 --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/index.js @@ -0,0 +1,3 @@ +'use client' +console.log('lib-cjs :: cjs') +module.exports = () => 'cjs' diff --git a/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/index.mjs b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/index.mjs new file mode 100644 index 0000000000000..8a112198a72ef --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/index.mjs @@ -0,0 +1,3 @@ +'use client' +console.log('lib-cjs :: esm') +export default () => 'esm' diff --git a/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/package.json b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/package.json new file mode 100644 index 0000000000000..a8d5b97fc0b92 --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-cjs/package.json @@ -0,0 +1,10 @@ +{ + "name": "lib-cjs", + "type": "commonjs", + "exports": { + ".": { + "import": "./index.mjs", + "default": "./index.js" + } + } +} diff --git a/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/index.cjs b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/index.cjs new file mode 100644 index 0000000000000..0b2a17179b5d3 --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/index.cjs @@ -0,0 +1,3 @@ +'use client' +console.log('lib-esm :: cjs') +module.exports = () => 'cjs' diff --git a/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/index.js b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/index.js new file mode 100644 index 0000000000000..2fc1856c0840a --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/index.js @@ -0,0 +1,3 @@ +'use client' +console.log('lib-esm :: esm') +export default () => 'esm' diff --git a/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/package.json b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/package.json new file mode 100644 index 0000000000000..e432daa17225d --- /dev/null +++ b/test/e2e/app-dir/client-module-with-package-type/node_modules/lib-esm/package.json @@ -0,0 +1,10 @@ +{ + "name": "lib-esm", + "type": "module", + "exports": { + ".": { + "require": "./index.cjs", + "default": "./index.js" + } + } +}