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> {
156 Arc::make_mut(&mut self.exports)
157 }
158
159 pub fn imports_from(&self, target: &ModuleId) -> Vec<&Import> {
176 self.imports
177 .iter()
178 .filter(|imp| imp.resolved_to.as_ref() == Some(target))
179 .collect()
180 }
181
182 pub fn has_import_from(&self, source: &str) -> bool {
199 self.imports.iter().any(|imp| imp.source == source)
200 }
201
202 pub fn import_sources(&self) -> Vec<&str> {
217 self.imports.iter().map(|imp| imp.source.as_str()).collect()
218 }
219}
220
221pub struct ModuleBuilder {
223 module: Module,
224}
225
226impl ModuleBuilder {
227 pub fn imports(mut self, imports: Vec<Import>) -> Self {
228 self.module.imports = Arc::new(imports);
229 self
230 }
231
232 pub fn exports(mut self, exports: Vec<Export>) -> Self {
233 self.module.exports = Arc::new(exports);
234 self
235 }
236
237 pub fn side_effects(mut self, has_side_effects: bool) -> Self {
238 self.module.has_side_effects = has_side_effects;
239 self
240 }
241
242 pub fn entry(mut self, is_entry: bool) -> Self {
243 self.module.is_entry = is_entry;
244 self
245 }
246
247 pub fn external(mut self, is_external: bool) -> Self {
248 self.module.is_external = is_external;
249 self
250 }
251
252 pub fn original_size(mut self, original_size: usize) -> Self {
253 self.module.original_size = original_size;
254 self
255 }
256
257 pub fn bundled_size(mut self, bundled_size: Option<usize>) -> Self {
258 self.module.bundled_size = bundled_size;
259 self
260 }
261
262 pub fn symbol_table(mut self, symbol_table: SymbolTable) -> Self {
263 self.module.symbol_table = Arc::new(symbol_table);
264 self
265 }
266
267 pub fn module_format(mut self, module_format: ModuleFormat) -> Self {
268 self.module.module_format = module_format;
269 self
270 }
271
272 pub fn exports_kind(mut self, exports_kind: ExportsKind) -> Self {
273 self.module.exports_kind = exports_kind;
274 self
275 }
276
277 pub fn has_star_exports(mut self, has_star_exports: bool) -> Self {
278 self.module.has_star_exports = has_star_exports;
279 self
280 }
281
282 pub fn execution_order(mut self, execution_order: Option<u32>) -> Self {
283 self.module.execution_order = execution_order;
284 self
285 }
286
287 pub fn build(self) -> Module {
288 self.module
289 }
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
294pub enum ModuleFormat {
295 EsmMjs,
297 EsmPackageJson,
299 Esm,
301 CjsPackageJson,
303 Cjs,
305 Unknown,
307}
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
311pub enum ExportsKind {
312 Esm,
314 CommonJs,
316 None,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
322pub enum SourceType {
323 JavaScript,
324 TypeScript,
325 Jsx,
326 Tsx,
327 Json,
328 Css,
329 Unknown,
330}
331
332impl SourceType {
333 pub fn from_extension(ext: &str) -> Self {
335 match ext {
336 "js" | "mjs" | "cjs" => Self::JavaScript,
337 "ts" | "mts" | "cts" => Self::TypeScript,
338 "jsx" => Self::Jsx,
339 "tsx" => Self::Tsx,
340 "json" => Self::Json,
341 "css" => Self::Css,
342 _ => Self::Unknown,
343 }
344 }
345
346 pub fn from_path(path: &Path) -> Self {
348 path.extension()
349 .and_then(|ext| ext.to_str())
350 .map_or(Self::Unknown, Self::from_extension)
351 }
352
353 pub fn is_javascript_like(&self) -> bool {
355 matches!(
356 self,
357 Self::JavaScript | Self::TypeScript | Self::Jsx | Self::Tsx
358 )
359 }
360}