Repository: vercel/next.js
Pull request: Turbopack: NFT debugging

- add depth flag to NFT CLI
- add tracing span for DirAssetReference
<img width="1365" height="504" alt="Bildschirmfoto 2025-08-27 um 11 44 00" src="https://github.com/user-attachments/assets/5baf0e76-76a8-4f4b-bb0a-396c4459dbb6" />


Closes PACK-5353

Changed files:
filename: turbopack/crates/turbopack-ecmascript/src/references/node.rs
patch: @@ -1,5 +1,6 @@
 use anyhow::Result;
 use either::Either;
+use tracing::Instrument;
 use turbo_rcstr::{RcStr, rcstr};
 use turbo_tasks::{ResolvedVc, ValueToString, Vc};
 use turbo_tasks_fs::FileSystemPath;
@@ -156,7 +157,17 @@ impl ModuleReference for DirAssetReference {
     #[turbo_tasks::function]
     async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
         let parent_path = self.source.ident().path().await?.parent();
-        Ok(resolve_reference_from_dir(parent_path, *self.path))
+        let span = tracing::info_span!(
+            "resolve DirAssetReference",
+            pattern = display(self.path.to_string().await?)
+        );
+        async {
+            resolve_reference_from_dir(parent_path, *self.path)
+                .resolve()
+                .await
+        }
+        .instrument(span)
+        .await
     }
 }
 

filename: turbopack/crates/turbopack-nft/src/main.rs
patch: @@ -31,6 +31,9 @@ pub struct Arguments {
 
     #[clap(long)]
     pub show_issues: bool,
+
+    #[clap(long)]
+    pub depth: Option<usize>,
 }
 
 #[global_allocator]
@@ -95,6 +98,7 @@ async fn main_inner(args: Arguments) -> Result<()> {
             args.entry.into(),
             args.graph,
             args.show_issues,
+            args.depth,
         )
         .await?;
         Ok(Default::default())

filename: turbopack/crates/turbopack-nft/src/nft.rs
patch: @@ -28,8 +28,9 @@ pub async fn node_file_trace(
     input: RcStr,
     graph: bool,
     show_issues: bool,
+    max_depth: Option<usize>,
 ) -> Result<()> {
-    let op = node_file_trace_operation(project_root.clone(), input.clone(), graph);
+    let op = node_file_trace_operation(project_root.clone(), input.clone(), graph, max_depth);
     let result = op.resolve_strongly_consistent().await?;
 
     if show_issues {
@@ -58,6 +59,7 @@ async fn node_file_trace_operation(
     project_root: RcStr,
     input: RcStr,
     graph: bool,
+    max_depth: Option<usize>,
 ) -> Result<Vc<Vec<RcStr>>> {
     let workspace_fs: Vc<Box<dyn FileSystem>> = Vc::upcast(DiskFileSystem::new(
         rcstr!("workspace"),
@@ -98,7 +100,7 @@ async fn node_file_trace_operation(
             ..Default::default()
         }
         .cell(),
-        Layer::new(rcstr!("test")),
+        Layer::new(rcstr!("externals-tracing")),
     );
     let module = module_asset_context
         .process(Vc::upcast(source), ReferenceType::Undefined)
@@ -107,7 +109,7 @@ async fn node_file_trace_operation(
     let asset = TracedAsset::new(module).to_resolved().await?;
 
     Ok(Vc::cell(if graph {
-        to_graph(ResolvedVc::upcast(asset)).await?
+        to_graph(ResolvedVc::upcast(asset), max_depth.unwrap_or(usize::MAX)).await?
     } else {
         to_list(ResolvedVc::upcast(asset)).await?
     }))
@@ -126,7 +128,7 @@ async fn to_list(asset: ResolvedVc<Box<dyn OutputAsset>>) -> Result<Vec<RcStr>>
     Ok(assets)
 }
 
-async fn to_graph(asset: ResolvedVc<Box<dyn OutputAsset>>) -> Result<Vec<RcStr>> {
+async fn to_graph(asset: ResolvedVc<Box<dyn OutputAsset>>, max_depth: usize) -> Result<Vec<RcStr>> {
     let mut visited = HashSet::new();
     let mut queue = Vec::new();
     queue.push((0, asset));
@@ -139,8 +141,10 @@ async fn to_graph(asset: ResolvedVc<Box<dyn OutputAsset>>) -> Result<Vec<RcStr>>
             indent.push_str("  ");
         }
         if visited.insert(asset) {
-            for &asset in references.iter().rev() {
-                queue.push((depth + 1, asset));
+            if depth < max_depth {
+                for &asset in references.iter().rev() {
+                    queue.push((depth + 1, asset));
+                }
             }
             result.push(format!("{}{}", indent, asset.path().to_string().await?).into());
         } else if references.is_empty() {
@@ -149,5 +153,8 @@ async fn to_graph(asset: ResolvedVc<Box<dyn OutputAsset>>) -> Result<Vec<RcStr>>
             result.push(format!("{}{} *...", indent, asset.path().to_string().await?).into());
         }
     }
+    result.push("".into());
+    result.push("*    : revisited and no references".into());
+    result.push("*... : revisited and references were already printed".into());
     Ok(result)
 }