Skip to main content

rolldown_plugin/plugin_context/
transform_plugin_context.rs

1use std::{ops::Deref, sync::Arc};
2
3use crate::PluginContext;
4use arcstr::ArcStr;
5use rolldown_common::{ModuleIdx, PluginIdx, SourceMapGenMsg, SourcemapChainElement, WatchPath};
6use rolldown_sourcemap::{SourceMap, collapse_sourcemaps, empty_sourcemap};
7use rolldown_utils::unique_arc::WeakRef;
8use std::sync::mpsc;
9use string_wizard::{MagicString, SourceMapOptions};
10
11#[derive(Debug)]
12pub struct TransformPluginContext {
13  pub inner: PluginContext,
14  sourcemap_chain: WeakRef<Vec<SourcemapChainElement>>,
15  original_code: ArcStr,
16  id: ArcStr,
17  module_idx: ModuleIdx,
18  plugin_idx: PluginIdx,
19  magic_string_tx: Option<mpsc::Sender<SourceMapGenMsg>>,
20}
21
22impl TransformPluginContext {
23  pub fn new(
24    inner: PluginContext,
25    sourcemap_chain: WeakRef<Vec<SourcemapChainElement>>,
26    original_code: ArcStr,
27    id: ArcStr,
28    module_idx: ModuleIdx,
29    plugin_idx: PluginIdx,
30    magic_string_tx: Option<mpsc::Sender<SourceMapGenMsg>>,
31  ) -> Self {
32    Self { inner, sourcemap_chain, original_code, id, module_idx, plugin_idx, magic_string_tx }
33  }
34
35  pub fn get_combined_sourcemap(&self) -> SourceMap {
36    self.sourcemap_chain.with_inner(|sourcemap_chain| {
37      let empty_map = empty_sourcemap();
38      let chain: Vec<&SourceMap> = sourcemap_chain
39        .iter()
40        .filter_map(|element| match element {
41          SourcemapChainElement::Transform((_, sourcemap))
42          | SourcemapChainElement::Load(sourcemap) => Some(sourcemap),
43          SourcemapChainElement::Omitted { .. } => Some(&empty_map),
44          SourcemapChainElement::Null { .. } => None,
45        })
46        .collect();
47      match chain.as_slice() {
48        [] => self.create_sourcemap(),
49        [single] => (*single).clone(),
50        // TODO Here could be cache result for pervious sourcemap_chain, only remapping new sourcemap chain
51        _ => collapse_sourcemaps(&chain),
52      }
53    })
54  }
55
56  fn create_sourcemap(&self) -> SourceMap {
57    let magic_string = MagicString::new(self.original_code.as_str());
58    magic_string.source_map(SourceMapOptions {
59      hires: string_wizard::Hires::Boundary,
60      include_content: true,
61      source: self.id.as_str().into(),
62    })
63  }
64
65  /// Add a file or a directory as a watch dependency.
66  pub fn add_watch_file(&self, file: &str) {
67    // Skip all operations for virtual modules (starting with \0)
68    // Virtual modules can't be refetched from disk during HMR
69    if self.id.starts_with('\0') {
70      return;
71    }
72
73    // Add to global watch files
74    self.inner.add_watch_file(file);
75
76    // Add to this module's transform dependencies
77    if let crate::PluginContext::Native(ctx) = &self.inner {
78      if let Some(plugin_driver) = ctx.plugin_driver.upgrade() {
79        plugin_driver
80          .add_transform_dependency(self.module_idx, WatchPath::new(file, &ctx.options.cwd));
81      }
82    }
83  }
84
85  pub fn send_magic_string(
86    &self,
87    magic_string: MagicString<'static>,
88  ) -> Result<Option<String>, mpsc::SendError<SourceMapGenMsg>> {
89    if let Some(tx) = self.magic_string_tx.as_ref() {
90      tx.send(SourceMapGenMsg::MagicString(Box::new((
91        self.module_idx,
92        self.plugin_idx,
93        self.id.clone(),
94        magic_string,
95      ))))
96      .map(|()| None)
97    } else {
98      Ok(Some(magic_string.source_map(string_wizard::SourceMapOptions::default()).to_json_string()))
99    }
100  }
101}
102
103impl Deref for TransformPluginContext {
104  type Target = PluginContext;
105
106  fn deref(&self) -> &Self::Target {
107    &self.inner
108  }
109}
110
111pub type SharedTransformPluginContext = Arc<TransformPluginContext>;