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 pub require: bool,
28
29 pub disable_inliner: bool,
34
35 pub disable_hygiene: bool,
37
38 pub disable_fixer: bool,
39
40 pub disable_dce: bool,
42
43 pub external_modules: Vec<Atom>,
45
46 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 Named { name: String },
61 Dynamic,
63 Lib { name: String },
65}
66
67#[derive(Debug)]
69pub struct Bundle {
70 pub kind: BundleKind,
71 pub id: ModuleId,
72 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 synthesized_ctxt: SyntaxContext,
96
97 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 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 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 #[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}