rolldown_plugin 0.1.1

Plugin interface for Rolldown bundler
Documentation
use std::sync::Arc;

use crate::types::hook_close_bundle_args::HookCloseBundleArgs;
use crate::types::hook_render_error::HookRenderErrorArgs;
use crate::{HookAddonArgs, PluginDriver};
use crate::{HookAugmentChunkHashReturn, HookNoopReturn, HookRenderChunkArgs};
use anyhow::{Context, Ok, Result};
use rolldown_common::{Output, RollupRenderedChunk, SharedNormalizedBundlerOptions};
use rolldown_devtools::{action, trace_action, trace_action_enabled};
use rolldown_error::{BuildDiagnostic, CausedPlugin};
use rolldown_sourcemap::SourceMap;
use tracing::Instrument;

impl PluginDriver {
  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::render_start",
    skip_all
  )]
  pub async fn render_start(&self, opts: &SharedNormalizedBundlerOptions) -> HookNoopReturn {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_render_start_meta)
    {
      let start = self.start_timing();
      let result =
        plugin.call_render_start(ctx, &crate::HookRenderStartArgs { options: opts }).await;
      self.record_timing(plugin_idx, start);
      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
    }
    Ok(())
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::banner",
    skip_all
  )]
  pub async fn banner(&self, args: HookAddonArgs, mut banner: String) -> Result<Option<String>> {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_banner_meta)
    {
      let start = self.start_timing();
      let result = plugin.call_banner(ctx, &args).await;
      self.record_timing(plugin_idx, start);
      if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
        banner.push('\n');
        banner.push_str(r.as_str());
      }
    }
    if banner.is_empty() {
      return Ok(None);
    }
    Ok(Some(banner))
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::footer",
    skip_all
  )]
  pub async fn footer(&self, args: HookAddonArgs, mut footer: String) -> Result<Option<String>> {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_footer_meta)
    {
      let start = self.start_timing();
      let result = plugin.call_footer(ctx, &args).await;
      self.record_timing(plugin_idx, start);
      if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
        footer.push('\n');
        footer.push_str(r.as_str());
      }
    }
    if footer.is_empty() {
      return Ok(None);
    }
    Ok(Some(footer))
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::intro",
    skip_all
  )]
  pub async fn intro(&self, args: HookAddonArgs, mut intro: String) -> Result<Option<String>> {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_intro_meta)
    {
      let start = self.start_timing();
      let result = plugin.call_intro(ctx, &args).await;
      self.record_timing(plugin_idx, start);
      if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
        intro.push('\n');
        intro.push_str(r.as_str());
      }
    }
    if intro.is_empty() {
      return Ok(None);
    }
    Ok(Some(intro))
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::outro",
    skip_all
  )]
  pub async fn outro(&self, args: HookAddonArgs, mut outro: String) -> Result<Option<String>> {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_outro_meta)
    {
      let start = self.start_timing();
      let result = plugin.call_outro(ctx, &args).await;
      self.record_timing(plugin_idx, start);
      if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
        outro.push('\n');
        outro.push_str(r.as_str());
      }
    }
    if outro.is_empty() {
      return Ok(None);
    }
    Ok(Some(outro))
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::render_chunk",
    skip_all
  )]
  pub async fn render_chunk(
    &self,
    mut args: HookRenderChunkArgs<'_>,
  ) -> Result<(String, Vec<SourceMap>)> {
    let mut sourcemap_chain = vec![];
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_render_chunk_meta)
    {
      async {
        if trace_action_enabled!() {
          trace_action!(action::HookRenderChunkStart {
            action: "HookRenderChunkStart",
            plugin_name: plugin.call_name().to_string(),
            plugin_id: plugin_idx.raw(),
            call_id: "${call_id}",
            content: args.code.as_str().to_string(),
          });
        }
        let start = self.start_timing();
        let result = plugin.call_render_chunk(ctx, &args).await;
        self.record_timing(plugin_idx, start);
        if let Some(r) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
          args.code = Arc::new(r.code);
          if let Some(map) = r.map {
            sourcemap_chain.push(map);
          }
          if trace_action_enabled!() {
            trace_action!(action::HookRenderChunkEnd {
              action: "HookRenderChunkEnd",
              plugin_name: plugin.call_name().to_string(),
              plugin_id: plugin_idx.raw(),
              call_id: "${call_id}",
              content: Some(args.code.as_str().to_string()),
            });
          }
        } else if trace_action_enabled!() {
          trace_action!(action::HookRenderChunkEnd {
            action: "HookRenderChunkEnd",
            plugin_name: plugin.call_name().to_string(),
            plugin_id: plugin_idx.raw(),
            call_id: "${call_id}",
            content: None,
          });
        }

        Ok(())
      }
      .instrument(tracing::trace_span!(
        "HookRenderChunk",
        CONTEXT_call_id = rolldown_utils::uuid::uuid_v4()
      ))
      .await?;
    }
    Ok((args.into_code(), sourcemap_chain))
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::augment_chunk_hash",
    skip_all
  )]
  pub async fn augment_chunk_hash(
    &self,
    chunk: Arc<RollupRenderedChunk>,
  ) -> HookAugmentChunkHashReturn {
    let mut hash = None;
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_augment_chunk_hash_meta)
    {
      let start = self.start_timing();
      let result = plugin.call_augment_chunk_hash(ctx, Arc::clone(&chunk)).await;
      self.record_timing(plugin_idx, start);
      if let Some(plugin_hash) = result.with_context(|| CausedPlugin::new(plugin.call_name()))? {
        hash.get_or_insert_with(String::default).push_str(&plugin_hash);
      }
    }
    Ok(hash)
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::render_error",
    skip_all
  )]
  pub async fn render_error(&self, args: &HookRenderErrorArgs<'_>) -> HookNoopReturn {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_render_error_meta)
    {
      let start = self.start_timing();
      let result = plugin.call_render_error(ctx, args).await;
      self.record_timing(plugin_idx, start);
      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
    }
    Ok(())
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::generate_bundle",
    skip_all
  )]
  pub async fn generate_bundle(
    &self,
    bundle: &mut Vec<Output>,
    is_write: bool,
    opts: &SharedNormalizedBundlerOptions,
    warnings: &mut Vec<BuildDiagnostic>,
  ) -> HookNoopReturn {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_generate_bundle_meta)
    {
      let mut args = crate::HookGenerateBundleArgs { is_write, bundle, options: opts };
      let start = self.start_timing();
      let result = plugin.call_generate_bundle(ctx, &mut args).await;
      self.record_timing(plugin_idx, start);
      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
      ctx.file_emitter().add_additional_files(bundle, warnings);
    }
    Ok(())
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::write_bundle",
    skip_all
  )]
  pub async fn write_bundle(
    &self,
    bundle: &mut Vec<Output>,
    opts: &SharedNormalizedBundlerOptions,
    warnings: &mut Vec<BuildDiagnostic>,
  ) -> HookNoopReturn {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_write_bundle_meta)
    {
      let mut args = crate::HookWriteBundleArgs { bundle, options: opts };
      let start = self.start_timing();
      let result = plugin.call_write_bundle(ctx, &mut args).await;
      self.record_timing(plugin_idx, start);
      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
      ctx.file_emitter().add_additional_files(bundle, warnings);
    }
    Ok(())
  }

  #[tracing::instrument(
    level = "trace",
    target = "rolldown_plugin::plugin_driver::output_hooks::total::close_bundle",
    skip_all
  )]
  pub async fn close_bundle(&self, args: Option<&HookCloseBundleArgs<'_>>) -> HookNoopReturn {
    for (plugin_idx, plugin, ctx) in
      self.iter_plugin_with_context_by_order(&self.order_by_close_bundle_meta)
    {
      let start = self.start_timing();
      let result = plugin.call_close_bundle(ctx, args).await;
      self.record_timing(plugin_idx, start);
      result.with_context(|| CausedPlugin::new(plugin.call_name()))?;
    }
    Ok(())
  }
}