Skip to main content

fallow_extract/cache/
conversion.rs

1//! Conversion between [`ModuleInfo`](crate::ModuleInfo) and [`CachedModule`].
2
3use oxc_span::Span;
4
5use crate::ExportName;
6
7use super::types::{
8    CachedDynamicImport, CachedDynamicImportPattern, CachedExport, CachedImport, CachedMember,
9    CachedModule, CachedReExport, CachedRequireCall, CachedSuppression, IMPORT_KIND_DEFAULT,
10    IMPORT_KIND_NAMED, IMPORT_KIND_NAMESPACE, IMPORT_KIND_SIDE_EFFECT,
11};
12
13/// Reconstruct a [`ModuleInfo`](crate::ModuleInfo) from a [`CachedModule`].
14#[must_use]
15pub fn cached_to_module(
16    cached: &CachedModule,
17    file_id: fallow_types::discover::FileId,
18) -> crate::ModuleInfo {
19    use crate::{
20        DynamicImportInfo, ExportInfo, ImportInfo, ImportedName, MemberInfo, ModuleInfo,
21        ReExportInfo, RequireCallInfo,
22    };
23
24    let exports = cached
25        .exports
26        .iter()
27        .map(|e| ExportInfo {
28            name: if e.is_default {
29                ExportName::Default
30            } else {
31                ExportName::Named(e.name.clone())
32            },
33            local_name: e.local_name.clone(),
34            is_type_only: e.is_type_only,
35            is_public: e.is_public,
36            span: Span::new(e.span_start, e.span_end),
37            members: e
38                .members
39                .iter()
40                .map(|m| MemberInfo {
41                    name: m.name.clone(),
42                    kind: m.kind.clone(),
43                    span: Span::new(m.span_start, m.span_end),
44                    has_decorator: m.has_decorator,
45                })
46                .collect(),
47        })
48        .collect();
49
50    let imports = cached
51        .imports
52        .iter()
53        .map(|i| ImportInfo {
54            source: i.source.clone(),
55            imported_name: match i.kind {
56                IMPORT_KIND_DEFAULT => ImportedName::Default,
57                IMPORT_KIND_NAMESPACE => ImportedName::Namespace,
58                IMPORT_KIND_SIDE_EFFECT => ImportedName::SideEffect,
59                // IMPORT_KIND_NAMED (0) and any unknown value default to Named
60                _ => ImportedName::Named(i.imported_name.clone()),
61            },
62            local_name: i.local_name.clone(),
63            is_type_only: i.is_type_only,
64            span: Span::new(i.span_start, i.span_end),
65            source_span: Span::new(i.source_span_start, i.source_span_end),
66        })
67        .collect();
68
69    let re_exports = cached
70        .re_exports
71        .iter()
72        .map(|r| ReExportInfo {
73            source: r.source.clone(),
74            imported_name: r.imported_name.clone(),
75            exported_name: r.exported_name.clone(),
76            is_type_only: r.is_type_only,
77        })
78        .collect();
79
80    let dynamic_imports = cached
81        .dynamic_imports
82        .iter()
83        .map(|d| DynamicImportInfo {
84            source: d.source.clone(),
85            span: Span::new(d.span_start, d.span_end),
86            destructured_names: d.destructured_names.clone(),
87            local_name: d.local_name.clone(),
88        })
89        .collect();
90
91    let require_calls = cached
92        .require_calls
93        .iter()
94        .map(|r| RequireCallInfo {
95            source: r.source.clone(),
96            span: Span::new(r.span_start, r.span_end),
97            destructured_names: r.destructured_names.clone(),
98            local_name: r.local_name.clone(),
99        })
100        .collect();
101
102    let dynamic_import_patterns = cached
103        .dynamic_import_patterns
104        .iter()
105        .map(|p| crate::DynamicImportPattern {
106            prefix: p.prefix.clone(),
107            suffix: p.suffix.clone(),
108            span: Span::new(p.span_start, p.span_end),
109        })
110        .collect();
111
112    let suppressions = cached
113        .suppressions
114        .iter()
115        .map(|s| crate::suppress::Suppression {
116            line: s.line,
117            kind: if s.kind == 0 {
118                None
119            } else {
120                crate::suppress::IssueKind::from_discriminant(s.kind)
121            },
122        })
123        .collect();
124
125    ModuleInfo {
126        file_id,
127        exports,
128        imports,
129        re_exports,
130        dynamic_imports,
131        dynamic_import_patterns,
132        require_calls,
133        member_accesses: cached.member_accesses.clone(),
134        whole_object_uses: cached.whole_object_uses.clone(),
135        has_cjs_exports: cached.has_cjs_exports,
136        content_hash: cached.content_hash,
137        suppressions,
138        unused_import_bindings: cached.unused_import_bindings.clone(),
139        line_offsets: cached.line_offsets.clone(),
140        complexity: cached.complexity.clone(),
141    }
142}
143
144/// Convert a [`ModuleInfo`](crate::ModuleInfo) to a [`CachedModule`] for storage.
145///
146/// `mtime_secs` and `file_size` come from `std::fs::metadata()` at parse time
147/// and enable fast cache validation on subsequent runs (skip file read when
148/// mtime+size match).
149#[must_use]
150pub fn module_to_cached(
151    module: &crate::ModuleInfo,
152    mtime_secs: u64,
153    file_size: u64,
154) -> CachedModule {
155    CachedModule {
156        content_hash: module.content_hash,
157        mtime_secs,
158        file_size,
159        exports: module
160            .exports
161            .iter()
162            .map(|e| CachedExport {
163                name: match &e.name {
164                    ExportName::Named(n) => n.clone(),
165                    ExportName::Default => String::new(),
166                },
167                is_default: matches!(e.name, ExportName::Default),
168                is_type_only: e.is_type_only,
169                is_public: e.is_public,
170                local_name: e.local_name.clone(),
171                span_start: e.span.start,
172                span_end: e.span.end,
173                members: e
174                    .members
175                    .iter()
176                    .map(|m| CachedMember {
177                        name: m.name.clone(),
178                        kind: m.kind.clone(),
179                        span_start: m.span.start,
180                        span_end: m.span.end,
181                        has_decorator: m.has_decorator,
182                    })
183                    .collect(),
184            })
185            .collect(),
186        imports: module
187            .imports
188            .iter()
189            .map(|i| {
190                let (kind, imported_name) = match &i.imported_name {
191                    crate::ImportedName::Named(n) => (IMPORT_KIND_NAMED, n.clone()),
192                    crate::ImportedName::Default => (IMPORT_KIND_DEFAULT, String::new()),
193                    crate::ImportedName::Namespace => (IMPORT_KIND_NAMESPACE, String::new()),
194                    crate::ImportedName::SideEffect => (IMPORT_KIND_SIDE_EFFECT, String::new()),
195                };
196                CachedImport {
197                    source: i.source.clone(),
198                    imported_name,
199                    local_name: i.local_name.clone(),
200                    is_type_only: i.is_type_only,
201                    kind,
202                    span_start: i.span.start,
203                    span_end: i.span.end,
204                    source_span_start: i.source_span.start,
205                    source_span_end: i.source_span.end,
206                }
207            })
208            .collect(),
209        re_exports: module
210            .re_exports
211            .iter()
212            .map(|r| CachedReExport {
213                source: r.source.clone(),
214                imported_name: r.imported_name.clone(),
215                exported_name: r.exported_name.clone(),
216                is_type_only: r.is_type_only,
217            })
218            .collect(),
219        dynamic_imports: module
220            .dynamic_imports
221            .iter()
222            .map(|d| CachedDynamicImport {
223                source: d.source.clone(),
224                span_start: d.span.start,
225                span_end: d.span.end,
226                destructured_names: d.destructured_names.clone(),
227                local_name: d.local_name.clone(),
228            })
229            .collect(),
230        require_calls: module
231            .require_calls
232            .iter()
233            .map(|r| CachedRequireCall {
234                source: r.source.clone(),
235                span_start: r.span.start,
236                span_end: r.span.end,
237                destructured_names: r.destructured_names.clone(),
238                local_name: r.local_name.clone(),
239            })
240            .collect(),
241        member_accesses: module.member_accesses.clone(),
242        whole_object_uses: module.whole_object_uses.clone(),
243        dynamic_import_patterns: module
244            .dynamic_import_patterns
245            .iter()
246            .map(|p| CachedDynamicImportPattern {
247                prefix: p.prefix.clone(),
248                suffix: p.suffix.clone(),
249                span_start: p.span.start,
250                span_end: p.span.end,
251            })
252            .collect(),
253        has_cjs_exports: module.has_cjs_exports,
254        unused_import_bindings: module.unused_import_bindings.clone(),
255        suppressions: module
256            .suppressions
257            .iter()
258            .map(|s| CachedSuppression {
259                line: s.line,
260                kind: s
261                    .kind
262                    .map_or(0, crate::suppress::IssueKind::to_discriminant),
263            })
264            .collect(),
265        line_offsets: module.line_offsets.clone(),
266        complexity: module.complexity.clone(),
267    }
268}