Repository: vercel/next.js
Pull request: Turbopack: Switch RequestKey's `conditions` field from BTreeMap to FrozenMap

Doing this as a small trial of the new `FrozenMap` API. @lukesandberg pointed this out as a potentially good candidate.

Changed files:
filename: Cargo.lock
patch: @@ -9674,6 +9674,7 @@ dependencies = [
  "tokio",
  "tracing",
  "turbo-bincode",
+ "turbo-frozenmap",
  "turbo-prehash",
  "turbo-rcstr",
  "turbo-tasks",

filename: turbopack/crates/turbo-frozenmap/src/map.rs
patch: @@ -381,6 +381,17 @@ impl<K, V> FrozenMap<K, V> {
             inner: slice.iter(),
         }
     }
+
+    /// Extend this [`FrozenMap`] by constructing a new map with the additional entries. New entries
+    /// with overlapping keys will overwrite existing ones.
+    #[must_use]
+    pub fn extend(&self, entries: impl IntoIterator<Item = (K, V)>) -> Self
+    where
+        K: Clone + Ord,
+        V: Clone,
+    {
+        self.as_slice().iter().cloned().chain(entries).collect()
+    }
 }
 
 // Manual implementation because the derive would add unnecessary `K: Default, V: Default` bounds.

filename: turbopack/crates/turbopack-core/Cargo.toml
patch: @@ -39,6 +39,7 @@ swc_sourcemap = { workspace = true }
 swc_core = { workspace = true, features = ["ecma_preset_env", "common"] }
 tracing = { workspace = true }
 turbo-bincode = { workspace = true }
+turbo-frozenmap = { workspace = true }
 turbo-prehash = { workspace = true }
 turbo-rcstr = { workspace = true }
 turbo-tasks = { workspace = true }
@@ -51,7 +52,6 @@ url = { workspace = true }
 urlencoding = { workspace = true }
 uuid = { workspace = true}
 
-
 [dev-dependencies]
 rstest = { workspace = true }
 tokio = { workspace = true }

filename: turbopack/crates/turbopack-core/src/resolve/mod.rs
patch: @@ -15,6 +15,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
 use serde::{Deserialize, Serialize};
 use smallvec::SmallVec;
 use tracing::{Instrument, Level};
+use turbo_frozenmap::FrozenMap;
 use turbo_rcstr::{RcStr, rcstr};
 use turbo_tasks::{
     FxIndexMap, FxIndexSet, NonLocalValue, ReadRef, ResolvedVc, SliceMap, TaskInput,
@@ -477,7 +478,7 @@ pub enum ResolveResultItem {
 #[turbo_tasks::value]
 pub struct RequestKey {
     pub request: Option<RcStr>,
-    pub conditions: BTreeMap<String, bool>,
+    pub conditions: FrozenMap<RcStr, bool>,
 }
 
 impl Display for RequestKey {
@@ -756,19 +757,26 @@ impl ResolveResult {
         }
     }
 
-    pub fn add_conditions(&mut self, conditions: impl IntoIterator<Item = (RcStr, bool)>) {
-        let mut primary = std::mem::take(&mut self.primary);
-        for (k, v) in conditions {
-            for (key, _) in primary.iter_mut() {
-                key.conditions.insert(k.to_string(), v);
-            }
-        }
-        // Deduplicate
-        self.primary = IntoIterator::into_iter(primary)
-            .collect::<FxIndexMap<_, _>>()
+    pub fn with_conditions(&self, new_conditions: &[(RcStr, bool)]) -> Self {
+        let primary = self
+            .primary
+            .iter()
+            .map(|(k, v)| {
+                (
+                    RequestKey {
+                        request: k.request.clone(),
+                        conditions: k.conditions.extend(new_conditions.iter().cloned()),
+                    },
+                    v.clone(),
+                )
+            })
+            .collect::<FxIndexMap<_, _>>() // Deduplicate
             .into_iter()
-            .collect::<Vec<_>>()
-            .into_boxed_slice();
+            .collect();
+        ResolveResult {
+            primary,
+            affecting_sources: self.affecting_sources.clone(),
+        }
     }
 }
 
@@ -3119,8 +3127,7 @@ async fn handle_exports_imports_field(
             };
 
             let resolve_result = if !conditions.is_empty() {
-                let mut resolve_result = resolve_result.owned().await?;
-                resolve_result.add_conditions(conditions);
+                let resolve_result = resolve_result.await?.with_conditions(&conditions);
                 resolve_result.cell()
             } else {
                 resolve_result

filename: turbopack/crates/turbopack-resolve/src/typescript.rs
patch: @@ -15,7 +15,7 @@ use turbopack_core::{
     },
     reference_type::{ReferenceType, TypeScriptReferenceSubType},
     resolve::{
-        AliasPattern, ModuleResolveResult, handle_resolve_error,
+        AliasPattern, ModuleResolveResult, RequestKey, handle_resolve_error,
         node::node_cjs_resolve_options,
         options::{
             ConditionValue, ImportMap, ImportMapping, ResolveIntoPackage, ResolveModules,
@@ -479,9 +479,14 @@ pub async fn type_resolve(
 pub async fn as_typings_result(result: Vc<ModuleResolveResult>) -> Result<Vc<ModuleResolveResult>> {
     let mut result = result.owned().await?;
     result.primary = IntoIterator::into_iter(take(&mut result.primary))
-        .map(|(mut k, v)| {
-            k.conditions.insert("types".to_string(), true);
-            (k, v)
+        .map(|(k, v)| {
+            (
+                RequestKey {
+                    request: k.request.clone(),
+                    conditions: k.conditions.extend([(rcstr!("types"), true)]),
+                },
+                v,
+            )
         })
         .collect();
     Ok(result.cell())