brk_rolldown_plugin/plugin_driver/
output_hooks.rs1use 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}