diff --git a/packages/next-swc/crates/next-api/src/app.rs b/packages/next-swc/crates/next-api/src/app.rs index 5c8d73a56b511..9f82537007f13 100644 --- a/packages/next-swc/crates/next-api/src/app.rs +++ b/packages/next-swc/crates/next-api/src/app.rs @@ -103,6 +103,7 @@ impl AppProject { fn route_ty(self: Vc) -> ServerContextType { ServerContextType::AppRoute { app_dir: self.app_dir(), + ecmascript_client_reference_transition_name: Some(self.client_transition_name()), } } @@ -328,8 +329,27 @@ impl AppProject { #[turbo_tasks::function] fn route_module_context(self: Vc) -> Vc { + let transitions = [ + ( + ECMASCRIPT_CLIENT_TRANSITION_NAME.to_string(), + Vc::upcast(NextEcmascriptClientReferenceTransition::new( + Vc::upcast(self.client_transition()), + self.ssr_transition(), + )), + ), + ( + "next-dynamic".to_string(), + Vc::upcast(NextDynamicTransition::new(Vc::upcast( + self.client_transition(), + ))), + ), + ("next-ssr".to_string(), Vc::upcast(self.ssr_transition())), + ] + .into_iter() + .collect(); + ModuleAssetContext::new( - Default::default(), + Vc::cell(transitions), self.project().server_compile_time_info(), self.route_module_options_context(), self.route_resolve_options_context(), diff --git a/packages/next-swc/crates/next-core/src/next_import_map.rs b/packages/next-swc/crates/next-core/src/next_import_map.rs index 76114d0e6b186..1be71bf8e7cc8 100644 --- a/packages/next-swc/crates/next-core/src/next_import_map.rs +++ b/packages/next-swc/crates/next-core/src/next_import_map.rs @@ -575,7 +575,7 @@ async fn insert_next_server_special_aliases( // the logic closely follows the one in createRSCAliases in webpack-config.ts ServerContextType::AppSSR { app_dir } | ServerContextType::AppRSC { app_dir, .. } - | ServerContextType::AppRoute { app_dir } => { + | ServerContextType::AppRoute { app_dir, .. } => { import_map.insert_exact_alias( "styled-jsx", request_to_import_mapping(get_next_package(app_dir), "styled-jsx"), diff --git a/packages/next-swc/crates/next-core/src/next_server/context.rs b/packages/next-swc/crates/next-core/src/next_server/context.rs index c044e1f32e01e..d4bd4703e92cf 100644 --- a/packages/next-swc/crates/next-core/src/next_server/context.rs +++ b/packages/next-swc/crates/next-core/src/next_server/context.rs @@ -93,6 +93,7 @@ pub enum ServerContextType { }, AppRoute { app_dir: Vc, + ecmascript_client_reference_transition_name: Option>, }, Middleware, Instrumentation, @@ -616,8 +617,27 @@ pub async fn get_server_module_options_context( ..module_options_context } } - ServerContextType::AppRoute { .. } => { + ServerContextType::AppRoute { + app_dir, + ecmascript_client_reference_transition_name, + } => { next_server_rules.extend(source_transform_rules); + if let Some(ecmascript_client_reference_transition_name) = + ecmascript_client_reference_transition_name + { + next_server_rules.push(get_ecma_transform_rule( + Box::new(ClientDirectiveTransformer::new( + ecmascript_client_reference_transition_name, + )), + enable_mdx_rs.is_some(), + true, + )); + } + + next_server_rules.push( + get_next_react_server_components_transform_rule(next_config, true, Some(app_dir)) + .await?, + ); let module_options_context = ModuleOptionsContext { esm_url_rewrite_behavior: Some(UrlRewriteBehavior::Full), diff --git a/test/e2e/app-dir/app-routes-client-component/ClientComponent.tsx b/test/e2e/app-dir/app-routes-client-component/ClientComponent.tsx new file mode 100644 index 0000000000000..bac9e866ae4d3 --- /dev/null +++ b/test/e2e/app-dir/app-routes-client-component/ClientComponent.tsx @@ -0,0 +1,5 @@ +'use client' + +export function ClientComponent() { + return
ClientComponent
+} diff --git a/test/e2e/app-dir/app-routes-client-component/app-routes-client-component.test.ts b/test/e2e/app-dir/app-routes-client-component/app-routes-client-component.test.ts new file mode 100644 index 0000000000000..c85903614f4d9 --- /dev/null +++ b/test/e2e/app-dir/app-routes-client-component/app-routes-client-component.test.ts @@ -0,0 +1,19 @@ +import { FileRef, nextTestSetup } from 'e2e-utils' +import path from 'path' + +describe('referencing a client component in an app route', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname)), + dependencies: { + react: 'latest', + 'react-dom': 'latest', + }, + }) + + it('responds without error', async () => { + expect(JSON.parse(await next.render('/runtime'))).toEqual({ + // Turbopack's proxy components are functions + clientComponent: process.env.TURBOPACK ? 'function' : 'object', + }) + }) +}) diff --git a/test/e2e/app-dir/app-routes-client-component/app/runtime/route.ts b/test/e2e/app-dir/app-routes-client-component/app/runtime/route.ts new file mode 100644 index 0000000000000..e4ce5094e904d --- /dev/null +++ b/test/e2e/app-dir/app-routes-client-component/app/runtime/route.ts @@ -0,0 +1,8 @@ +import { NextResponse } from 'next/server' +import { ClientComponent } from '../../ClientComponent' + +export function GET() { + return NextResponse.json({ + clientComponent: typeof ClientComponent, + }) +}