Repository: vercel/next.js
Pull request: Turbopack: don't recompute empty parse results

Don't use the recomputation mechanism for this, just make it an optional.

Closes PACK-5471

Changed files:
filename: turbopack/crates/turbopack-ecmascript/src/lib.rs
patch: @@ -516,7 +516,7 @@ impl EcmascriptAnalyzable for EcmascriptModuleAsset {
             .await?;
 
         Ok(EcmascriptModuleContentOptions {
-            parsed,
+            parsed: Some(parsed),
             module: ResolvedVc::upcast(self),
             specified_module_type: module_type_result.module_type,
             chunking_context,
@@ -885,7 +885,7 @@ pub struct EcmascriptModuleContent {
 #[derive(Clone, Debug, Hash, TaskInput)]
 pub struct EcmascriptModuleContentOptions {
     module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
-    parsed: ResolvedVc<ParseResult>,
+    parsed: Option<ResolvedVc<ParseResult>>,
     specified_module_type: SpecifiedModuleType,
     chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
     references: ResolvedVc<ModuleReferences>,
@@ -1030,7 +1030,7 @@ impl EcmascriptModuleContent {
         generate_source_map: bool,
     ) -> Result<Vc<Self>> {
         let content = process_parse_result(
-            parsed.to_resolved().await?,
+            Some(parsed.to_resolved().await?),
             ident,
             specified_module_type,
             generate_source_map,
@@ -1655,9 +1655,8 @@ struct ScopeHoistingOptions<'a> {
     modules: &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
 }
 
-#[instrument(level = Level::TRACE, skip_all, name = "process module")]
 async fn process_parse_result(
-    parsed: ResolvedVc<ParseResult>,
+    parsed: Option<ResolvedVc<ParseResult>>,
     ident: Vc<AssetIdent>,
     specified_module_type: SpecifiedModuleType,
     generate_source_map: bool,
@@ -1914,12 +1913,16 @@ async fn process_parse_result(
             })
         },
     )
+    .instrument(tracing::trace_span!(
+        "process parse result",
+        ident = display(ident.to_string().await?),
+    ))
     .await
 }
 
 /// Try to avoid cloning the AST and Globals by unwrapping the ReadRef (and cloning otherwise).
 async fn with_consumed_parse_result<T>(
-    parsed: ResolvedVc<ParseResult>,
+    parsed: Option<ResolvedVc<ParseResult>>,
     success: impl AsyncFnOnce(
         Program,
         &Arc<SourceMap>,
@@ -1929,6 +1932,24 @@ async fn with_consumed_parse_result<T>(
     ) -> Result<T>,
     error: impl AsyncFnOnce(&ParseResult) -> Result<T>,
 ) -> Result<T> {
+    let Some(parsed) = parsed else {
+        let globals = Globals::new();
+        let eval_context = GLOBALS.set(&globals, || EvalContext {
+            unresolved_mark: Mark::new(),
+            top_level_mark: Mark::new(),
+            imports: Default::default(),
+            force_free_values: Default::default(),
+        });
+        return success(
+            Program::Module(swc_core::ecma::ast::Module::dummy()),
+            &Default::default(),
+            &Default::default(),
+            Either::Left(eval_context),
+            Either::Left(Default::default()),
+        )
+        .await;
+    };
+
     let parsed = parsed.final_read_hint().await?;
     match &*parsed {
         ParseResult::Ok { .. } => {

filename: turbopack/crates/turbopack-ecmascript/src/parse.rs
patch: @@ -10,7 +10,6 @@ use swc_core::{
         errors::{HANDLER, Handler},
         input::StringInput,
         source_map::{Files, SourceMapGenConfig, build_source_map},
-        util::take::Take,
     },
     ecma::{
         ast::{EsVersion, Id, ObjectPatProp, Pat, Program, VarDecl},
@@ -44,7 +43,7 @@ use turbopack_swc_utils::emitter::IssueEmitter;
 use super::EcmascriptModuleAssetType;
 use crate::{
     EcmascriptInputTransform,
-    analyzer::{ImportMap, graph::EvalContext},
+    analyzer::graph::EvalContext,
     swc_comments::ImmutableComments,
     transform::{EcmascriptInputTransforms, TransformContext},
 };
@@ -80,28 +79,6 @@ impl PartialEq for ParseResult {
     }
 }
 
-#[turbo_tasks::value_impl]
-impl ParseResult {
-    #[turbo_tasks::function]
-    pub fn empty() -> Vc<ParseResult> {
-        let globals = Globals::new();
-        let eval_context = GLOBALS.set(&globals, || EvalContext {
-            unresolved_mark: Mark::new(),
-            top_level_mark: Mark::new(),
-            imports: ImportMap::default(),
-            force_free_values: Default::default(),
-        });
-        ParseResult::Ok {
-            program: Program::Module(swc_core::ecma::ast::Module::dummy()),
-            comments: Default::default(),
-            eval_context,
-            globals: Arc::new(globals),
-            source_map: Default::default(),
-        }
-        .cell()
-    }
-}
-
 /// `original_source_maps_complete` indicates whether the `original_source_maps` cover the whole
 /// map, i.e. whether every module that ended up in `mappings` had an original sourcemap.
 #[instrument(level = Level::INFO, skip_all)]

filename: turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/facade/module.rs
patch: @@ -23,7 +23,6 @@ use crate::{
     chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
     code_gen::CodeGens,
     export::Liveness,
-    parse::ParseResult,
     references::{
         async_module::{AsyncModule, OptionAsyncModule},
         esm::{EsmExport, EsmExports, base::EsmAssetReferences},
@@ -212,7 +211,7 @@ impl EcmascriptAnalyzable for EcmascriptModuleFacadeModule {
         let (part_references, esm_references) = self.await?.specific_references().await?;
 
         Ok(EcmascriptModuleContentOptions {
-            parsed: ParseResult::empty().to_resolved().await?,
+            parsed: None,
             module: ResolvedVc::upcast(self),
             specified_module_type: SpecifiedModuleType::EcmaScript,
             chunking_context,

filename: turbopack/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs
patch: @@ -111,7 +111,7 @@ impl EcmascriptAnalyzable for EcmascriptModuleLocalsModule {
             .await?;
 
         Ok(EcmascriptModuleContentOptions {
-            parsed,
+            parsed: Some(parsed),
             module: ResolvedVc::upcast(self),
             specified_module_type: module_type_result.module_type,
             chunking_context,

filename: turbopack/crates/turbopack-ecmascript/src/tree_shake/asset.rs
patch: @@ -94,7 +94,7 @@ impl EcmascriptAnalyzable for EcmascriptModulePartAsset {
             .reference_module_source_maps(Vc::upcast(*self))
             .await?;
         Ok(EcmascriptModuleContentOptions {
-            parsed,
+            parsed: Some(parsed),
             module: ResolvedVc::upcast(self),
             specified_module_type: module_type_result.module_type,
             chunking_context,