fob_graph/memory/
imports.rs

1//! Import analysis methods for ModuleGraph.
2
3use super::graph::ModuleGraph;
4use super::types::{NamespaceImportInfo, SideEffectImport, TypeOnlyImport};
5use crate::Result;
6
7impl ModuleGraph {
8    /// Get all side-effect-only imports across the graph.
9    ///
10    /// Side-effect imports like `import 'polyfill'` execute code but don't bind any values.
11    /// These are important to track as they can't be tree-shaken and always contribute
12    /// to bundle size.
13    pub fn side_effect_only_imports(&self) -> Result<Vec<SideEffectImport>> {
14        let inner = self.inner.read();
15        let mut side_effects = Vec::new();
16
17        for module in inner.modules.values() {
18            for import in module.imports.iter() {
19                if import.is_side_effect_only() {
20                    side_effects.push(SideEffectImport {
21                        importer: module.id.clone(),
22                        source: import.source.clone(),
23                        resolved_to: import.resolved_to.clone(),
24                        span: import.span.clone(),
25                    });
26                }
27            }
28        }
29
30        Ok(side_effects)
31    }
32
33    /// Get all namespace imports and their usage.
34    ///
35    /// Namespace imports (`import * as foo`) import all exports from a module.
36    /// This is useful for tracking potential over-imports that could be optimized
37    /// to named imports.
38    pub fn namespace_imports(&self) -> Result<Vec<NamespaceImportInfo>> {
39        let inner = self.inner.read();
40        let mut namespaces = Vec::new();
41
42        for module in inner.modules.values() {
43            for import in module.imports.iter() {
44                if import.is_namespace_import() {
45                    if let Some(namespace_name) = import.namespace_name() {
46                        namespaces.push(NamespaceImportInfo {
47                            importer: module.id.clone(),
48                            namespace_name: namespace_name.to_string(),
49                            source: import.source.clone(),
50                            resolved_to: import.resolved_to.clone(),
51                        });
52                    }
53                }
54            }
55        }
56
57        Ok(namespaces)
58    }
59
60    /// Get all type-only imports (TypeScript).
61    ///
62    /// Type-only imports are erased at runtime and don't contribute to bundle size.
63    /// Tracking these helps understand the TypeScript structure without conflating
64    /// with runtime dependencies.
65    pub fn type_only_imports(&self) -> Result<Vec<TypeOnlyImport>> {
66        let inner = self.inner.read();
67        let mut type_imports = Vec::new();
68
69        for module in inner.modules.values() {
70            for import in module.imports.iter() {
71                if import.is_type_only() {
72                    type_imports.push(TypeOnlyImport {
73                        importer: module.id.clone(),
74                        source: import.source.clone(),
75                        specifiers: import.specifiers.clone(),
76                        span: import.span.clone(),
77                    });
78                }
79            }
80        }
81
82        Ok(type_imports)
83    }
84}