Repository: rolldown/rolldown
Pull request #4187: perf: skip empty hook with `hook_usage` meta
Author: login: IWANABETHATGUY
Created: 2025-04-18T08:26:12Z | Merged: 2025-04-19T03:19:24Z

<!-- Thank you for contributing! -->

### Description

<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->



Diff:
diff --git a/crates/rolldown_plugin/src/plugin_driver/build_hooks.rs b/crates/rolldown_plugin/src/plugin_driver/build_hooks.rs
index f31cbe545e3e..d40e3c2aede9 100644
--- a/crates/rolldown_plugin/src/plugin_driver/build_hooks.rs
+++ b/crates/rolldown_plugin/src/plugin_driver/build_hooks.rs
@@ -2,7 +2,8 @@ use std::sync::Arc;
 
 use crate::{
   HookBuildEndArgs, HookLoadArgs, HookLoadReturn, HookNoopReturn, HookResolveIdArgs,
-  HookResolveIdReturn, HookTransformArgs, PluginContext, PluginDriver, TransformPluginContext,
+  HookResolveIdReturn, HookTransformArgs, HookUsage, PluginContext, PluginDriver,
+  TransformPluginContext,
   pluginable::HookTransformAstReturn,
   types::{
     hook_resolve_id_skipped::HookResolveIdSkipped, hook_transform_ast_args::HookTransformAstArgs,
@@ -54,8 +55,12 @@ impl PluginDriver {
     //   r?;
     // }
 
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_build_start_meta)
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_build_start_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::BuildStart) {
+        continue;
+      }
       plugin.call_build_start(ctx, &crate::HookBuildStartArgs { options: opts }).await?;
     }
 
@@ -91,6 +96,10 @@ impl PluginDriver {
     for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_resolve_id_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::ResolveId) {
+        continue;
+      }
+      // TODO: Maybe we could optimize this a little
       if skipped_plugins.iter().any(|p| *p == plugin_idx) {
         continue;
       }
@@ -128,6 +137,9 @@ impl PluginDriver {
     for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_resolve_dynamic_import_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::ResolveDynamicImport) {
+        continue;
+      }
       if skipped_plugins.iter().any(|p| *p == plugin_idx) {
         continue;
       }
@@ -160,6 +172,9 @@ impl PluginDriver {
     for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_load_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::Load) {
+        continue;
+      }
       trace_action!(action::HookLoadCallStart {
         kind: "HookLoadCallStart".to_string(),
         module_id: args.id.to_string(),
@@ -206,6 +221,9 @@ impl PluginDriver {
     for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_transform_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::Transform) {
+        continue;
+      }
       trace_action!(action::HookTransformCallStart {
         kind: "HookTransformCallStart".to_string(),
         module_id: id.to_string(),
@@ -301,9 +319,12 @@ impl PluginDriver {
   }
 
   pub async fn transform_ast(&self, mut args: HookTransformAstArgs<'_>) -> HookTransformAstReturn {
-    for (_, plugin, ctx) in
+    for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_transform_ast_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::TransformAst) {
+        continue;
+      }
       args.ast = plugin
         .call_transform_ast(
           ctx,
@@ -327,9 +348,12 @@ impl PluginDriver {
     module_info: Arc<ModuleInfo>,
     normal_module: &NormalModule,
   ) -> HookNoopReturn {
-    for (_, plugin, ctx) in
+    for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_module_parsed_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::ModuleParsed) {
+        continue;
+      }
       plugin
         .call_module_parsed(ctx, Arc::clone(&module_info), normal_module)
         .instrument(debug_span!("module_parsed_hook", plugin_name = plugin.call_name().as_ref()))
@@ -339,7 +363,12 @@ impl PluginDriver {
   }
 
   pub async fn build_end(&self, args: Option<&HookBuildEndArgs<'_>>) -> HookNoopReturn {
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_build_end_meta) {
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_build_end_meta)
+    {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::Transform) {
+        continue;
+      }
       plugin
         .call_build_end(ctx, args)
         .instrument(debug_span!("build_end_hook", plugin_name = plugin.call_name().as_ref()))
diff --git a/crates/rolldown_plugin/src/plugin_driver/mod.rs b/crates/rolldown_plugin/src/plugin_driver/mod.rs
index b49874f5ca1d..c2bf1ac11e8e 100644
--- a/crates/rolldown_plugin/src/plugin_driver/mod.rs
+++ b/crates/rolldown_plugin/src/plugin_driver/mod.rs
@@ -6,6 +6,7 @@ use std::{
 
 use arcstr::ArcStr;
 use dashmap::{DashMap, DashSet};
+use oxc_index::IndexVec;
 use rolldown_common::{
   ModuleId, ModuleInfo, ModuleLoaderMsg, SharedFileEmitter, SharedNormalizedBundlerOptions,
 };
@@ -15,7 +16,7 @@ use tokio::sync::Mutex;
 
 use crate::{
   __inner::SharedPluginable,
-  PluginContext, PluginHookMeta, PluginOrder,
+  HookUsage, PluginContext, PluginHookMeta, PluginOrder,
   plugin_context::{LoadCallback, PluginContextImpl},
   type_aliases::{IndexPluginContext, IndexPluginable},
   types::plugin_idx::PluginIdx,
@@ -36,6 +37,7 @@ pub struct PluginDriver {
   pub modules: Arc<FxDashMap<ArcStr, Arc<ModuleInfo>>>,
   pub context_load_modules: Arc<FxDashMap<ArcStr, LoadCallback>>,
   pub(crate) tx: Arc<Mutex<Option<tokio::sync::mpsc::Sender<ModuleLoaderMsg>>>>,
+  pub(crate) plugin_usage_vec: IndexVec<PluginIdx, HookUsage>,
 }
 
 impl PluginDriver {
@@ -49,6 +51,7 @@ impl PluginDriver {
     let modules = Arc::new(DashMap::default());
     let context_load_modules = Arc::new(DashMap::default());
     let tx = Arc::new(Mutex::new(None));
+    let mut plugin_usage_vec = IndexVec::new();
 
     Arc::new_cyclic(|plugin_driver| {
       let mut index_plugins = IndexPluginable::with_capacity(plugins.len());
@@ -56,6 +59,7 @@ impl PluginDriver {
 
       plugins.into_iter().for_each(|plugin| {
         let plugin_idx = index_plugins.push(Arc::clone(&plugin));
+        plugin_usage_vec.push(plugin.call_hook_usage());
         index_contexts.push(
           PluginContextImpl {
             skipped_resolve_calls: vec![],
@@ -82,6 +86,7 @@ impl PluginDriver {
         modules,
         context_load_modules,
         tx,
+        plugin_usage_vec,
       }
     })
   }
diff --git a/crates/rolldown_plugin/src/plugin_driver/output_hooks.rs b/crates/rolldown_plugin/src/plugin_driver/output_hooks.rs
index 7e3e7d011fda..92914f453d11 100644
--- a/crates/rolldown_plugin/src/plugin_driver/output_hooks.rs
+++ b/crates/rolldown_plugin/src/plugin_driver/output_hooks.rs
@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use crate::types::hook_render_error::HookRenderErrorArgs;
-use crate::{HookAddonArgs, PluginDriver};
+use crate::{HookAddonArgs, HookUsage, PluginDriver};
 use crate::{HookAugmentChunkHashReturn, HookNoopReturn, HookRenderChunkArgs};
 use anyhow::{Ok, Result};
 use rolldown_common::{Output, RollupRenderedChunk, SharedNormalizedBundlerOptions};
@@ -11,8 +11,12 @@ use tracing::{Instrument, debug_span};
 
 impl PluginDriver {
   pub async fn render_start(&self, opts: &SharedNormalizedBundlerOptions) -> HookNoopReturn {
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_render_start_meta)
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_render_start_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::RenderStart) {
+        continue;
+      }
       plugin
         .call_render_start(ctx, &crate::HookRenderStartArgs { options: opts })
         .instrument(debug_span!("render_start_hook", plugin_name = plugin.call_name().as_ref()))
@@ -22,7 +26,12 @@ impl PluginDriver {
   }
 
   pub async fn banner(&self, args: HookAddonArgs, mut banner: String) -> Result<Option<String>> {
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_banner_meta) {
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_banner_meta)
+    {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::Banner) {
+        continue;
+      }
       if let Some(r) = plugin
         .call_banner(ctx, &args)
         .instrument(debug_span!("banner_hook", plugin_name = plugin.call_name().as_ref()))
@@ -39,7 +48,12 @@ impl PluginDriver {
   }
 
   pub async fn footer(&self, args: HookAddonArgs, mut footer: String) -> Result<Option<String>> {
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_footer_meta) {
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_footer_meta)
+    {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::Footer) {
+        continue;
+      }
       if let Some(r) = plugin
         .call_footer(ctx, &args)
         .instrument(debug_span!("footer_hook", plugin_name = plugin.call_name().as_ref()))
@@ -56,7 +70,12 @@ impl PluginDriver {
   }
 
   pub async fn intro(&self, args: HookAddonArgs, mut intro: String) -> Result<Option<String>> {
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_intro_meta) {
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_intro_meta)
+    {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::Intro) {
+        continue;
+      }
       if let Some(r) = plugin
         .call_intro(ctx, &args)
         .instrument(debug_span!("intro_hook", plugin_name = plugin.call_name().as_ref()))
@@ -73,7 +92,12 @@ impl PluginDriver {
   }
 
   pub async fn outro(&self, args: HookAddonArgs, mut outro: String) -> Result<Option<String>> {
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_outro_meta) {
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_outro_meta)
+    {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::Outro) {
+        continue;
+      }
       if let Some(r) = plugin
         .call_outro(ctx, &args)
         .instrument(debug_span!("outro_hook", plugin_name = plugin.call_name().as_ref()))
@@ -94,8 +118,12 @@ impl PluginDriver {
     mut args: HookRenderChunkArgs<'_>,
   ) -> Result<(String, Vec<SourceMap>)> {
     let mut sourcemap_chain = vec![];
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_render_chunk_meta)
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_render_chunk_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::RenderChunk) {
+        continue;
+      }
       if let Some(r) = plugin
         .call_render_chunk(ctx, &args)
         .instrument(debug_span!("render_chunk_hook", plugin_name = plugin.call_name().as_ref()))
@@ -115,9 +143,12 @@ impl PluginDriver {
     chunk: Arc<RollupRenderedChunk>,
   ) -> HookAugmentChunkHashReturn {
     let mut hash = None;
-    for (_, plugin, ctx) in
+    for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_augment_chunk_hash_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::AugmentChunkHash) {
+        continue;
+      }
       if let Some(plugin_hash) = plugin
         .call_augment_chunk_hash(ctx, Arc::clone(&chunk))
         .instrument(debug_span!(
@@ -133,8 +164,12 @@ impl PluginDriver {
   }
 
   pub async fn render_error(&self, args: &HookRenderErrorArgs<'_>) -> HookNoopReturn {
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_render_error_meta)
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_render_error_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::RenderError) {
+        continue;
+      }
       plugin
         .call_render_error(ctx, args)
         .instrument(debug_span!("render_error_hook", plugin_name = plugin.call_name().as_ref()))
@@ -150,9 +185,12 @@ impl PluginDriver {
     opts: &SharedNormalizedBundlerOptions,
     warnings: &mut Vec<BuildDiagnostic>,
   ) -> HookNoopReturn {
-    for (_, plugin, ctx) in
+    for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_generate_bundle_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::GenerateBundle) {
+        continue;
+      }
       let mut args = crate::HookGenerateBundleArgs { is_write, bundle, options: opts };
       plugin
         .call_generate_bundle(ctx, &mut args)
diff --git a/crates/rolldown_plugin/src/plugin_driver/watch_hooks.rs b/crates/rolldown_plugin/src/plugin_driver/watch_hooks.rs
index 6657d8e9479d..2bd29d5aa138 100644
--- a/crates/rolldown_plugin/src/plugin_driver/watch_hooks.rs
+++ b/crates/rolldown_plugin/src/plugin_driver/watch_hooks.rs
@@ -1,20 +1,28 @@
 use crate::HookNoopReturn;
+use crate::HookUsage;
 use crate::PluginDriver;
 use rolldown_common::WatcherChangeKind;
 
 impl PluginDriver {
   pub async fn watch_change(&self, path: &str, event: WatcherChangeKind) -> HookNoopReturn {
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_watch_change_meta)
+    for (plugin_idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_watch_change_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::WatchChange) {
+        continue;
+      }
       plugin.call_watch_change(ctx, path, event).await?;
     }
     Ok(())
   }
 
   pub async fn close_watcher(&self) -> HookNoopReturn {
-    for (_, plugin, ctx) in
+    for (plugin_idx, plugin, ctx) in
       self.iter_plugin_with_context_by_order(&self.order_by_close_watcher_meta)
     {
+      if !self.plugin_usage_vec[plugin_idx].contains(HookUsage::CloseWatcher) {
+        continue;
+      }
       plugin.call_close_watcher(ctx).await?;
     }
     Ok(())
diff --git a/crates/rolldown_plugin/src/pluginable.rs b/crates/rolldown_plugin/src/pluginable.rs
index 70315e9fb3c3..fe5282ddb090 100644
--- a/crates/rolldown_plugin/src/pluginable.rs
+++ b/crates/rolldown_plugin/src/pluginable.rs
@@ -4,7 +4,7 @@ use super::plugin_context::PluginContext;
 use crate::{
   HookAddonArgs, HookBuildEndArgs, HookBuildStartArgs, HookGenerateBundleArgs,
   HookInjectionOutputReturn, HookLoadArgs, HookRenderChunkArgs, HookRenderStartArgs,
-  HookResolveIdArgs, HookTransformArgs, Plugin, SharedTransformPluginContext,
+  HookResolveIdArgs, HookTransformArgs, HookUsage, Plugin, SharedTransformPluginContext,
   plugin_hook_meta::PluginHookMeta,
   types::{
     hook_render_error::HookRenderErrorArgs, hook_transform_ast_args::HookTransformAstArgs,
@@ -208,6 +208,8 @@ pub trait Pluginable: Any + Debug + Send + Sync + 'static {
   fn call_close_watcher_meta(&self) -> Option<PluginHookMeta> {
     None
   }
+
+  fn call_hook_usage(&self) -> HookUsage;
 }
 
 #[async_trait::async_trait]
@@ -458,4 +460,8 @@ impl<T: Plugin> Pluginable for T {
   fn call_transform_ast_meta(&self) -> Option<PluginHookMeta> {
     Plugin::transform_ast_meta(self)
   }
+
+  fn call_hook_usage(&self) -> HookUsage {
+    Plugin::register_hook_usage(self)
+  }
 }


Reviews:
user: login: hyf0
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T08:41:11Z
user: login: hyf0
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T08:43:07Z
user: login: hyf0
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-18T08:43:12Z
user: login: IWANABETHATGUY
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T08:45:04Z

Review comments:
diff_hunk: @@ -54,8 +55,12 @@ impl PluginDriver {
     //   r?;
     // }
 
-    for (_, plugin, ctx) in self.iter_plugin_with_context_by_order(&self.order_by_build_start_meta)
+    for (idx, plugin, ctx) in
+      self.iter_plugin_with_context_by_order(&self.order_by_build_start_meta)
     {
+      if !self.plugin_usage_vec[idx.raw() as usize].contains(HookUsage::BuildStart) {
path: crates/rolldown_plugin/src/plugin_driver/build_hooks.rs
user: login: hyf0
body: Could it be a `IndexVec`? `idx_raw() as usize` looks cumbersome.
created_at: 2025-04-18T08:41:11Z
author_association: MEMBER
diff_hunk: @@ -458,4 +460,8 @@ impl<T: Plugin> Pluginable for T {
   fn call_transform_ast_meta(&self) -> Option<PluginHookMeta> {
     Plugin::transform_ast_meta(self)
   }
+
+  fn hook_usage(&self) -> HookUsage {
path: crates/rolldown_plugin/src/pluginable.rs
user: login: hyf0
body: Some background:
- Methods of `Pluginable` should be indentical as `Plugin`'s. But to avoid naming conflicts, I just simply add `call_` prefix to each method.
- I don't think we need to follow it. Just add some contexts here.
created_at: 2025-04-18T08:43:07Z
author_association: MEMBER
diff_hunk: @@ -458,4 +460,8 @@ impl<T: Plugin> Pluginable for T {
   fn call_transform_ast_meta(&self) -> Option<PluginHookMeta> {
     Plugin::transform_ast_meta(self)
   }
+
+  fn hook_usage(&self) -> HookUsage {
path: crates/rolldown_plugin/src/pluginable.rs
user: login: IWANABETHATGUY
body: Ok, I will rename it to `call_hook_usage`
created_at: 2025-04-18T08:45:04Z
author_association: MEMBER

PR comments:
user: login: IWANABETHATGUY
created_at: 2025-04-18T08:26:28Z
body: * **#4192** <a href="https://app.graphite.dev/github/pr/rolldown/rolldown/4192?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#4187** <a href="https://app.graphite.dev/github/pr/rolldown/rolldown/4187?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/rolldown/rolldown/4187?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* **#4181** <a href="https://app.graphite.dev/github/pr/rolldown/rolldown/4181?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-04-18T09:00:40Z
body: ### Benchmarks Rust
- target: `main`(6f18af678f735036b36236ccc2479afd13345cb8)
- pr: `04-18-perf_skip_empty_hook_with_hook_usage`(5d1eddf84d53654c9beab944f2aa627acd96dc1c)
```
group                                                               pr                                     target
-----                                                               --                                     ------
bundle/bundle@multi-duplicated-top-level-symbol                     1.02     79.8±1.24ms        ? ?/sec    1.00     78.4±3.36ms        ? ?/sec
bundle/bundle@multi-duplicated-top-level-symbol-minify              1.00    100.3±1.53ms        ? ?/sec    1.00    100.8±3.40ms        ? ?/sec
bundle/bundle@multi-duplicated-top-level-symbol-minify-sourcemap    1.00    113.8±2.34ms        ? ?/sec    1.01    115.0±3.05ms        ? ?/sec
bundle/bundle@multi-duplicated-top-level-symbol-sourcemap           1.00     87.8±1.93ms        ? ?/sec    1.01     89.1±2.90ms        ? ?/sec
bundle/bundle@rome-ts                                               1.00    127.9±2.78ms        ? ?/sec    1.01    129.2±3.46ms        ? ?/sec
bundle/bundle@rome-ts-minify                                        1.05    233.8±4.72ms        ? ?/sec    1.00    222.6±9.11ms        ? ?/sec
bundle/bundle@rome-ts-minify-sourcemap                              1.05    269.5±4.46ms        ? ?/sec    1.00   256.3±11.27ms        ? ?/sec
bundle/bundle@rome-ts-sourcemap                                     1.00    140.0±2.10ms        ? ?/sec    1.00    140.6±2.83ms        ? ?/sec
bundle/bundle@threejs                                               1.03     44.8±4.02ms        ? ?/sec    1.00     43.5±0.89ms        ? ?/sec
bundle/bundle@threejs-minify                                        1.00     88.9±3.28ms        ? ?/sec    1.01     90.2±3.51ms        ? ?/sec
bundle/bundle@threejs-minify-sourcemap                              1.00    104.4±1.33ms        ? ?/sec    1.02    106.8±3.73ms        ? ?/sec
bundle/bundle@threejs-sourcemap                                     1.00     49.1±0.31ms        ? ?/sec    1.01     49.5±0.43ms        ? ?/sec
bundle/bundle@threejs10x                                            1.00    434.7±4.47ms        ? ?/sec    1.00    435.5±7.43ms        ? ?/sec
bundle/bundle@threejs10x-minify                                     1.00   1066.0±8.64ms        ? ?/sec    1.00   1064.0±9.04ms        ? ?/sec
bundle/bundle@threejs10x-minify-sourcemap                           1.01  1257.3±11.59ms        ? ?/sec    1.00  1248.8±11.56ms        ? ?/sec
bundle/bundle@threejs10x-sourcemap                                  1.00    512.6±4.57ms        ? ?/sec    1.00    512.7±8.34ms        ? ?/sec
remapping/remapping                                                 1.00     27.3±1.11ms        ? ?/sec    1.00     27.4±0.28ms        ? ?/sec
remapping/render-chunk-remapping                                    1.05     70.7±4.39ms        ? ?/sec    1.00     67.5±3.89ms        ? ?/sec
scan/scan@rome-ts                                                   1.03    102.6±1.56ms        ? ?/sec    1.00     99.9±2.14ms        ? ?/sec
scan/scan@threejs                                                   1.00     33.7±1.09ms        ? ?/sec    1.01     33.9±1.28ms        ? ?/sec
scan/scan@threejs10x                                                1.00    326.1±3.82ms        ? ?/sec    1.03    334.7±9.81ms        ? ?/sec
```
author_association: CONTRIBUTOR
user: login: netlify[bot]
created_at: 2025-04-19T02:47:08Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *rolldown-rs* canceled.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 5d1eddf84d53654c9beab944f2aa627acd96dc1c |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/rolldown-rs/deploys/68030e8e6201670008d81879 |
author_association: NONE