Skip to content

Commit

Permalink
Turbopack: Allow client components to be imported in app routes (#64520)
Browse files Browse the repository at this point in the history
Resolves #64412

This adds a client transition to the app route `ModuleAssetContext` and
the corresponding transforms so that client components can be safely
imported and referenced (as their proxies) in app routes.

Test Plan: Added an integration test


Closes PACK-2964
  • Loading branch information
wbinnssmith committed Apr 15, 2024
1 parent 24575b3 commit f794402
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 3 deletions.
22 changes: 21 additions & 1 deletion packages/next-swc/crates/next-api/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl AppProject {
fn route_ty(self: Vc<Self>) -> ServerContextType {
ServerContextType::AppRoute {
app_dir: self.app_dir(),
ecmascript_client_reference_transition_name: Some(self.client_transition_name()),
}
}

Expand Down Expand Up @@ -328,8 +329,27 @@ impl AppProject {

#[turbo_tasks::function]
fn route_module_context(self: Vc<Self>) -> Vc<ModuleAssetContext> {
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(),
Expand Down
2 changes: 1 addition & 1 deletion packages/next-swc/crates/next-core/src/next_import_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
22 changes: 21 additions & 1 deletion packages/next-swc/crates/next-core/src/next_server/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub enum ServerContextType {
},
AppRoute {
app_dir: Vc<FileSystemPath>,
ecmascript_client_reference_transition_name: Option<Vc<String>>,
},
Middleware,
Instrumentation,
Expand Down Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use client'

export function ClientComponent() {
return <div>ClientComponent</div>
}
Original file line number Diff line number Diff line change
@@ -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',
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { NextResponse } from 'next/server'
import { ClientComponent } from '../../ClientComponent'

export function GET() {
return NextResponse.json({
clientComponent: typeof ClientComponent,
})
}

1 comment on commit f794402

@ijjk
Copy link
Member

@ijjk ijjk commented on f794402 Apr 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stats from current release

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary v14.2.1 vercel/next.js canary Change
buildDuration 13.9s 26.9s ⚠️ +13s
buildDurationCached 7.6s 6.9s N/A
nodeModulesSize 199 MB 199 MB ⚠️ +54.4 kB
nextStartRea..uration (ms) 400ms 409ms N/A
Client Bundles (main, webpack)
vercel/next.js canary v14.2.1 vercel/next.js canary Change
2453-HASH.js gzip 31.4 kB 31.4 kB N/A
3304.HASH.js gzip 181 B 181 B
3f784ff6-HASH.js gzip 53.7 kB 53.7 kB
8299-HASH.js gzip 5.1 kB 5.1 kB N/A
framework-HASH.js gzip 45.2 kB 45.2 kB
main-app-HASH.js gzip 242 B 242 B
main-HASH.js gzip 32.2 kB 29.6 kB N/A
webpack-HASH.js gzip 1.68 kB 1.68 kB N/A
Overall change 99.3 kB 99.3 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary v14.2.1 vercel/next.js canary Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary v14.2.1 vercel/next.js canary Change
_app-HASH.js gzip 196 B 197 B N/A
_error-HASH.js gzip 184 B 184 B
amp-HASH.js gzip 505 B 505 B
css-HASH.js gzip 324 B 325 B N/A
dynamic-HASH.js gzip 2.5 kB 2.5 kB N/A
edge-ssr-HASH.js gzip 258 B 258 B
head-HASH.js gzip 352 B 352 B
hooks-HASH.js gzip 370 B 371 B N/A
image-HASH.js gzip 4.27 kB 4.27 kB
index-HASH.js gzip 259 B 259 B
link-HASH.js gzip 2.67 kB 2.67 kB N/A
routerDirect..HASH.js gzip 314 B 312 B N/A
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 309 B 309 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 6.63 kB 6.63 kB
Client Build Manifests
vercel/next.js canary v14.2.1 vercel/next.js canary Change
_buildManifest.js gzip 483 B 485 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary v14.2.1 vercel/next.js canary Change
index.html gzip 530 B 529 B N/A
link.html gzip 542 B 542 B
withRouter.html gzip 525 B 524 B N/A
Overall change 542 B 542 B
Edge SSR bundle Size
vercel/next.js canary v14.2.1 vercel/next.js canary Change
edge-ssr.js gzip 95.6 kB 95.6 kB N/A
page.js gzip 3.06 kB 3.05 kB N/A
Overall change 0 B 0 B
Middleware size
vercel/next.js canary v14.2.1 vercel/next.js canary Change
middleware-b..fest.js gzip 623 B 626 B N/A
middleware-r..fest.js gzip 155 B 156 B N/A
middleware.js gzip 25.5 kB 25.5 kB N/A
edge-runtime..pack.js gzip 839 B 839 B
Overall change 839 B 839 B
Next Runtimes
vercel/next.js canary v14.2.1 vercel/next.js canary Change
app-page-exp...dev.js gzip 171 kB 171 kB N/A
app-page-exp..prod.js gzip 97.4 kB 97.5 kB N/A
app-page-tur..prod.js gzip 99.2 kB 99.2 kB N/A
app-page-tur..prod.js gzip 93.4 kB 93.5 kB N/A
app-page.run...dev.js gzip 144 kB 145 kB N/A
app-page.run..prod.js gzip 91.9 kB 92 kB N/A
app-route-ex...dev.js gzip 21.5 kB 21.5 kB N/A
app-route-ex..prod.js gzip 15.2 kB 15.2 kB N/A
app-route-tu..prod.js gzip 15.2 kB 15.2 kB N/A
app-route-tu..prod.js gzip 14.9 kB 14.9 kB N/A
app-route.ru...dev.js gzip 21.1 kB 21.1 kB N/A
app-route.ru..prod.js gzip 14.9 kB 14.9 kB N/A
pages-api-tu..prod.js gzip 9.55 kB 9.55 kB
pages-api.ru...dev.js gzip 9.82 kB 9.82 kB
pages-api.ru..prod.js gzip 9.55 kB 9.55 kB
pages-turbo...prod.js gzip 22.5 kB 22.5 kB N/A
pages.runtim...dev.js gzip 23.1 kB 23.1 kB N/A
pages.runtim..prod.js gzip 22.5 kB 22.5 kB N/A
server.runti..prod.js gzip 51.3 kB 51.4 kB N/A
Overall change 28.9 kB 28.9 kB
build cache Overall increase ⚠️
vercel/next.js canary v14.2.1 vercel/next.js canary Change
0.pack gzip 1.58 MB 1.59 MB ⚠️ +2.23 kB
index.pack gzip 107 kB 108 kB ⚠️ +936 B
Overall change 1.69 MB 1.69 MB ⚠️ +3.17 kB
Diff details
Diff for page.js

Diff too large to display

Diff for middleware.js

Diff too large to display

Diff for edge-ssr.js

Diff too large to display

Diff for 2453-HASH.js

Diff too large to display

Diff for main-HASH.js

Diff too large to display

Diff for app-page-exp..ntime.dev.js
failed to diff
Diff for app-page-exp..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page.runtime.dev.js

Diff too large to display

Diff for app-page.runtime.prod.js

Diff too large to display

Diff for app-route-ex..ntime.dev.js

Diff too large to display

Diff for app-route-ex..time.prod.js

Diff too large to display

Diff for app-route-tu..time.prod.js

Diff too large to display

Diff for app-route-tu..time.prod.js

Diff too large to display

Diff for app-route.runtime.dev.js

Diff too large to display

Diff for app-route.ru..time.prod.js

Diff too large to display

Diff for pages-turbo...time.prod.js

Diff too large to display

Diff for pages.runtime.dev.js

Diff too large to display

Diff for pages.runtime.prod.js

Diff too large to display

Diff for server.runtime.prod.js

Diff too large to display

Please sign in to comment.