fob_graph/memory/
construction.rs1use rustc_hash::FxHashMap as HashMap;
4
5use super::super::external_dep::ExternalDependency;
6use super::super::{Module, ModuleId};
7use super::graph::{GraphInner, ModuleGraph};
8use crate::{Error, Result};
9
10impl ModuleGraph {
11 pub fn new() -> Result<Self> {
13 Ok(Self {
14 inner: std::sync::Arc::new(parking_lot::RwLock::new(GraphInner::default())),
15 })
16 }
17
18 pub fn from_modules<I>(modules: I) -> Result<Self>
20 where
21 I: IntoIterator<Item = Module>,
22 {
23 let graph = Self::new()?;
24 for module in modules {
25 graph.add_module(module)?;
26 }
27 Ok(graph)
28 }
29
30 pub fn from_collected_data(
32 collection: super::super::collection::CollectionState,
33 ) -> Result<Self> {
34 use super::super::from_collection::{
35 PendingImport, convert_collected_exports, convert_collected_imports,
36 convert_collected_module_id, has_star_export, infer_exports_kind,
37 };
38 use super::super::module::ModuleFormat as FobModuleFormat;
39 use super::super::semantic::analyze_symbols;
40
41 let graph = Self::new()?;
42
43 let mut path_to_id: std::collections::HashMap<String, ModuleId> =
44 std::collections::HashMap::new();
45 let mut pending_modules: Vec<(ModuleId, Module, Vec<PendingImport>)> = Vec::new();
46
47 for (path, collected) in &collection.modules {
49 if collected.is_external {
50 continue;
51 }
52
53 let module_id = convert_collected_module_id(path)
54 .map_err(|e| Error::InvalidConfig(format!("Module ID conversion failed: {}", e)))?;
55 path_to_id.insert(path.clone(), module_id);
56 }
57
58 for (path, collected) in &collection.modules {
60 if collected.is_external {
61 continue;
62 }
63
64 let module_id = path_to_id.get(path).ok_or_else(|| {
65 Error::InvalidConfig(format!("Module ID not found for path: {}", path))
66 })?;
67
68 let exports = convert_collected_exports(collected, module_id);
69 let imports = convert_collected_imports(collected, module_id, &path_to_id);
70
71 let has_side_effects = collected.has_side_effects;
72
73 let source_type = super::super::SourceType::from_path(module_id.as_path());
75 let code = collected.code.as_deref().unwrap_or("");
76 let mut symbol_table = analyze_symbols(
77 code,
78 module_id.as_path().to_str().unwrap_or("unknown"),
79 source_type,
80 )
81 .unwrap_or_default();
82
83 let export_names: Vec<String> = exports.iter().map(|e| e.name.clone()).collect();
85 symbol_table.mark_exports(&export_names);
86
87 let mut builder = Module::builder(
88 module_id.clone(),
89 module_id.as_path().to_path_buf(),
90 source_type,
91 )
92 .exports(exports)
93 .side_effects(has_side_effects)
94 .original_size(code.len())
95 .bundled_size(None)
96 .external(false)
97 .symbol_table(symbol_table)
98 .module_format(FobModuleFormat::Unknown)
99 .exports_kind(infer_exports_kind(&collected.exports))
100 .has_star_exports(has_star_export(&collected.exports))
101 .execution_order(None);
102
103 if collected.is_entry {
104 builder = builder.entry(true);
105 }
106
107 let module = builder.build();
108 pending_modules.push((module_id.clone(), module, imports));
109 }
110
111 let mut external_aggregate: HashMap<String, ExternalDependency> = HashMap::default();
112
113 for (module_id, mut module, pending_imports) in pending_modules {
115 let mut resolved_imports = Vec::with_capacity(pending_imports.len());
116 for mut pending_import in pending_imports {
117 if let Some(target_path) = pending_import.target {
118 if let Some(target_id) = path_to_id.get(&target_path) {
119 pending_import.import.resolved_to = Some(target_id.clone());
120 graph.add_dependency(module_id.clone(), target_id.clone())?;
121 } else {
122 let dep = external_aggregate
124 .entry(target_path.clone())
125 .or_insert_with(|| ExternalDependency::new(target_path.clone()));
126 dep.push_importer(module_id.clone());
127 }
128 }
129 resolved_imports.push(pending_import.import);
130 }
131
132 module.imports = std::sync::Arc::new(resolved_imports);
134 graph.add_module(module)?;
135 }
136
137 for dep in external_aggregate.into_values() {
138 graph.add_external_dependency(dep)?;
139 }
140
141 Ok(graph)
142 }
143}