swc_bundler/
util.rs

1#![allow(dead_code)]
2
3use std::hash::Hash;
4
5use rustc_hash::FxBuildHasher;
6#[cfg(not(feature = "concurrent"))]
7use rustc_hash::FxHashMap;
8use swc_atoms::atom;
9use swc_common::{SyntaxContext, DUMMY_SP};
10use swc_ecma_ast::*;
11use swc_ecma_utils::ident::IdentLike;
12use swc_ecma_visit::{noop_visit_mut_type, VisitMut};
13
14#[cfg(feature = "concurrent")]
15pub(crate) type Readonly<T> = std::sync::Arc<T>;
16
17#[cfg(not(feature = "concurrent"))]
18pub(crate) type Readonly<T> = T;
19
20const TRACK: bool = false;
21
22pub(crate) trait VarDeclaratorExt: Into<VarDeclarator> {
23    fn into_module_item(self, injected_ctxt: SyntaxContext, name: &str) -> ModuleItem {
24        VarDecl {
25            span: DUMMY_SP,
26            ctxt: injected_ctxt,
27            kind: VarDeclKind::Const,
28            declare: false,
29            decls: if TRACK {
30                vec![
31                    self.into(),
32                    Str {
33                        span: DUMMY_SP,
34                        raw: None,
35                        value: name.into(),
36                    }
37                    .assign_to(Ident::new_no_ctxt(atom!("INJECTED_FROM"), DUMMY_SP)),
38                ]
39            } else {
40                vec![self.into()]
41            },
42        }
43        .into()
44    }
45}
46
47impl<T> VarDeclaratorExt for T where T: Into<VarDeclarator> {}
48
49pub(crate) trait ExprExt: Into<Expr> {
50    #[track_caller]
51    fn assign_to<T>(self, lhs: T) -> VarDeclarator
52    where
53        T: IdentLike,
54    {
55        let init = self.into();
56        let lhs = lhs.into_id();
57
58        if cfg!(debug_assertions) {
59            if let Expr::Ident(rhs) = &init {
60                debug_assert_ne!(lhs, rhs.to_id());
61            }
62        }
63
64        VarDeclarator {
65            span: DUMMY_SP,
66            name: Ident::new(lhs.0, DUMMY_SP, lhs.1).into(),
67            init: Some(Box::new(init)),
68            definite: false,
69        }
70    }
71}
72
73impl<T> ExprExt for T where T: Into<Expr> {}
74
75#[derive(Debug)]
76pub(crate) struct CHashSet<V>
77where
78    V: Eq + Hash,
79{
80    inner: CloneMap<V, ()>,
81}
82
83impl<V> CHashSet<V>
84where
85    V: Eq + Hash,
86{
87    // pub fn insert(&self, v: V) -> bool {
88    //     self.inner.insert(v, ()).is_none()
89    // }
90}
91
92impl<V> Default for CHashSet<V>
93where
94    V: Eq + Hash,
95{
96    fn default() -> Self {
97        Self {
98            inner: Default::default(),
99        }
100    }
101}
102
103#[derive(Debug)]
104pub(crate) struct CloneMap<K, V>
105where
106    K: Eq + Hash,
107    V: Clone,
108{
109    #[cfg(feature = "concurrent")]
110    inner: dashmap::DashMap<K, V, FxBuildHasher>,
111    #[cfg(not(feature = "concurrent"))]
112    inner: std::cell::RefCell<FxHashMap<K, V>>,
113}
114
115impl<K, V> Default for CloneMap<K, V>
116where
117    K: Eq + Hash,
118    V: Clone,
119{
120    fn default() -> Self {
121        Self {
122            inner: Default::default(),
123        }
124    }
125}
126
127impl<K, V> CloneMap<K, V>
128where
129    K: Eq + Hash,
130    V: Clone,
131{
132    #[cfg(feature = "concurrent")]
133    pub fn get(&self, k: &K) -> Option<V> {
134        self.inner.get(k).map(|v| v.value().clone())
135    }
136
137    #[cfg(not(feature = "concurrent"))]
138    pub fn get(&self, k: &K) -> Option<V> {
139        self.inner.borrow().get(k).map(|v| v.clone())
140    }
141
142    #[cfg(feature = "concurrent")]
143    pub fn insert(&self, k: K, v: V) -> Option<V> {
144        self.inner.insert(k, v)
145    }
146
147    #[cfg(not(feature = "concurrent"))]
148    pub fn insert(&self, k: K, v: V) -> Option<V> {
149        self.inner.borrow_mut().insert(k, v)
150    }
151}
152
153pub(crate) struct HygieneRemover;
154
155impl VisitMut for HygieneRemover {
156    noop_visit_mut_type!(fail);
157
158    fn visit_mut_syntax_context(&mut self, n: &mut SyntaxContext) {
159        *n = SyntaxContext::empty();
160    }
161}
162
163#[cfg(feature = "rayon")]
164pub(crate) use rayon::join;
165
166#[cfg(not(feature = "rayon"))]
167pub(crate) fn join<A, B, RA, RB>(op_a: A, op_b: B) -> (RA, RB)
168where
169    A: FnOnce() -> RA,
170    B: FnOnce() -> RB,
171{
172    (op_a(), op_b())
173}
174
175#[cfg(feature = "rayon")]
176pub(crate) use rayon::iter::IntoParallelIterator;
177
178/// Fake trait
179#[cfg(not(feature = "rayon"))]
180pub(crate) trait IntoParallelIterator: Sized + IntoIterator {
181    fn into_par_iter(self) -> <Self as IntoIterator>::IntoIter {
182        self.into_iter()
183    }
184}
185
186#[cfg(not(feature = "rayon"))]
187impl<T> IntoParallelIterator for T where T: IntoIterator {}
188
189fn metadata(key: &str, value: &str) -> PropOrSpread {
190    PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
191        key: PropName::Ident(IdentName::new(key.into(), DUMMY_SP)),
192        value: Lit::Str(Str {
193            span: DUMMY_SP,
194            value: value.into(),
195            raw: None,
196        })
197        .into(),
198    })))
199}
200
201#[derive(Debug, Default)]
202pub(crate) struct ExportMetadata {
203    pub injected: bool,
204    pub export_ctxt: Option<SyntaxContext>,
205}
206
207impl ExportMetadata {
208    pub fn into_with(self) -> Box<ObjectLit> {
209        let mut obj = Some(Box::new(ObjectLit {
210            span: DUMMY_SP,
211            props: Vec::new(),
212        }));
213
214        self.encode(&mut obj);
215
216        obj.unwrap()
217    }
218
219    pub fn encode(&self, to: &mut Option<Box<ObjectLit>>) {
220        let mut props = Vec::new();
221
222        if self.injected {
223            props.push(metadata("__swc_bundler__injected__", "1"));
224        }
225
226        if let Some(export_ctxt) = self.export_ctxt {
227            props.push(metadata(
228                "__swc_bundler__export_ctxt__",
229                &export_ctxt.as_u32().to_string(),
230            ));
231        }
232
233        if to.is_none() {
234            *to = Some(Box::new(ObjectLit {
235                span: DUMMY_SP,
236                props,
237            }));
238        } else {
239            let obj = to.as_mut().unwrap();
240            obj.props.extend(props);
241        }
242    }
243
244    pub fn decode(with: Option<&ObjectLit>) -> Self {
245        let mut data = ExportMetadata::default();
246
247        if let Some(with) = with {
248            for prop in &with.props {
249                if let PropOrSpread::Prop(p) = prop {
250                    if let Prop::KeyValue(KeyValueProp {
251                        key: PropName::Ident(IdentName { sym, .. }),
252                        value,
253                        ..
254                    }) = &**p
255                    {
256                        if *sym == "__swc_bundler__injected__" {
257                            if let Expr::Lit(Lit::Str(Str { value, .. })) = &**value {
258                                if value == "1" {
259                                    data.injected = true;
260                                }
261                            }
262                        } else if *sym == "__swc_bundler__export_ctxt__" {
263                            if let Expr::Lit(Lit::Str(Str { value, .. })) = &**value {
264                                if let Some(value) = value.as_str() {
265                                    if let Ok(v) = value.parse() {
266                                        data.export_ctxt = Some(SyntaxContext::from_u32(v));
267                                    }
268                                }
269                            }
270                        }
271                    }
272                }
273            }
274        }
275
276        data
277    }
278}
279
280pub(crate) fn is_injected(with: &ObjectLit) -> bool {
281    ExportMetadata::decode(Some(with)).injected
282}