1use std::path::{Path, PathBuf};
2use std::sync::Arc;
3
4use serde::{Deserialize, Serialize};
5
6use super::symbol::SymbolTable;
7use super::{Export, Import, ModuleId};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Module {
15 pub id: ModuleId,
16 pub path: PathBuf,
17 pub source_type: SourceType,
18 #[serde(with = "arc_vec_serde")]
19 pub imports: Arc<Vec<Import>>,
20 #[serde(with = "arc_vec_serde")]
21 pub exports: Arc<Vec<Export>>,
22 pub has_side_effects: bool,
23 pub is_entry: bool,
24 pub is_external: bool,
25 pub original_size: usize,
26 pub bundled_size: Option<usize>,
27 #[serde(with = "arc_symbol_table_serde")]
29 pub symbol_table: Arc<SymbolTable>,
30 pub module_format: ModuleFormat,
32 pub exports_kind: ExportsKind,
34 pub has_star_exports: bool,
36 pub execution_order: Option<u32>,
38}
39
40mod arc_vec_serde {
42 use super::*;
43 use serde::de::Deserializer;
44 use serde::ser::Serializer;
45
46 pub fn serialize<S, T>(value: &Arc<Vec<T>>, serializer: S) -> Result<S::Ok, S::Error>
47 where
48 S: Serializer,
49 T: Serialize,
50 {
51 value.as_ref().serialize(serializer)
52 }
53
54 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Arc<Vec<T>>, D::Error>
55 where
56 D: Deserializer<'de>,
57 T: Deserialize<'de>,
58 {
59 Vec::deserialize(deserializer).map(Arc::new)
60 }
61}
62
63mod arc_symbol_table_serde {
65 use super::*;
66 use serde::de::Deserializer;
67 use serde::ser::Serializer;
68
69 pub fn serialize<S>(value: &Arc<SymbolTable>, serializer: S) -> Result<S::Ok, S::Error>
70 where
71 S: Serializer,
72 {
73 value.as_ref().serialize(serializer)
74 }
75
76 pub fn deserialize<'de, D>(deserializer: D) -> Result<Arc<SymbolTable>, D::Error>
77 where
78 D: Deserializer<'de>,
79 {
80 SymbolTable::deserialize(deserializer).map(Arc::new)
81 }
82}
83
84impl Module {
85 pub fn builder(id: ModuleId, path: PathBuf, source_type: SourceType) -> ModuleBuilder {
87 ModuleBuilder {
88 module: Self {
89 id,
90 path,
91 source_type,
92 imports: Arc::new(Vec::new()),
93 exports: Arc::new(Vec::new()),
94 has_side_effects: false,
95 is_entry: false,
96 is_external: false,
97 original_size: 0,
98 bundled_size: None,
99 symbol_table: Arc::new(SymbolTable::new()),
100 module_format: ModuleFormat::Unknown,
101 exports_kind: ExportsKind::None,
102 has_star_exports: false,
103 execution_order: None,
104 },
105 }
106 }
107
108 pub fn mark_entry(&mut self) {
110 self.is_entry = true;
111 }
112
113 pub fn mark_external(&mut self) {
115 self.is_external = true;
116 }
117
118 pub fn set_side_effects(&mut self, has_side_effects: bool) {
120 self.has_side_effects = has_side_effects;
121 }
122
123 pub fn set_bundled_size(&mut self, size: Option<usize>) {
125 self.bundled_size = size;
126 }
127
128 pub fn imports_iter(&self) -> impl Iterator<Item = &Import> {
130 self.imports.iter()
131 }
132
133 pub fn exports_iter(&self) -> impl Iterator<Item = &Export> {
135 self.exports.iter()
136 }
137
138 pub fn exports_mut(&mut self) -> &mut Vec<Export> {
152 Arc::make_mut(&mut self.exports)
153 }
154
155 pub fn imports_from(&self, target: &ModuleId) -> Vec<&Import> {
167 self.imports
168 .iter()
169 .filter(|imp| imp.resolved_to.as_ref() == Some(target))
170 .collect()
171 }
172
173 pub fn has_import_from(&self, source: &str) -> bool {
186 self.imports.iter().any(|imp| imp.source == source)
187 }
188
189 pub fn import_sources(&self) -> Vec<&str> {
200 self.imports.iter().map(|imp| imp.source.as_str()).collect()
201 }
202}
203
204pub struct ModuleBuilder {
206 module: Module,
207}
208
209impl ModuleBuilder {
210 pub fn imports(mut self, imports: Vec<Import>) -> Self {
211 self.module.imports = Arc::new(imports);
212 self
213 }
214
215 pub fn exports(mut self, exports: Vec<Export>) -> Self {
216 self.module.exports = Arc::new(exports);
217 self
218 }
219
220 pub fn side_effects(mut self, has_side_effects: bool) -> Self {
221 self.module.has_side_effects = has_side_effects;
222 self
223 }
224
225 pub fn entry(mut self, is_entry: bool) -> Self {
226 self.module.is_entry = is_entry;
227 self
228 }
229
230 pub fn external(mut self, is_external: bool) -> Self {
231 self.module.is_external = is_external;
232 self
233 }
234
235 pub fn original_size(mut self, original_size: usize) -> Self {
236 self.module.original_size = original_size;
237 self
238 }
239
240 pub fn bundled_size(mut self, bundled_size: Option<usize>) -> Self {
241 self.module.bundled_size = bundled_size;
242 self
243 }
244
245 pub fn symbol_table(mut self, symbol_table: SymbolTable) -> Self {
246 self.module.symbol_table = Arc::new(symbol_table);
247 self
248 }
249
250 pub fn module_format(mut self, module_format: ModuleFormat) -> Self {
251 self.module.module_format = module_format;
252 self
253 }
254
255 pub fn exports_kind(mut self, exports_kind: ExportsKind) -> Self {
256 self.module.exports_kind = exports_kind;
257 self
258 }
259
260 pub fn has_star_exports(mut self, has_star_exports: bool) -> Self {
261 self.module.has_star_exports = has_star_exports;
262 self
263 }
264
265 pub fn execution_order(mut self, execution_order: Option<u32>) -> Self {
266 self.module.execution_order = execution_order;
267 self
268 }
269
270 pub fn build(self) -> Module {
271 self.module
272 }
273}
274
275#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
277pub enum ModuleFormat {
278 EsmMjs,
280 EsmPackageJson,
282 Esm,
284 CjsPackageJson,
286 Cjs,
288 Unknown,
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
294pub enum ExportsKind {
295 Esm,
297 CommonJs,
299 None,
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
305pub enum SourceType {
306 JavaScript,
307 TypeScript,
308 Jsx,
309 Tsx,
310 Json,
311 Css,
312 Unknown,
313}
314
315impl SourceType {
316 pub fn from_extension(ext: &str) -> Self {
318 match ext {
319 "js" | "mjs" | "cjs" => Self::JavaScript,
320 "ts" | "mts" | "cts" => Self::TypeScript,
321 "jsx" => Self::Jsx,
322 "tsx" => Self::Tsx,
323 "json" => Self::Json,
324 "css" => Self::Css,
325 _ => Self::Unknown,
326 }
327 }
328
329 pub fn from_path(path: &Path) -> Self {
331 path.extension()
332 .and_then(|ext| ext.to_str())
333 .map_or(Self::Unknown, Self::from_extension)
334 }
335
336 pub fn is_javascript_like(&self) -> bool {
338 matches!(
339 self,
340 Self::JavaScript | Self::TypeScript | Self::Jsx | Self::Tsx
341 )
342 }
343}