swc_bundler/bundler/
mod.rs

1use std::collections::HashMap;
2
3use anyhow::{Context, Error};
4use rustc_hash::FxHashMap;
5use swc_atoms::Atom;
6use swc_common::{sync::Lrc, FileName, Globals, Mark, SourceMap, SyntaxContext, GLOBALS};
7use swc_ecma_ast::Module;
8
9use self::scope::Scope;
10use crate::{Hook, Load, ModuleId, Resolve};
11
12mod chunk;
13mod export;
14mod finalize;
15mod helpers;
16mod import;
17mod keywords;
18mod load;
19mod optimize;
20mod scope;
21#[cfg(test)]
22pub(crate) mod tests;
23
24#[derive(Debug, Default)]
25pub struct Config {
26    /// If it's true, [Bundler] searches for require calls.
27    pub require: bool,
28
29    /// If it's true, many temporary variables will be generated.
30    ///
31    /// This option exists mainly for testing. As inlining and dce removes all
32    /// temporary variables, it's really hard to see what's going on.
33    pub disable_inliner: bool,
34
35    /// Useful if you are going to minify the code.
36    pub disable_hygiene: bool,
37
38    pub disable_fixer: bool,
39
40    /// Disable tree-shaking optimization.
41    pub disable_dce: bool,
42
43    /// List of modules which should be preserved.
44    pub external_modules: Vec<Atom>,
45
46    /// Type of emitted module
47    pub module: ModuleType,
48}
49
50#[derive(Debug, PartialEq, Eq, Hash, Default)]
51pub enum ModuleType {
52    #[default]
53    Es,
54    Iife,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub enum BundleKind {
59    /// User-provided entry
60    Named { name: String },
61    /// Auto-generated entry (created by import expression)
62    Dynamic,
63    /// A lazy-loaded shared library
64    Lib { name: String },
65}
66
67/// Built bundle
68#[derive(Debug)]
69pub struct Bundle {
70    pub kind: BundleKind,
71    pub id: ModuleId,
72    /// Merged module.
73    pub module: Module,
74}
75
76pub struct Bundler<'a, L, R>
77where
78    L: Load,
79    R: Resolve,
80{
81    config: Config,
82
83    unresolved_mark: Mark,
84
85    globals: &'a Globals,
86    cm: Lrc<SourceMap>,
87    loader: L,
88    resolver: R,
89
90    _helper_ctxt: SyntaxContext,
91    /// Used to mark nodes as synthesized.
92    ///
93    /// We can check if a span is a dummy for now, but in future we may improve
94    /// spans.
95    synthesized_ctxt: SyntaxContext,
96
97    /// Used to mark a variable declaration as injected.
98    pub(crate) injected_ctxt: SyntaxContext,
99
100    scope: Scope,
101
102    hook: Box<dyn 'a + Hook>,
103}
104
105impl<'a, L, R> Bundler<'a, L, R>
106where
107    L: Load,
108    R: Resolve,
109{
110    pub fn new(
111        globals: &'a Globals,
112        cm: Lrc<SourceMap>,
113        loader: L,
114        resolver: R,
115        config: Config,
116        hook: Box<dyn 'a + Hook>,
117    ) -> Self {
118        GLOBALS.set(globals, || {
119            let helper_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
120            tracing::debug!("Helper ctxt: {:?}", helper_ctxt);
121            let synthesized_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
122            tracing::debug!("Synthesized ctxt: {:?}", synthesized_ctxt);
123            let injected_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
124            tracing::debug!("Injected ctxt: {:?}", injected_ctxt);
125
126            Bundler {
127                config,
128                globals,
129                cm,
130                loader,
131                resolver,
132                _helper_ctxt: helper_ctxt,
133                synthesized_ctxt,
134                injected_ctxt,
135                scope: Default::default(),
136                hook,
137                unresolved_mark: Mark::new(),
138            }
139        })
140    }
141
142    pub(crate) fn is_external(&self, src: &Atom) -> bool {
143        self.config.external_modules.iter().any(|v| v == src)
144    }
145
146    ///
147    ///
148    ///
149    /// Note: This method will panic if entries references each other in
150    /// circular manner. However, it applies only to the provided `entries`, and
151    /// dependencies with circular reference is ok.
152    pub fn bundle(&mut self, entries: HashMap<String, FileName>) -> Result<Vec<Bundle>, Error> {
153        let results = entries
154            .into_iter()
155            .map(|(name, path)| -> Result<_, Error> {
156                let path = match path {
157                    FileName::Real(path) => {
158                        if cfg!(target_os = "windows") {
159                            let path = path
160                                .canonicalize()
161                                .context("failed to canonicalize entry")?;
162                            FileName::Real(path)
163                        } else {
164                            FileName::Real(path)
165                        }
166                    }
167                    _ => path,
168                };
169
170                let res = self
171                    .load_transformed(&path)
172                    .context("load_transformed failed")?;
173                Ok((name, res))
174            })
175            .collect::<Vec<_>>();
176
177        // We collect at here to handle dynamic imports
178        // TODO: Handle dynamic imports
179
180        let local = {
181            let mut output = FxHashMap::default();
182
183            for res in results {
184                let (name, m) = res?;
185                let m = m.unwrap();
186
187                output.insert(name, m);
188            }
189
190            output
191        };
192
193        let bundles = self.chunk(local)?;
194
195        let bundles = self.finalize(bundles, self.unresolved_mark)?;
196
197        #[cfg(feature = "concurrent")]
198        {
199            let scope = std::mem::take(&mut self.scope);
200            rayon::spawn(move || drop(scope))
201        }
202
203        Ok(bundles)
204    }
205
206    /// Sets `swc_common::GLOBALS`
207    #[inline]
208    fn run<F, Ret>(&self, op: F) -> Ret
209    where
210        F: FnOnce() -> Ret,
211    {
212        GLOBALS.set(self.globals, op)
213    }
214}