brk_rolldown_plugin/plugin_driver/
output_hooks.rs

1use std::sync::Arc;
2
3use crate::types::hook_render_error::HookRenderErrorArgs;
4use crate::{HookAddonArgs, PluginDriver};
5use crate::{HookAugmentChunkHashReturn, HookNoopReturn, HookRenderChunkArgs};
6use anyhow::{Context, Ok, Result};
7use rolldown_common::{Output, RollupRenderedChunk, SharedNormalizedBundlerOptions};
8use rolldown_devtools::{action, trace_action};
9use rolldown_error::{BuildDiagnostic, CausedPlugin};
10use rolldown_sourcemap::SourceMap;
11use tracing::Instrument;
12
13impl PluginDriver {
14  pub async fn render_start(&self, opts: &SharedNormalizedBundlerOptions) -> HookNoopReturn {
15    for (plugin_idx, plugin, ctx) in
16      self.iter_plugin_with_context_by_order(&self.order_by_render_start_meta)
17    {
18      let start = self.start_timing();
19      let result =
20        plugin.call_render_start(ctx, &crate::HookRenderStartArgs { options: opts }).await;
21      self.record_timing(plugin_idx, start);
22      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
23    }
24    Ok(())
25  }
26
27  pub async fn banner(&self, args: HookAddonArgs, mut banner: String) -> Result<Option<String>> {
28    for (plugin_idx, plugin, ctx) in
29      self.iter_plugin_with_context_by_order(&self.order_by_banner_meta)
30    {
31      let start = self.start_timing();
32      let result = plugin.call_banner(ctx, &args).await;
33      self.record_timing(plugin_idx, start);
34      if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
35        banner.push('\n');
36        banner.push_str(r.as_str());
37      }
38    }
39    if banner.is_empty() {
40      return Ok(None);
41    }
42    Ok(Some(banner))
43  }
44
45  pub async fn footer(&self, args: HookAddonArgs, mut footer: String) -> Result<Option<String>> {
46    for (plugin_idx, plugin, ctx) in
47      self.iter_plugin_with_context_by_order(&self.order_by_footer_meta)
48    {
49      let start = self.start_timing();
50      let result = plugin.call_footer(ctx, &args).await;
51      self.record_timing(plugin_idx, start);
52      if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
53        footer.push('\n');
54        footer.push_str(r.as_str());
55      }
56    }
57    if footer.is_empty() {
58      return Ok(None);
59    }
60    Ok(Some(footer))
61  }
62
63  pub async fn intro(&self, args: HookAddonArgs, mut intro: String) -> Result<Option<String>> {
64    for (plugin_idx, plugin, ctx) in
65      self.iter_plugin_with_context_by_order(&self.order_by_intro_meta)
66    {
67      let start = self.start_timing();
68      let result = plugin.call_intro(ctx, &args).await;
69      self.record_timing(plugin_idx, start);
70      if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
71        intro.push('\n');
72        intro.push_str(r.as_str());
73      }
74    }
75    if intro.is_empty() {
76      return Ok(None);
77    }
78    Ok(Some(intro))
79  }
80
81  pub async fn outro(&self, args: HookAddonArgs, mut outro: String) -> Result<Option<String>> {
82    for (plugin_idx, plugin, ctx) in
83      self.iter_plugin_with_context_by_order(&self.order_by_outro_meta)
84    {
85      let start = self.start_timing();
86      let result = plugin.call_outro(ctx, &args).await;
87      self.record_timing(plugin_idx, start);
88      if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
89        outro.push('\n');
90        outro.push_str(r.as_str());
91      }
92    }
93    if outro.is_empty() {
94      return Ok(None);
95    }
96    Ok(Some(outro))
97  }
98
99  pub async fn render_chunk(
100    &self,
101    mut args: HookRenderChunkArgs<'_>,
102  ) -> Result<(String, Vec<SourceMap>)> {
103    let mut sourcemap_chain = vec![];
104    for (plugin_idx, plugin, ctx) in
105      self.iter_plugin_with_context_by_order(&self.order_by_render_chunk_meta)
106    {
107      async {
108        trace_action!(action::HookRenderChunkStart {
109          action: "HookRenderChunkStart",
110          plugin_name: plugin.call_name().to_string(),
111          plugin_id: plugin_idx.raw(),
112          call_id: "${call_id}",
113          content: args.code.clone(),
114        });
115        let start = self.start_timing();
116        let result = plugin.call_render_chunk(ctx, &args).await;
117        self.record_timing(plugin_idx, start);
118        if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
119          args.code = r.code;
120          if let Some(map) = r.map {
121            sourcemap_chain.push(map);
122          }
123          trace_action!(action::HookRenderChunkEnd {
124            action: "HookRenderChunkEnd",
125            plugin_name: plugin.call_name().to_string(),
126            plugin_id: plugin_idx.raw(),
127            call_id: "${call_id}",
128            content: Some(args.code.clone()),
129          });
130        } else {
131          trace_action!(action::HookRenderChunkEnd {
132            action: "HookRenderChunkEnd",
133            plugin_name: plugin.call_name().to_string(),
134            plugin_id: plugin_idx.raw(),
135            call_id: "${call_id}",
136            content: None,
137          });
138        }
139
140        Ok(())
141      }
142      .instrument(tracing::trace_span!(
143        "HookRenderChunk",
144        CONTEXT_call_id = rolldown_utils::uuid::uuid_v4()
145      ))
146      .await?;
147    }
148    Ok((args.code, sourcemap_chain))
149  }
150
151  pub async fn augment_chunk_hash(
152    &self,
153    chunk: Arc<RollupRenderedChunk>,
154  ) -> HookAugmentChunkHashReturn {
155    let mut hash = None;
156    for (plugin_idx, plugin, ctx) in
157      self.iter_plugin_with_context_by_order(&self.order_by_augment_chunk_hash_meta)
158    {
159      let start = self.start_timing();
160      let result = plugin.call_augment_chunk_hash(ctx, Arc::clone(&chunk)).await;
161      self.record_timing(plugin_idx, start);
162      if let Some(plugin_hash) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
163        hash.get_or_insert_with(String::default).push_str(&plugin_hash);
164      }
165    }
166    Ok(hash)
167  }
168
169  pub async fn render_error(&self, args: &HookRenderErrorArgs<'_>) -> HookNoopReturn {
170    for (plugin_idx, plugin, ctx) in
171      self.iter_plugin_with_context_by_order(&self.order_by_render_error_meta)
172    {
173      let start = self.start_timing();
174      let result = plugin.call_render_error(ctx, args).await;
175      self.record_timing(plugin_idx, start);
176      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
177    }
178    Ok(())
179  }
180
181  pub async fn generate_bundle(
182    &self,
183    bundle: &mut Vec<Output>,
184    is_write: bool,
185    opts: &SharedNormalizedBundlerOptions,
186    warnings: &mut Vec<BuildDiagnostic>,
187  ) -> HookNoopReturn {
188    for (plugin_idx, plugin, ctx) in
189      self.iter_plugin_with_context_by_order(&self.order_by_generate_bundle_meta)
190    {
191      let mut args = crate::HookGenerateBundleArgs { is_write, bundle, options: opts };
192      let start = self.start_timing();
193      let result = plugin.call_generate_bundle(ctx, &mut args).await;
194      self.record_timing(plugin_idx, start);
195      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
196      ctx.file_emitter().add_additional_files(bundle, warnings);
197    }
198    Ok(())
199  }
200
201  pub async fn write_bundle(
202    &self,
203    bundle: &mut Vec<Output>,
204    opts: &SharedNormalizedBundlerOptions,
205    warnings: &mut Vec<BuildDiagnostic>,
206  ) -> HookNoopReturn {
207    for (plugin_idx, plugin, ctx) in
208      self.iter_plugin_with_context_by_order(&self.order_by_write_bundle_meta)
209    {
210      let mut args = crate::HookWriteBundleArgs { bundle, options: opts };
211      let start = self.start_timing();
212      let result = plugin.call_write_bundle(ctx, &mut args).await;
213      self.record_timing(plugin_idx, start);
214      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
215      ctx.file_emitter().add_additional_files(bundle, warnings);
216    }
217    Ok(())
218  }
219
220  pub async fn close_bundle(&self) -> HookNoopReturn {
221    for (plugin_idx, plugin, ctx) in
222      self.iter_plugin_with_context_by_order(&self.order_by_close_bundle_meta)
223    {
224      let start = self.start_timing();
225      let result = plugin.call_close_bundle(ctx).await;
226      self.record_timing(plugin_idx, start);
227      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
228    }
229    Ok(())
230  }
231}