Repository: vercel/next.js
Pull request: Turbopack: use tracing context for config watching

It was using the `evaluate_context` to watch the config (and transitively imported files) for changes.
That was incorrect for various reasons though: any errors during that caused the build to fail, and it didn't actually collect all referenced files.

Turns out we already have multiple options to enable this, as NFT and "config watching" are really the same thing:

```
ResolveOptions.loose_errors: true
(ResolveOptionsContext.collect_affecting_sources: true)
ModuleOptionsContext.analyze_mode: AnalyzeMode::Tracing
ModuleOptionsContext.tree_shaking_mode: None
```

~~technically, `any_content_changed_of_module` should actually watch the `affecting_sources` as well though?~~

Changed files:
filename: turbopack/crates/turbopack-node/src/transforms/postcss.rs
patch: @@ -103,6 +103,7 @@ fn postcss_configs() -> Vc<Vec<RcStr>> {
 #[turbo_tasks::value]
 pub struct PostCssTransform {
     evaluate_context: ResolvedVc<Box<dyn AssetContext>>,
+    config_tracing_context: ResolvedVc<Box<dyn AssetContext>>,
     execution_context: ResolvedVc<ExecutionContext>,
     config_location: PostCssConfigLocation,
     source_maps: bool,
@@ -113,12 +114,14 @@ impl PostCssTransform {
     #[turbo_tasks::function]
     pub fn new(
         evaluate_context: ResolvedVc<Box<dyn AssetContext>>,
+        config_tracing_context: ResolvedVc<Box<dyn AssetContext>>,
         execution_context: ResolvedVc<ExecutionContext>,
         config_location: PostCssConfigLocation,
         source_maps: bool,
     ) -> Vc<Self> {
         PostCssTransform {
             evaluate_context,
+            config_tracing_context,
             execution_context,
             config_location,
             source_maps,
@@ -134,6 +137,7 @@ impl SourceTransform for PostCssTransform {
         Vc::upcast(
             PostCssTransformedAsset {
                 evaluate_context: self.evaluate_context,
+                config_tracing_context: self.config_tracing_context,
                 execution_context: self.execution_context,
                 config_location: self.config_location,
                 source,
@@ -147,6 +151,7 @@ impl SourceTransform for PostCssTransform {
 #[turbo_tasks::value]
 struct PostCssTransformedAsset {
     evaluate_context: ResolvedVc<Box<dyn AssetContext>>,
+    config_tracing_context: ResolvedVc<Box<dyn AssetContext>>,
     execution_context: ResolvedVc<ExecutionContext>,
     config_location: PostCssConfigLocation,
     source: ResolvedVc<Box<dyn Source>>,
@@ -483,7 +488,7 @@ impl PostCssTransformedAsset {
         let source_map = self.source_map;
 
         // This invalidates the transform when the config changes.
-        let config_changed = config_changed(*evaluate_context, config_path.clone())
+        let config_changed = config_changed(*self.config_tracing_context, config_path.clone())
             .to_resolved()
             .await?;
 

filename: turbopack/crates/turbopack/src/evaluate_context.rs
patch: @@ -17,7 +17,7 @@ use turbopack_node::execution_context::ExecutionContext;
 use turbopack_resolve::resolve_options_context::ResolveOptionsContext;
 
 use crate::{
-    ModuleAssetContext,
+    ModuleAssetContext, externals_tracing_module_context,
     module_options::{EcmascriptOptionsContext, ModuleOptionsContext, TypescriptTransformOptions},
     transition::TransitionOptions,
 };
@@ -29,6 +29,14 @@ pub fn node_build_environment() -> Vc<Environment> {
     ))
 }
 
+async fn node_env_value(env: Vc<Box<dyn ProcessEnv>>) -> Result<RcStr> {
+    if let Some(node_env) = &*env.read(rcstr!("NODE_ENV")).await? {
+        Ok(node_env.clone())
+    } else {
+        Ok(rcstr!("development"))
+    }
+}
+
 #[turbo_tasks::function]
 pub async fn node_evaluate_asset_context(
     execution_context: Vc<ExecutionContext>,
@@ -51,12 +59,7 @@ pub async fn node_evaluate_asset_context(
         .resolved_cell(),
     );
     let import_map = import_map.resolved_cell();
-    let node_env: RcStr =
-        if let Some(node_env) = &*execution_context.env().read(rcstr!("NODE_ENV")).await? {
-            node_env.clone()
-        } else {
-            rcstr!("development")
-        };
+    let node_env = node_env_value(execution_context.env()).await?;
 
     // base context used for node_modules (and context for app code will be derived
     // from this)
@@ -115,3 +118,24 @@ pub async fn node_evaluate_asset_context(
         layer,
     )))
 }
+
+#[turbo_tasks::function]
+pub async fn config_tracing_module_context(
+    execution_context: Vc<ExecutionContext>,
+) -> Result<Vc<Box<dyn AssetContext>>> {
+    let node_env = node_env_value(execution_context.env()).await?;
+
+    Ok(Vc::upcast(externals_tracing_module_context(
+        CompileTimeInfo::builder(node_build_environment().to_resolved().await?)
+            .defines(
+                compile_time_defines!(
+                    process.turbopack = true,
+                    process.env.NODE_ENV = node_env.into_owned(),
+                    process.env.TURBOPACK = true
+                )
+                .resolved_cell(),
+            )
+            .cell()
+            .await?,
+    )))
+}

filename: turbopack/crates/turbopack/src/module_options/mod.rs
patch: @@ -34,7 +34,8 @@ use turbopack_node::{
 use turbopack_wasm::source::WebAssemblySourceType;
 
 use crate::{
-    evaluate_context::node_evaluate_asset_context, resolve_options_context::ResolveOptionsContext,
+    evaluate_context::{config_tracing_module_context, node_evaluate_asset_context},
+    resolve_options_context::ResolveOptionsContext,
 };
 
 #[turbo_tasks::function]
@@ -695,6 +696,7 @@ impl ModuleOptions {
                                     Layer::new(rcstr!("postcss")),
                                     true,
                                 ),
+                                config_tracing_module_context(*execution_context),
                                 *execution_context,
                                 options.config_location,
                                 matches!(css_source_maps, SourceMapsType::Full),