Repository: vercel/next.js
Pull request: Turbopack: normalize ref type for external tracing

Fixes duplicate `CachedExternalModule`s, due to different result depending on the reference type:

As opposed to adding the whole reference type to the external's `AssetIdent`, normalize the reference type, tracing an external with `EcmaScriptModules(ImportPart(Evaluation))` (which returns nothing for side-effect-free packages) is strange anyway.

```
request: "@emotion/react"
reference_type: EcmaScriptModules(ImportPart(Evaluation))
tracing_resolve_result: [] 
tracing_resolve_affecting: ["[project]/node_modules/.pnpm/@emotion+react@11.11.1_@types+react@19.1.1_react@19.2.0-canary-1d6c8168-20250411/node_modules/@emotion/react/package.json", "[project]/node_modules/@emotion/react"]
```

```
request: "@emotion/react"
reference_type: EcmaScriptModules(ImportPart(Export("CacheProvider")))
tracing_resolve_result: ["[project]/node_modules/.pnpm/@emotion+react@11.11.1_@types+react@19.1.1_react@19.2.0-canary-1d6c8168-20250411/node_modules/@emotion/react/dist/emotion-react.cjs.mjs [externals-tracing] (ecmascript)"]
tracing_resolve_affecting: ["[project]/node_modules/.pnpm/@emotion+react@11.11.1_@types+react@19.1.1_react@19.2.0-canary-1d6c8168-20250411/node_modules/@emotion/react/package.json", "[project]/node_modules/@emotion/react"]
```


Closes PACK-4375

Changed files:
filename: turbopack/crates/turbopack-core/src/reference_type.rs
patch: @@ -28,9 +28,10 @@ impl InnerAssets {
 // behavior.
 
 #[turbo_tasks::value(serialization = "auto_for_input")]
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Default, Clone, Hash)]
 pub enum CommonJsReferenceSubType {
     Custom(u8),
+    #[default]
     Undefined,
 }
 
@@ -170,7 +171,7 @@ impl ImportContext {
 }
 
 #[turbo_tasks::value(serialization = "auto_for_input")]
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Default, Clone, Hash)]
 pub enum CssReferenceSubType {
     AtImport(Option<ResolvedVc<ImportContext>>),
     /// Reference from ModuleCssAsset to an imported ModuleCssAsset for retrieving the composed
@@ -181,15 +182,17 @@ pub enum CssReferenceSubType {
     /// Used for generating the list of classes in a ModuleCssAsset
     Analyze,
     Custom(u8),
+    #[default]
     Undefined,
 }
 
 #[turbo_tasks::value(serialization = "auto_for_input")]
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Default, Clone, Hash)]
 pub enum UrlReferenceSubType {
     EcmaScriptNewUrl,
     CssUrl,
     Custom(u8),
+    #[default]
     Undefined,
 }
 
@@ -229,7 +232,7 @@ pub enum EntryReferenceSubType {
 }
 
 #[turbo_tasks::value(serialization = "auto_for_input")]
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Default, Clone, Hash)]
 pub enum ReferenceType {
     CommonJs(CommonJsReferenceSubType),
     EcmaScriptModules(EcmaScriptModulesReferenceSubType),
@@ -241,6 +244,7 @@ pub enum ReferenceType {
     Runtime,
     Internal(ResolvedVc<InnerAssets>),
     Custom(u8),
+    #[default]
     Undefined,
 }
 

filename: turbopack/crates/turbopack/src/lib.rs
patch: @@ -799,6 +799,27 @@ impl AssetContext for ModuleAssetContext {
                                     let externals_context = externals_tracing_module_context(ty);
                                     let root_origin = tracing_root.join("_".into());
 
+                                    // Normalize reference type, there is no such thing as a
+                                    // `ReferenceType::EcmaScriptModules(ImportPart(Evaluation))`
+                                    // for externals (and otherwise, this causes duplicate
+                                    // CachedExternalModules for both `ImportPart(Evaluation)` and
+                                    // `ImportPart(Export("CacheProvider"))`)
+                                    let reference_type = Value::new(match &*reference_type {
+                                        ReferenceType::EcmaScriptModules(_) => {
+                                            ReferenceType::EcmaScriptModules(Default::default())
+                                        }
+                                        ReferenceType::CommonJs(_) => {
+                                            ReferenceType::CommonJs(Default::default())
+                                        }
+                                        ReferenceType::Css(_) => {
+                                            ReferenceType::Css(Default::default())
+                                        }
+                                        ReferenceType::Url(_) => {
+                                            ReferenceType::Url(Default::default())
+                                        }
+                                        _ => ReferenceType::Undefined,
+                                    });
+
                                     let external_result = externals_context
                                         .resolve_asset(
                                             root_origin,
@@ -822,6 +843,7 @@ impl AssetContext for ModuleAssetContext {
                                         );
 
                                     modules
+                                        .into_iter()
                                         .map(|s| {
                                             Vc::upcast::<Box<dyn ModuleReference>>(
                                                 TracedModuleReference::new(s),