1use std::time::{SystemTime, UNIX_EPOCH};
11
12use oxc_span::Span;
13
14use crate::ExportName;
15use fallow_types::extract::{NamespaceObjectAlias, VisibilityTag};
16
17#[must_use]
23pub fn current_unix_seconds() -> u64 {
24 SystemTime::now()
25 .duration_since(UNIX_EPOCH)
26 .map_or(0, |d| d.as_secs())
27}
28
29use super::types::{
30 CachedDynamicImport, CachedDynamicImportPattern, CachedExport, CachedImport,
31 CachedLocalTypeDeclaration, CachedMember, CachedModule, CachedNamespaceObjectAlias,
32 CachedPublicSignatureTypeReference, CachedReExport, CachedRequireCall, CachedSuppression,
33 CachedUnknownSuppressionKind, IMPORT_KIND_DEFAULT, IMPORT_KIND_NAMED, IMPORT_KIND_NAMESPACE,
34 IMPORT_KIND_SIDE_EFFECT,
35};
36
37#[must_use]
39pub fn cached_to_module(
40 cached: &CachedModule,
41 file_id: fallow_types::discover::FileId,
42) -> crate::ModuleInfo {
43 cached_to_module_opts(cached, file_id, true)
44}
45
46#[must_use]
51#[expect(
52 clippy::too_many_lines,
53 reason = "single flat field-by-field deserialization; splitting it harms readability"
54)]
55pub fn cached_to_module_opts(
56 cached: &CachedModule,
57 file_id: fallow_types::discover::FileId,
58 need_complexity: bool,
59) -> crate::ModuleInfo {
60 use crate::{
61 DynamicImportInfo, ExportInfo, ImportInfo, ImportedName, LocalTypeDeclaration, MemberInfo,
62 ModuleInfo, PublicSignatureTypeReference, ReExportInfo, RequireCallInfo,
63 };
64
65 let exports = cached
66 .exports
67 .iter()
68 .map(|e| ExportInfo {
69 name: if e.is_default {
70 ExportName::Default
71 } else {
72 ExportName::Named(e.name.clone())
73 },
74 local_name: e.local_name.clone(),
75 is_type_only: e.is_type_only,
76 is_side_effect_used: e.is_side_effect_used,
77 visibility: match e.visibility {
78 1 => VisibilityTag::Public,
79 2 => VisibilityTag::Internal,
80 3 => VisibilityTag::Beta,
81 4 => VisibilityTag::Alpha,
82 5 => VisibilityTag::ExpectedUnused,
83 _ => VisibilityTag::None,
84 },
85 span: Span::new(e.span_start, e.span_end),
86 members: e
87 .members
88 .iter()
89 .map(|m| MemberInfo {
90 name: m.name.clone(),
91 kind: m.kind,
92 span: Span::new(m.span_start, m.span_end),
93 has_decorator: m.has_decorator,
94 decorator_names: m.decorator_names.clone(),
95 is_instance_returning_static: m.is_instance_returning_static,
96 is_self_returning: m.is_self_returning,
97 })
98 .collect(),
99 super_class: e.super_class.clone(),
100 })
101 .collect();
102
103 let imports = cached
104 .imports
105 .iter()
106 .map(|i| ImportInfo {
107 source: i.source.clone(),
108 imported_name: match i.kind {
109 IMPORT_KIND_DEFAULT => ImportedName::Default,
110 IMPORT_KIND_NAMESPACE => ImportedName::Namespace,
111 IMPORT_KIND_SIDE_EFFECT => ImportedName::SideEffect,
112 _ => ImportedName::Named(i.imported_name.clone()),
114 },
115 local_name: i.local_name.clone(),
116 is_type_only: i.is_type_only,
117 from_style: i.from_style,
118 span: Span::new(i.span_start, i.span_end),
119 source_span: Span::new(i.source_span_start, i.source_span_end),
120 })
121 .collect();
122
123 let re_exports = cached
124 .re_exports
125 .iter()
126 .map(|r| ReExportInfo {
127 source: r.source.clone(),
128 imported_name: r.imported_name.clone(),
129 exported_name: r.exported_name.clone(),
130 is_type_only: r.is_type_only,
131 span: Span::new(r.span_start, r.span_end),
132 })
133 .collect();
134
135 let dynamic_imports = cached
136 .dynamic_imports
137 .iter()
138 .map(|d| DynamicImportInfo {
139 source: d.source.clone(),
140 span: Span::new(d.span_start, d.span_end),
141 destructured_names: d.destructured_names.clone(),
142 local_name: d.local_name.clone(),
143 is_speculative: d.is_speculative,
144 })
145 .collect();
146
147 let require_calls = cached
148 .require_calls
149 .iter()
150 .map(|r| RequireCallInfo {
151 source: r.source.clone(),
152 span: Span::new(r.span_start, r.span_end),
153 source_span: Span::new(r.source_span_start, r.source_span_end),
154 destructured_names: r.destructured_names.clone(),
155 local_name: r.local_name.clone(),
156 })
157 .collect();
158
159 let dynamic_import_patterns = cached
160 .dynamic_import_patterns
161 .iter()
162 .map(|p| crate::DynamicImportPattern {
163 prefix: p.prefix.clone(),
164 suffix: p.suffix.clone(),
165 span: Span::new(p.span_start, p.span_end),
166 })
167 .collect();
168
169 let suppressions = cached
170 .suppressions
171 .iter()
172 .map(|s| crate::suppress::Suppression {
173 line: s.line,
174 comment_line: s.comment_line,
175 kind: if s.kind == 0 {
176 None
177 } else {
178 crate::suppress::IssueKind::from_discriminant(s.kind)
179 },
180 })
181 .collect();
182
183 let unknown_suppression_kinds = cached
184 .unknown_suppression_kinds
185 .iter()
186 .map(|u| fallow_types::suppress::UnknownSuppressionKind {
187 comment_line: u.comment_line,
188 is_file_level: u.is_file_level,
189 token: u.token.clone(),
190 })
191 .collect();
192
193 ModuleInfo {
194 file_id,
195 exports,
196 imports,
197 re_exports,
198 dynamic_imports,
199 dynamic_import_patterns,
200 require_calls,
201 package_path_references: cached.package_path_references.clone(),
202 member_accesses: cached.member_accesses.clone(),
203 whole_object_uses: cached.whole_object_uses.clone(),
204 has_cjs_exports: cached.has_cjs_exports,
205 has_angular_component_template_url: cached.has_angular_component_template_url,
206 content_hash: cached.content_hash,
207 suppressions,
208 unknown_suppression_kinds,
209 unused_import_bindings: cached.unused_import_bindings.clone(),
210 type_referenced_import_bindings: cached.type_referenced_import_bindings.clone(),
211 value_referenced_import_bindings: cached.value_referenced_import_bindings.clone(),
212 line_offsets: cached.line_offsets.clone(),
213 complexity: if need_complexity {
214 cached.complexity.clone()
215 } else {
216 Vec::new()
217 },
218 flag_uses: cached.flag_uses.clone(),
219 class_heritage: cached.class_heritage.clone(),
220 injection_tokens: cached.injection_tokens.clone(),
221 local_type_declarations: cached
222 .local_type_declarations
223 .iter()
224 .map(|decl| LocalTypeDeclaration {
225 name: decl.name.clone(),
226 span: Span::new(decl.span_start, decl.span_end),
227 })
228 .collect(),
229 public_signature_type_references: cached
230 .public_signature_type_references
231 .iter()
232 .map(|reference| PublicSignatureTypeReference {
233 export_name: reference.export_name.clone(),
234 type_name: reference.type_name.clone(),
235 span: Span::new(reference.span_start, reference.span_end),
236 })
237 .collect(),
238 namespace_object_aliases: cached
239 .namespace_object_aliases
240 .iter()
241 .map(|alias| NamespaceObjectAlias {
242 via_export_name: alias.via_export_name.clone(),
243 suffix: alias.suffix.clone(),
244 namespace_local: alias.namespace_local.clone(),
245 })
246 .collect(),
247 iconify_prefixes: cached.iconify_prefixes.clone(),
248 iconify_icon_names: cached.iconify_icon_names.clone(),
249 auto_import_candidates: cached.auto_import_candidates.clone(),
250 directives: cached.directives.clone(),
251 security_sinks: cached.security_sinks.clone(),
252 security_sinks_skipped: cached.security_sinks_skipped,
253 security_unresolved_callee_sites: cached.security_unresolved_callee_sites.clone(),
254 tainted_bindings: cached.tainted_bindings.clone(),
255 sanitized_sink_args: cached.sanitized_sink_args.clone(),
256 security_control_sites: cached.security_control_sites.clone(),
257 callee_uses: cached.callee_uses.clone(),
258 }
259}
260
261#[must_use]
267#[expect(
268 clippy::too_many_lines,
269 reason = "single flat field-by-field serialization; splitting it harms readability"
270)]
271pub fn module_to_cached(
272 module: &crate::ModuleInfo,
273 mtime_secs: u64,
274 file_size: u64,
275) -> CachedModule {
276 CachedModule {
277 content_hash: module.content_hash,
278 mtime_secs,
279 file_size,
280 last_access_secs: current_unix_seconds(),
281 exports: module
282 .exports
283 .iter()
284 .map(|e| CachedExport {
285 name: match &e.name {
286 ExportName::Named(n) => n.clone(),
287 ExportName::Default => String::new(),
288 },
289 is_default: matches!(e.name, ExportName::Default),
290 is_type_only: e.is_type_only,
291 is_side_effect_used: e.is_side_effect_used,
292 visibility: e.visibility as u8,
293 local_name: e.local_name.clone(),
294 span_start: e.span.start,
295 span_end: e.span.end,
296 members: e
297 .members
298 .iter()
299 .map(|m| CachedMember {
300 name: m.name.clone(),
301 kind: m.kind,
302 span_start: m.span.start,
303 span_end: m.span.end,
304 has_decorator: m.has_decorator,
305 decorator_names: m.decorator_names.clone(),
306 is_instance_returning_static: m.is_instance_returning_static,
307 is_self_returning: m.is_self_returning,
308 })
309 .collect(),
310 super_class: e.super_class.clone(),
311 })
312 .collect(),
313 imports: module
314 .imports
315 .iter()
316 .map(|i| {
317 let (kind, imported_name) = match &i.imported_name {
318 crate::ImportedName::Named(n) => (IMPORT_KIND_NAMED, n.clone()),
319 crate::ImportedName::Default => (IMPORT_KIND_DEFAULT, String::new()),
320 crate::ImportedName::Namespace => (IMPORT_KIND_NAMESPACE, String::new()),
321 crate::ImportedName::SideEffect => (IMPORT_KIND_SIDE_EFFECT, String::new()),
322 };
323 CachedImport {
324 source: i.source.clone(),
325 imported_name,
326 local_name: i.local_name.clone(),
327 is_type_only: i.is_type_only,
328 from_style: i.from_style,
329 kind,
330 span_start: i.span.start,
331 span_end: i.span.end,
332 source_span_start: i.source_span.start,
333 source_span_end: i.source_span.end,
334 }
335 })
336 .collect(),
337 re_exports: module
338 .re_exports
339 .iter()
340 .map(|r| CachedReExport {
341 source: r.source.clone(),
342 imported_name: r.imported_name.clone(),
343 exported_name: r.exported_name.clone(),
344 is_type_only: r.is_type_only,
345 span_start: r.span.start,
346 span_end: r.span.end,
347 })
348 .collect(),
349 dynamic_imports: module
350 .dynamic_imports
351 .iter()
352 .map(|d| CachedDynamicImport {
353 source: d.source.clone(),
354 span_start: d.span.start,
355 span_end: d.span.end,
356 destructured_names: d.destructured_names.clone(),
357 local_name: d.local_name.clone(),
358 is_speculative: d.is_speculative,
359 })
360 .collect(),
361 require_calls: module
362 .require_calls
363 .iter()
364 .map(|r| CachedRequireCall {
365 source: r.source.clone(),
366 span_start: r.span.start,
367 span_end: r.span.end,
368 source_span_start: r.source_span.start,
369 source_span_end: r.source_span.end,
370 destructured_names: r.destructured_names.clone(),
371 local_name: r.local_name.clone(),
372 })
373 .collect(),
374 package_path_references: module.package_path_references.clone(),
375 member_accesses: module.member_accesses.clone(),
376 whole_object_uses: module.whole_object_uses.clone(),
377 dynamic_import_patterns: module
378 .dynamic_import_patterns
379 .iter()
380 .map(|p| CachedDynamicImportPattern {
381 prefix: p.prefix.clone(),
382 suffix: p.suffix.clone(),
383 span_start: p.span.start,
384 span_end: p.span.end,
385 })
386 .collect(),
387 has_cjs_exports: module.has_cjs_exports,
388 has_angular_component_template_url: module.has_angular_component_template_url,
389 unused_import_bindings: module.unused_import_bindings.clone(),
390 type_referenced_import_bindings: module.type_referenced_import_bindings.clone(),
391 value_referenced_import_bindings: module.value_referenced_import_bindings.clone(),
392 suppressions: module
393 .suppressions
394 .iter()
395 .map(|s| CachedSuppression {
396 line: s.line,
397 comment_line: s.comment_line,
398 kind: s
399 .kind
400 .map_or(0, crate::suppress::IssueKind::to_discriminant),
401 })
402 .collect(),
403 unknown_suppression_kinds: module
404 .unknown_suppression_kinds
405 .iter()
406 .map(|u| CachedUnknownSuppressionKind {
407 comment_line: u.comment_line,
408 is_file_level: u.is_file_level,
409 token: u.token.clone(),
410 })
411 .collect(),
412 line_offsets: module.line_offsets.clone(),
413 complexity: module.complexity.clone(),
414 flag_uses: module.flag_uses.clone(),
415 class_heritage: module.class_heritage.clone(),
416 injection_tokens: module.injection_tokens.clone(),
417 local_type_declarations: module
418 .local_type_declarations
419 .iter()
420 .map(|decl| CachedLocalTypeDeclaration {
421 name: decl.name.clone(),
422 span_start: decl.span.start,
423 span_end: decl.span.end,
424 })
425 .collect(),
426 public_signature_type_references: module
427 .public_signature_type_references
428 .iter()
429 .map(|reference| CachedPublicSignatureTypeReference {
430 export_name: reference.export_name.clone(),
431 type_name: reference.type_name.clone(),
432 span_start: reference.span.start,
433 span_end: reference.span.end,
434 })
435 .collect(),
436 namespace_object_aliases: module
437 .namespace_object_aliases
438 .iter()
439 .map(|alias| CachedNamespaceObjectAlias {
440 via_export_name: alias.via_export_name.clone(),
441 suffix: alias.suffix.clone(),
442 namespace_local: alias.namespace_local.clone(),
443 })
444 .collect(),
445 iconify_prefixes: module.iconify_prefixes.clone(),
446 iconify_icon_names: module.iconify_icon_names.clone(),
447 auto_import_candidates: module.auto_import_candidates.clone(),
448 directives: module.directives.clone(),
449 security_sinks: module.security_sinks.clone(),
450 security_sinks_skipped: module.security_sinks_skipped,
451 security_unresolved_callee_sites: module.security_unresolved_callee_sites.clone(),
452 tainted_bindings: module.tainted_bindings.clone(),
453 sanitized_sink_args: module.sanitized_sink_args.clone(),
454 security_control_sites: module.security_control_sites.clone(),
455 callee_uses: module.callee_uses.clone(),
456 }
457}