Repository: vercel/next.js
Pull request: Turbopack: read asset entries strongly consistent

### What?

Avoid eventual consistent results influencing the map state.

Closes PACK-4310

Changed files:
filename: crates/next-api/src/versioned_content_map.rs
patch: @@ -120,30 +120,19 @@ impl VersionedContentMap {
         client_relative_path: Vc<FileSystemPath>,
         client_output_path: Vc<FileSystemPath>,
     ) -> Result<Vc<OptionMapEntry>> {
-        let assets = assets_operation.connect();
-        async fn get_entries(
-            assets: Vc<OutputAssets>,
-        ) -> Result<Vec<(ResolvedVc<FileSystemPath>, ResolvedVc<Box<dyn OutputAsset>>)>> {
-            let assets_ref = assets.await?;
-            let entries = assets_ref
-                .iter()
-                .map(|&asset| async move {
-                    let path = asset.path().to_resolved().await?;
-                    Ok((path, asset))
-                })
-                .try_join()
-                .await?;
-            Ok(entries)
-        }
-        let entries = get_entries(assets).await.unwrap_or_default();
+        let entries = get_entries(assets_operation)
+            .read_strongly_consistent()
+            .await
+            // Any error should result in an empty list, which removes all assets from the map
+            .ok();
 
         self.map_path_to_op.update_conditionally(|map| {
             let mut changed = false;
 
             // get current map's keys, subtract keys that don't exist in operation
             let mut stale_assets = map.0.keys().copied().collect::<FxHashSet<_>>();
 
-            for (k, _) in entries.iter() {
+            for (k, _) in entries.iter().flatten() {
                 let res = map.0.entry(*k).or_default().insert(assets_operation);
                 stale_assets.remove(k);
                 changed = changed || res;
@@ -163,12 +152,17 @@ impl VersionedContentMap {
         });
 
         // Make sure all written client assets are up-to-date
-        let _ = emit_assets(assets, node_root, client_relative_path, client_output_path)
-            .resolve()
-            .await?;
+        let _ = emit_assets(
+            assets_operation.connect(),
+            node_root,
+            client_relative_path,
+            client_output_path,
+        )
+        .resolve()
+        .await?;
         let map_entry = Vc::cell(Some(MapEntry {
             assets_operation,
-            path_to_asset: entries.into_iter().collect(),
+            path_to_asset: entries.iter().flatten().copied().collect(),
         }));
         Ok(map_entry)
     }
@@ -265,6 +259,25 @@ impl VersionedContentMap {
     }
 }
 
+type GetEntriesResultT = Vec<(ResolvedVc<FileSystemPath>, ResolvedVc<Box<dyn OutputAsset>>)>;
+
+#[turbo_tasks::value(transparent)]
+struct GetEntriesResult(GetEntriesResultT);
+
+#[turbo_tasks::function(operation)]
+async fn get_entries(assets: OperationVc<OutputAssets>) -> Result<Vc<GetEntriesResult>> {
+    let assets_ref = assets.connect().await?;
+    let entries = assets_ref
+        .iter()
+        .map(|&asset| async move {
+            let path = asset.path().to_resolved().await?;
+            Ok((path, asset))
+        })
+        .try_join()
+        .await?;
+    Ok(Vc::cell(entries))
+}
+
 #[turbo_tasks::function(operation)]
 fn compute_entry_operation(
     map: ResolvedVc<VersionedContentMap>,

filename: test/development/app-dir/missing-required-html-tags/index.test.ts
patch: @@ -8,7 +8,7 @@ import {
 } from 'next-test-utils'
 
 describe('app-dir - missing required html tags', () => {
-  const { next, isTurbopack } = nextTestSetup({ files: __dirname })
+  const { next } = nextTestSetup({ files: __dirname })
 
   it('should display correct error count in dev indicator', async () => {
     const browser = await next.browser('/')
@@ -75,22 +75,7 @@ describe('app-dir - missing required html tags', () => {
       )
     )
 
-    if (isTurbopack) {
-      await assertHasRedbox(browser)
-      await expect(browser).toDisplayRedbox(`
-       {
-         "count": 1,
-         "description": "Error: Missing <html> and <body> tags in the root layout.
-       Read more at https://nextjs.org/docs/messages/missing-root-layout-tags",
-         "environmentLabel": null,
-         "label": "Runtime Error",
-         "source": null,
-         "stack": [],
-       }
-      `)
-    } else {
-      // TODO(NDX-768): Should show "missing tags" error
-      await assertNoRedbox(browser)
-    }
+    // TODO(NDX-768): Should show "missing tags" error
+    await assertNoRedbox(browser)
   })
 })