use std::{ops::Deref, sync::Arc};
use crate::PluginContext;
use arcstr::ArcStr;
use rolldown_common::{ModuleIdx, PluginIdx, SourceMapGenMsg, SourcemapChainElement};
use rolldown_sourcemap::{SourceMap, collapse_sourcemaps, empty_sourcemap};
use rolldown_utils::unique_arc::WeakRef;
use std::sync::mpsc;
use string_wizard::{MagicString, SourceMapOptions};
#[derive(Debug)]
pub struct TransformPluginContext {
pub inner: PluginContext,
sourcemap_chain: WeakRef<Vec<SourcemapChainElement>>,
original_code: ArcStr,
id: ArcStr,
module_idx: ModuleIdx,
plugin_idx: PluginIdx,
magic_string_tx: Option<Arc<mpsc::Sender<SourceMapGenMsg>>>,
}
impl TransformPluginContext {
pub fn new(
inner: PluginContext,
sourcemap_chain: WeakRef<Vec<SourcemapChainElement>>,
original_code: ArcStr,
id: ArcStr,
module_idx: ModuleIdx,
plugin_idx: PluginIdx,
magic_string_tx: Option<Arc<mpsc::Sender<SourceMapGenMsg>>>,
) -> Self {
Self { inner, sourcemap_chain, original_code, id, module_idx, plugin_idx, magic_string_tx }
}
pub fn get_combined_sourcemap(&self) -> SourceMap {
self.sourcemap_chain.with_inner(|sourcemap_chain| {
let empty_map = empty_sourcemap();
let chain: Vec<&SourceMap> = sourcemap_chain
.iter()
.filter_map(|element| match element {
SourcemapChainElement::Transform((_, sourcemap))
| SourcemapChainElement::Load(sourcemap) => Some(sourcemap),
SourcemapChainElement::Omitted { .. } => Some(&empty_map),
SourcemapChainElement::Null { .. } => None,
})
.collect();
match chain.as_slice() {
[] => self.create_sourcemap(),
[single] => (*single).clone(),
_ => collapse_sourcemaps(&chain),
}
})
}
fn create_sourcemap(&self) -> SourceMap {
let magic_string = MagicString::new(self.original_code.as_str());
magic_string.source_map(SourceMapOptions {
hires: string_wizard::Hires::Boundary,
include_content: true,
source: self.id.as_str().into(),
})
}
pub fn add_watch_file(&self, file: &str) {
if self.id.starts_with('\0') {
return;
}
self.inner.add_watch_file(file);
if let crate::PluginContext::Native(ctx) = &self.inner {
if let Some(plugin_driver) = ctx.plugin_driver.upgrade() {
plugin_driver.add_transform_dependency(self.module_idx, file);
}
}
}
pub fn send_magic_string(
&self,
magic_string: MagicString<'static>,
) -> Result<Option<String>, mpsc::SendError<SourceMapGenMsg>> {
if let Some(tx) = self.magic_string_tx.as_ref() {
tx.send(SourceMapGenMsg::MagicString(Box::new((
self.module_idx,
self.plugin_idx,
self.id.clone(),
magic_string,
))))
.map(|()| None)
} else {
Ok(Some(magic_string.source_map(string_wizard::SourceMapOptions::default()).to_json_string()))
}
}
}
impl Deref for TransformPluginContext {
type Target = PluginContext;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
pub type SharedTransformPluginContext = Arc<TransformPluginContext>;