swc_bundler/bundler/chunk/
mod.rs1use std::time::Instant;
2
3use anyhow::{Context, Error};
4#[cfg(feature = "rayon")]
5use rayon::iter::ParallelIterator;
6use rustc_hash::FxHashMap;
7
8use super::{load::TransformedModule, Bundler};
9use crate::{
10 bundler::chunk::merge::Ctx, load::Load, resolve::Resolve, util::IntoParallelIterator, Bundle,
11};
12
13mod cjs;
14mod computed_key;
15mod merge;
16mod plan;
17
18impl<L, R> Bundler<'_, L, R>
19where
20 L: Load,
21 R: Resolve,
22{
23 pub(super) fn chunk(
29 &self,
30 entries: FxHashMap<String, TransformedModule>,
31 ) -> Result<Vec<Bundle>, Error> {
32 #[cfg(not(target_arch = "wasm32"))]
33 let start = Instant::now();
34 let (plan, graph, cycles) = self.determine_entries(entries).context("failed to plan")?;
35 #[cfg(not(target_arch = "wasm32"))]
36 let dur = Instant::now() - start;
37 #[cfg(not(target_arch = "wasm32"))]
38 tracing::debug!("Dependency analysis took {:?}", dur);
39
40 if cfg!(debug_assertions) {
41 for (i, id1) in plan.all.iter().enumerate() {
42 for (j, id2) in plan.all.iter().enumerate() {
43 if i == j {
44 continue;
45 }
46
47 debug_assert_ne!(
48 id1, id2,
49 "Dependency analysis created duplicate entries: {id1:?}"
50 )
51 }
52 }
53 }
54
55 let ctx = Ctx {
56 graph,
57 cycles,
58 transitive_remap: Default::default(),
59 export_stars_in_wrapped: Default::default(),
60 };
61
62 #[cfg(not(target_arch = "wasm32"))]
63 let start = Instant::now();
64 let mut all = (&*plan.all)
65 .into_par_iter()
66 .map(|id| -> Result<_, Error> {
67 self.run(|| {
68 let is_entry = plan.entries.contains_key(id);
70 let module = self.get_for_merging(&ctx, *id, is_entry)?;
71
72 Ok((*id, module))
73 })
74 })
75 .collect::<Result<FxHashMap<_, _>, _>>()?;
76
77 #[cfg(not(target_arch = "wasm32"))]
78 let dur = Instant::now() - start;
79 #[cfg(not(target_arch = "wasm32"))]
80 tracing::debug!("Module preparation took {:?}", dur);
81
82 let entries = all
83 .iter()
84 .filter_map(|(id, module)| {
85 if plan.entries.contains_key(id) {
86 return Some((*id, module.clone()));
87 }
88 None
89 })
90 .collect::<Vec<_>>();
91
92 let merged: Vec<_> = if entries.len() == 1 {
93 entries
94 .into_iter()
95 .map(|(id, mut entry)| {
96 self.merge_into_entry(&ctx, id, &mut entry, &mut all);
97
98 tracing::debug!("Merged `{}` and it's dep into an entry", id);
99
100 (id, entry)
101 })
102 .map(|(id, module)| {
103 let kind = plan
104 .entries
105 .get(&id)
106 .unwrap_or_else(|| {
107 unreachable!("Plan does not contain bundle kind for {:?}", id)
108 })
109 .clone();
110 Bundle {
111 kind,
112 id,
113 module: module.into(),
114 }
115 })
116 .collect()
117 } else {
118 entries
119 .into_iter()
120 .map(|(id, mut entry)| {
121 let mut a = all.clone();
122 self.merge_into_entry(&ctx, id, &mut entry, &mut a);
123
124 tracing::debug!("Merged `{}` and it's dep into an entry", id);
125
126 (id, entry)
127 })
128 .map(|(id, module)| {
129 let kind = plan
130 .entries
131 .get(&id)
132 .unwrap_or_else(|| {
133 unreachable!("Plan does not contain bundle kind for {:?}", id)
134 })
135 .clone();
136 Bundle {
137 kind,
138 id,
139 module: module.into(),
140 }
141 })
142 .collect()
143 };
144
145 Ok(merged)
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use swc_common::FileName;
152
153 use super::*;
154 use crate::bundler::tests::suite;
155
156 #[test]
157 fn cjs_chunk() {
158 suite()
159 .file(
160 "main.js",
161 "
162 require('./a');
163 require('./b');
164 ",
165 )
166 .file("a.js", "require('./common')")
167 .file("b.js", "require('./common')")
168 .file("common.js", r#"console.log('foo')"#)
169 .run(|t| {
170 let module = t
171 .bundler
172 .load_transformed(&FileName::Real("main.js".into()))?
173 .unwrap();
174 let mut entries = FxHashMap::default();
175 entries.insert("main.js".to_string(), module);
176
177 let chunked = t.bundler.chunk(entries)?;
178 assert_eq!(chunked.len(), 1);
179
180 Ok(())
181 });
182 }
183}