Repository: vercel/next.js
Pull request: Turbopack: don't execute action loader eagerly

The server-reference-manifest.json contains the chunk item id of the action loader. The loader itself doesn't "register" itself in any way. So no need to execute it eagerly.

Closes PACK-5362

Changed files:
filename: crates/next-api/src/app.rs
patch: @@ -1748,7 +1748,7 @@ impl AppEndpoint {
                     assets,
                     availability_info,
                 } = *chunking_context
-                    .evaluated_chunk_group(
+                    .chunk_group(
                         server_action_manifest_loader.ident(),
                         ChunkGroup::Entry(
                             [ResolvedVc::upcast(server_action_manifest_loader)]
@@ -1785,7 +1785,6 @@ impl AppEndpoint {
                     bail!("rsc_entry must be evaluatable");
                 };
 
-                evaluatable_assets.push(server_action_manifest_loader);
                 evaluatable_assets.push(rsc_entry);
 
                 async {
@@ -1864,6 +1863,25 @@ impl AppEndpoint {
                         .await?;
                     }
 
+                    {
+                        let chunk_group = chunking_context
+                            .chunk_group(
+                                server_action_manifest_loader.ident(),
+                                ChunkGroup::Entry(vec![ResolvedVc::upcast(
+                                    server_action_manifest_loader,
+                                )]),
+                                module_graph,
+                                current_availability_info,
+                            )
+                            .await?;
+
+                        current_chunks = current_chunks
+                            .concatenate(*chunk_group.assets)
+                            .resolve()
+                            .await?;
+                        current_availability_info = chunk_group.availability_info;
+                    }
+
                     anyhow::Ok(Vc::cell(vec![
                         chunking_context
                             .entry_chunk_group_asset(

filename: crates/next-api/src/server_actions.rs
patch: @@ -19,11 +19,13 @@ use swc_core::{
     },
 };
 use turbo_rcstr::{RcStr, rcstr};
-use turbo_tasks::{FxIndexMap, ResolvedVc, TryFlatJoinIterExt, ValueToString, Vc};
+use turbo_tasks::{FxIndexMap, ResolvedVc, TryFlatJoinIterExt, Vc};
 use turbo_tasks_fs::{self, File, FileSystemPath, rope::RopeBuilder};
 use turbopack_core::{
     asset::AssetContent,
-    chunk::{ChunkItem, ChunkItemExt, ChunkableModule, ChunkingContext, EvaluatableAsset},
+    chunk::{
+        ChunkItem, ChunkItemExt, ChunkableModule, ChunkingContext, EvaluatableAsset, ModuleId,
+    },
     context::AssetContext,
     file_source::FileSource,
     ident::AssetIdent,
@@ -168,7 +170,11 @@ async fn build_manifest(
     let key = format!("app{page_name}");
 
     let actions_value = actions.await?;
-    let loader_id = chunk_item.id().to_string().await?;
+    let loader_id = chunk_item.id().await?;
+    let loader_id = match &*loader_id {
+        ModuleId::Number(id) => ActionManifestModuleId::Number(*id),
+        ModuleId::String(id) => ActionManifestModuleId::String(id),
+    };
     let mapping = match runtime {
         NextRuntime::Edge => &mut manifest.edge,
         NextRuntime::NodeJs => &mut manifest.node,
@@ -190,7 +196,7 @@ async fn build_manifest(
         entry.workers.insert(
             &key,
             ActionManifestWorkerEntry {
-                module_id: ActionManifestModuleId::String(loader_id.as_str()),
+                module_id: loader_id.clone(),
                 is_async: *async_module_info.is_async(chunk_item.module()).await?,
                 exported_name: name.as_str(),
                 filename: filename.as_str(),

filename: crates/next-core/src/next_manifests/mod.rs
patch: @@ -329,11 +329,11 @@ pub struct ActionManifestWorkerEntry<'a> {
     pub filename: &'a str,
 }
 
-#[derive(Serialize, Debug)]
+#[derive(Serialize, Debug, Clone)]
 #[serde(untagged)]
 pub enum ActionManifestModuleId<'a> {
     String(&'a str),
-    Number(f64),
+    Number(u64),
 }
 
 #[derive(

filename: test/e2e/app-dir/actions/app-action.test.ts
patch: @@ -44,7 +44,7 @@ describe('app-dir action handling', () => {
 
           expect(itemInfo.filename).toBeString()
           // can be outside app dir but this test suite has them all in app
-          expect(itemInfo.filename.startsWith('app/')).toBe(true)
+          expect(itemInfo.filename).toStartWith('app/')
           expect(itemInfo.exportedName).toBeString()
         } catch (err) {
           require('console').error(`Invalid action entry ${item}`, err)

filename: test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts
patch: @@ -2697,8 +2697,7 @@ describe('Cache Components Errors', () => {
                     |                                      ^",
                  "stack": [
                    "{module evaluation} app/use-cache-private-in-unstable-cache/page.tsx (21:38)",
-                   "{module evaluation} .next-internal/server/app/use-cache-private-in-unstable-cache/page/actions.js (server actions loader) (1:1)",
-                   "<FIXME-file-protocol>",
+                   "<FIXME-next-dist-dir>",
                    "<FIXME-next-dist-dir>",
                  ],
                }
@@ -2754,8 +2753,8 @@ describe('Cache Components Errors', () => {
                 expect(output).toMatchInlineSnapshot(`
                  "Error: "use cache: private" must not be used within \`unstable_cache()\`.
                      at __TURBOPACK__module__evaluation__ (bundler:///app/use-cache-private-in-unstable-cache/page.tsx:21:38)
-                     at __TURBOPACK__module__evaluation__ (bundler:///.next-internal/server/app/use-cache-private-in-unstable-cache/page/actions.js (server actions loader):1:1)
                      at a (<next-dist-dir>)
+                     at b (<next-dist-dir>)
                    19 | }
                    20 |
                  > 21 | const getCachedData = unstable_cache(async () => {
@@ -2773,7 +2772,6 @@ describe('Cache Components Errors', () => {
                 expect(output).toMatchInlineSnapshot(`
                  "Error: "use cache: private" must not be used within \`unstable_cache()\`.
                      at __TURBOPACK__module__evaluation__ (bundler:///app/use-cache-private-in-unstable-cache/page.tsx:21:38)
-                     at __TURBOPACK__module__evaluation__ (bundler:///.next-internal/server/app/use-cache-private-in-unstable-cache/page/actions.js%20(server%20actions%20loader):1:1)
                      at a (<next-dist-dir>)
                    19 | }
                    20 |
@@ -2842,8 +2840,7 @@ describe('Cache Components Errors', () => {
                     | ^",
                  "stack": [
                    "{module evaluation} app/use-cache-private-in-use-cache/page.tsx (15:1)",
-                   "{module evaluation} .next-internal/server/app/use-cache-private-in-use-cache/page/actions.js (server actions loader) (1:1)",
-                   "<FIXME-file-protocol>",
+                   "<FIXME-next-dist-dir>",
                    "<FIXME-next-dist-dir>",
                  ],
                }
@@ -2900,8 +2897,8 @@ describe('Cache Components Errors', () => {
                 expect(output).toMatchInlineSnapshot(`
                  "Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private".
                      at __TURBOPACK__module__evaluation__ (bundler:///app/use-cache-private-in-use-cache/page.tsx:15:1)
-                     at __TURBOPACK__module__evaluation__ (bundler:///.next-internal/server/app/use-cache-private-in-use-cache/page/actions.js (server actions loader):1:1)
                      at a (<next-dist-dir>)
+                     at b (<next-dist-dir>)
                    13 | }
                    14 |
                  > 15 | async function Private() {
@@ -2911,8 +2908,8 @@ describe('Cache Components Errors', () => {
                    18 |   return <p>Private</p>
                  Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private".
                      at __TURBOPACK__module__evaluation__ (bundler:///app/use-cache-private-in-use-cache/page.tsx:15:1)
-                     at __TURBOPACK__module__evaluation__ (bundler:///.next-internal/server/app/use-cache-private-in-use-cache/page/actions.js (server actions loader):1:1)
-                     at b (<next-dist-dir>)
+                     at c (<next-dist-dir>)
+                     at d (<next-dist-dir>)
                    13 | }
                    14 |
                  > 15 | async function Private() {
@@ -2930,7 +2927,6 @@ describe('Cache Components Errors', () => {
                 expect(output).toMatchInlineSnapshot(`
                  "Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private".
                      at __TURBOPACK__module__evaluation__ (bundler:///app/use-cache-private-in-use-cache/page.tsx:15:1)
-                     at __TURBOPACK__module__evaluation__ (bundler:///.next-internal/server/app/use-cache-private-in-use-cache/page/actions.js%20(server%20actions%20loader):1:1)
                      at a (<next-dist-dir>)
                    13 | }
                    14 |
@@ -2941,7 +2937,6 @@ describe('Cache Components Errors', () => {
                    18 |   return <p>Private</p>
                  Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private".
                      at __TURBOPACK__module__evaluation__ (bundler:///app/use-cache-private-in-use-cache/page.tsx:15:1)
-                     at __TURBOPACK__module__evaluation__ (bundler:///.next-internal/server/app/use-cache-private-in-use-cache/page/actions.js%20(server%20actions%20loader):1:1)
                      at b (<next-dist-dir>)
                    13 | }
                    14 |