1use std::time::{SystemTime, UNIX_EPOCH};
11
12use oxc_span::Span;
13
14use crate::ExportName;
15use fallow_types::extract::{NamespaceObjectAlias, VisibilityTag};
16use fallow_types::suppress::{PolicyRuleSuppression, SuppressionTarget};
17
18#[must_use]
24pub fn current_unix_seconds() -> u64 {
25 SystemTime::now()
26 .duration_since(UNIX_EPOCH)
27 .map_or(0, |d| d.as_secs())
28}
29
30use super::types::{
31 CachedDynamicImport, CachedDynamicImportPattern, CachedExport, CachedImport,
32 CachedLocalTypeDeclaration, CachedMember, CachedModule, CachedNamespaceObjectAlias,
33 CachedPublicSignatureTypeReference, CachedReExport, CachedRequireCall, CachedSuppression,
34 CachedUnknownSuppressionKind, IMPORT_KIND_DEFAULT, IMPORT_KIND_NAMED, IMPORT_KIND_NAMESPACE,
35 IMPORT_KIND_SIDE_EFFECT,
36};
37
38#[must_use]
40pub fn cached_to_module(
41 cached: &CachedModule,
42 file_id: fallow_types::discover::FileId,
43) -> crate::ModuleInfo {
44 cached_to_module_opts(cached, file_id, true)
45}
46
47fn cached_exports_to_module(exports: &[CachedExport]) -> Vec<crate::ExportInfo> {
48 exports
49 .iter()
50 .map(|export| crate::ExportInfo {
51 name: if export.is_default {
52 ExportName::Default
53 } else {
54 ExportName::Named(export.name.clone())
55 },
56 local_name: export.local_name.clone(),
57 is_type_only: export.is_type_only,
58 is_side_effect_used: export.is_side_effect_used,
59 visibility: match export.visibility {
60 1 => VisibilityTag::Public,
61 2 => VisibilityTag::Internal,
62 3 => VisibilityTag::Beta,
63 4 => VisibilityTag::Alpha,
64 5 => VisibilityTag::ExpectedUnused,
65 _ => VisibilityTag::None,
66 },
67 span: Span::new(export.span_start, export.span_end),
68 members: export
69 .members
70 .iter()
71 .map(|member| crate::MemberInfo {
72 name: member.name.clone(),
73 kind: member.kind,
74 span: Span::new(member.span_start, member.span_end),
75 has_decorator: member.has_decorator,
76 decorator_names: member.decorator_names.clone(),
77 is_instance_returning_static: member.is_instance_returning_static,
78 is_self_returning: member.is_self_returning,
79 })
80 .collect(),
81 super_class: export.super_class.clone(),
82 })
83 .collect()
84}
85
86fn cached_imports_to_module(imports: &[CachedImport]) -> Vec<crate::ImportInfo> {
87 imports
88 .iter()
89 .map(|import| crate::ImportInfo {
90 source: import.source.clone(),
91 imported_name: match import.kind {
92 IMPORT_KIND_DEFAULT => crate::ImportedName::Default,
93 IMPORT_KIND_NAMESPACE => crate::ImportedName::Namespace,
94 IMPORT_KIND_SIDE_EFFECT => crate::ImportedName::SideEffect,
95 _ => crate::ImportedName::Named(import.imported_name.clone()),
96 },
97 local_name: import.local_name.clone(),
98 is_type_only: import.is_type_only,
99 from_style: import.from_style,
100 span: Span::new(import.span_start, import.span_end),
101 source_span: Span::new(import.source_span_start, import.source_span_end),
102 })
103 .collect()
104}
105
106fn cached_re_exports_to_module(re_exports: &[CachedReExport]) -> Vec<crate::ReExportInfo> {
107 re_exports
108 .iter()
109 .map(|re_export| crate::ReExportInfo {
110 source: re_export.source.clone(),
111 imported_name: re_export.imported_name.clone(),
112 exported_name: re_export.exported_name.clone(),
113 is_type_only: re_export.is_type_only,
114 span: Span::new(re_export.span_start, re_export.span_end),
115 })
116 .collect()
117}
118
119fn cached_dynamic_imports_to_module(
120 dynamic_imports: &[CachedDynamicImport],
121) -> Vec<crate::DynamicImportInfo> {
122 dynamic_imports
123 .iter()
124 .map(|dynamic_import| crate::DynamicImportInfo {
125 source: dynamic_import.source.clone(),
126 span: Span::new(dynamic_import.span_start, dynamic_import.span_end),
127 destructured_names: dynamic_import.destructured_names.clone(),
128 local_name: dynamic_import.local_name.clone(),
129 is_speculative: dynamic_import.is_speculative,
130 })
131 .collect()
132}
133
134fn cached_require_calls_to_module(
135 require_calls: &[CachedRequireCall],
136) -> Vec<crate::RequireCallInfo> {
137 require_calls
138 .iter()
139 .map(|require_call| crate::RequireCallInfo {
140 source: require_call.source.clone(),
141 span: Span::new(require_call.span_start, require_call.span_end),
142 source_span: Span::new(require_call.source_span_start, require_call.source_span_end),
143 destructured_names: require_call.destructured_names.clone(),
144 local_name: require_call.local_name.clone(),
145 })
146 .collect()
147}
148
149fn cached_dynamic_patterns_to_module(
150 dynamic_import_patterns: &[CachedDynamicImportPattern],
151) -> Vec<crate::DynamicImportPattern> {
152 dynamic_import_patterns
153 .iter()
154 .map(|pattern| crate::DynamicImportPattern {
155 prefix: pattern.prefix.clone(),
156 suffix: pattern.suffix.clone(),
157 span: Span::new(pattern.span_start, pattern.span_end),
158 })
159 .collect()
160}
161
162fn cached_suppressions_to_module(
163 suppressions: &[CachedSuppression],
164) -> Vec<crate::suppress::Suppression> {
165 suppressions
166 .iter()
167 .map(|suppression| {
168 let target = if suppression.kind == 0 {
169 None
170 } else if suppression.kind
171 == crate::suppress::IssueKind::PolicyViolation.to_discriminant()
172 && !suppression.policy_pack.is_empty()
173 && !suppression.policy_rule_id.is_empty()
174 {
175 Some(SuppressionTarget::PolicyRule(PolicyRuleSuppression::new(
176 suppression.policy_pack.clone(),
177 suppression.policy_rule_id.clone(),
178 )))
179 } else {
180 crate::suppress::IssueKind::from_discriminant(suppression.kind)
181 .map(SuppressionTarget::Issue)
182 };
183 crate::suppress::Suppression {
184 line: suppression.line,
185 comment_line: suppression.comment_line,
186 target,
187 }
188 })
189 .collect()
190}
191
192fn cached_unknown_suppressions_to_module(
193 unknown_suppression_kinds: &[CachedUnknownSuppressionKind],
194) -> Vec<fallow_types::suppress::UnknownSuppressionKind> {
195 unknown_suppression_kinds
196 .iter()
197 .map(|unknown| fallow_types::suppress::UnknownSuppressionKind {
198 comment_line: unknown.comment_line,
199 is_file_level: unknown.is_file_level,
200 token: unknown.token.clone(),
201 })
202 .collect()
203}
204
205fn cached_local_types_to_module(
206 local_type_declarations: &[CachedLocalTypeDeclaration],
207) -> Vec<crate::LocalTypeDeclaration> {
208 local_type_declarations
209 .iter()
210 .map(|declaration| crate::LocalTypeDeclaration {
211 name: declaration.name.clone(),
212 span: Span::new(declaration.span_start, declaration.span_end),
213 })
214 .collect()
215}
216
217fn cached_signature_refs_to_module(
218 public_signature_type_references: &[CachedPublicSignatureTypeReference],
219) -> Vec<crate::PublicSignatureTypeReference> {
220 public_signature_type_references
221 .iter()
222 .map(|reference| crate::PublicSignatureTypeReference {
223 export_name: reference.export_name.clone(),
224 type_name: reference.type_name.clone(),
225 span: Span::new(reference.span_start, reference.span_end),
226 })
227 .collect()
228}
229
230fn cached_namespace_aliases_to_module(
231 namespace_object_aliases: &[CachedNamespaceObjectAlias],
232) -> Vec<NamespaceObjectAlias> {
233 namespace_object_aliases
234 .iter()
235 .map(|alias| NamespaceObjectAlias {
236 via_export_name: alias.via_export_name.clone(),
237 suffix: alias.suffix.clone(),
238 namespace_local: alias.namespace_local.clone(),
239 })
240 .collect()
241}
242
243fn module_exports_to_cached(exports: &[crate::ExportInfo]) -> Vec<CachedExport> {
244 exports
245 .iter()
246 .map(|export| CachedExport {
247 name: match &export.name {
248 ExportName::Named(name) => name.clone(),
249 ExportName::Default => String::new(),
250 },
251 is_default: matches!(export.name, ExportName::Default),
252 is_type_only: export.is_type_only,
253 is_side_effect_used: export.is_side_effect_used,
254 visibility: export.visibility as u8,
255 local_name: export.local_name.clone(),
256 span_start: export.span.start,
257 span_end: export.span.end,
258 members: export
259 .members
260 .iter()
261 .map(|member| CachedMember {
262 name: member.name.clone(),
263 kind: member.kind,
264 span_start: member.span.start,
265 span_end: member.span.end,
266 has_decorator: member.has_decorator,
267 decorator_names: member.decorator_names.clone(),
268 is_instance_returning_static: member.is_instance_returning_static,
269 is_self_returning: member.is_self_returning,
270 })
271 .collect(),
272 super_class: export.super_class.clone(),
273 })
274 .collect()
275}
276
277fn module_imports_to_cached(imports: &[crate::ImportInfo]) -> Vec<CachedImport> {
278 imports
279 .iter()
280 .map(|import| {
281 let (kind, imported_name) = match &import.imported_name {
282 crate::ImportedName::Named(name) => (IMPORT_KIND_NAMED, name.clone()),
283 crate::ImportedName::Default => (IMPORT_KIND_DEFAULT, String::new()),
284 crate::ImportedName::Namespace => (IMPORT_KIND_NAMESPACE, String::new()),
285 crate::ImportedName::SideEffect => (IMPORT_KIND_SIDE_EFFECT, String::new()),
286 };
287 CachedImport {
288 source: import.source.clone(),
289 imported_name,
290 local_name: import.local_name.clone(),
291 is_type_only: import.is_type_only,
292 from_style: import.from_style,
293 kind,
294 span_start: import.span.start,
295 span_end: import.span.end,
296 source_span_start: import.source_span.start,
297 source_span_end: import.source_span.end,
298 }
299 })
300 .collect()
301}
302
303fn module_re_exports_to_cached(re_exports: &[crate::ReExportInfo]) -> Vec<CachedReExport> {
304 re_exports
305 .iter()
306 .map(|re_export| CachedReExport {
307 source: re_export.source.clone(),
308 imported_name: re_export.imported_name.clone(),
309 exported_name: re_export.exported_name.clone(),
310 is_type_only: re_export.is_type_only,
311 span_start: re_export.span.start,
312 span_end: re_export.span.end,
313 })
314 .collect()
315}
316
317fn module_dynamic_imports_to_cached(
318 dynamic_imports: &[crate::DynamicImportInfo],
319) -> Vec<CachedDynamicImport> {
320 dynamic_imports
321 .iter()
322 .map(|dynamic_import| CachedDynamicImport {
323 source: dynamic_import.source.clone(),
324 span_start: dynamic_import.span.start,
325 span_end: dynamic_import.span.end,
326 destructured_names: dynamic_import.destructured_names.clone(),
327 local_name: dynamic_import.local_name.clone(),
328 is_speculative: dynamic_import.is_speculative,
329 })
330 .collect()
331}
332
333fn module_require_calls_to_cached(
334 require_calls: &[crate::RequireCallInfo],
335) -> Vec<CachedRequireCall> {
336 require_calls
337 .iter()
338 .map(|require_call| CachedRequireCall {
339 source: require_call.source.clone(),
340 span_start: require_call.span.start,
341 span_end: require_call.span.end,
342 source_span_start: require_call.source_span.start,
343 source_span_end: require_call.source_span.end,
344 destructured_names: require_call.destructured_names.clone(),
345 local_name: require_call.local_name.clone(),
346 })
347 .collect()
348}
349
350fn module_dynamic_patterns_to_cached(
351 dynamic_import_patterns: &[crate::DynamicImportPattern],
352) -> Vec<CachedDynamicImportPattern> {
353 dynamic_import_patterns
354 .iter()
355 .map(|pattern| CachedDynamicImportPattern {
356 prefix: pattern.prefix.clone(),
357 suffix: pattern.suffix.clone(),
358 span_start: pattern.span.start,
359 span_end: pattern.span.end,
360 })
361 .collect()
362}
363
364fn module_suppressions_to_cached(
365 suppressions: &[crate::suppress::Suppression],
366) -> Vec<CachedSuppression> {
367 suppressions
368 .iter()
369 .map(|suppression| {
370 let (kind, policy_pack, policy_rule_id) = match &suppression.target {
371 None => (0, String::new(), String::new()),
372 Some(SuppressionTarget::Issue(kind)) => {
373 (kind.to_discriminant(), String::new(), String::new())
374 }
375 Some(SuppressionTarget::PolicyRule(target)) => (
376 crate::suppress::IssueKind::PolicyViolation.to_discriminant(),
377 target.pack.clone(),
378 target.rule_id.clone(),
379 ),
380 };
381 CachedSuppression {
382 line: suppression.line,
383 comment_line: suppression.comment_line,
384 kind,
385 policy_pack,
386 policy_rule_id,
387 }
388 })
389 .collect()
390}
391
392fn module_unknown_suppressions_to_cached(
393 unknown_suppression_kinds: &[fallow_types::suppress::UnknownSuppressionKind],
394) -> Vec<CachedUnknownSuppressionKind> {
395 unknown_suppression_kinds
396 .iter()
397 .map(|unknown| CachedUnknownSuppressionKind {
398 comment_line: unknown.comment_line,
399 is_file_level: unknown.is_file_level,
400 token: unknown.token.clone(),
401 })
402 .collect()
403}
404
405fn module_local_types_to_cached(
406 local_type_declarations: &[crate::LocalTypeDeclaration],
407) -> Vec<CachedLocalTypeDeclaration> {
408 local_type_declarations
409 .iter()
410 .map(|declaration| CachedLocalTypeDeclaration {
411 name: declaration.name.clone(),
412 span_start: declaration.span.start,
413 span_end: declaration.span.end,
414 })
415 .collect()
416}
417
418fn module_signature_refs_to_cached(
419 public_signature_type_references: &[crate::PublicSignatureTypeReference],
420) -> Vec<CachedPublicSignatureTypeReference> {
421 public_signature_type_references
422 .iter()
423 .map(|reference| CachedPublicSignatureTypeReference {
424 export_name: reference.export_name.clone(),
425 type_name: reference.type_name.clone(),
426 span_start: reference.span.start,
427 span_end: reference.span.end,
428 })
429 .collect()
430}
431
432fn module_namespace_aliases_to_cached(
433 namespace_object_aliases: &[NamespaceObjectAlias],
434) -> Vec<CachedNamespaceObjectAlias> {
435 namespace_object_aliases
436 .iter()
437 .map(|alias| CachedNamespaceObjectAlias {
438 via_export_name: alias.via_export_name.clone(),
439 suffix: alias.suffix.clone(),
440 namespace_local: alias.namespace_local.clone(),
441 })
442 .collect()
443}
444
445#[must_use]
450pub fn cached_to_module_opts(
451 cached: &CachedModule,
452 file_id: fallow_types::discover::FileId,
453 need_complexity: bool,
454) -> crate::ModuleInfo {
455 crate::ModuleInfo {
456 file_id,
457 exports: cached_exports_to_module(&cached.exports),
458 imports: cached_imports_to_module(&cached.imports),
459 re_exports: cached_re_exports_to_module(&cached.re_exports),
460 dynamic_imports: cached_dynamic_imports_to_module(&cached.dynamic_imports),
461 dynamic_import_patterns: cached_dynamic_patterns_to_module(&cached.dynamic_import_patterns),
462 require_calls: cached_require_calls_to_module(&cached.require_calls),
463 package_path_references: cached.package_path_references.clone(),
464 member_accesses: cached.member_accesses.clone(),
465 whole_object_uses: cached.whole_object_uses.clone(),
466 has_cjs_exports: cached.has_cjs_exports,
467 has_angular_component_template_url: cached.has_angular_component_template_url,
468 content_hash: cached.content_hash,
469 suppressions: cached_suppressions_to_module(&cached.suppressions),
470 unknown_suppression_kinds: cached_unknown_suppressions_to_module(
471 &cached.unknown_suppression_kinds,
472 ),
473 unused_import_bindings: cached.unused_import_bindings.clone(),
474 type_referenced_import_bindings: cached.type_referenced_import_bindings.clone(),
475 value_referenced_import_bindings: cached.value_referenced_import_bindings.clone(),
476 line_offsets: cached.line_offsets.clone(),
477 complexity: if need_complexity {
478 cached.complexity.clone()
479 } else {
480 Vec::new()
481 },
482 flag_uses: cached.flag_uses.clone(),
483 class_heritage: cached.class_heritage.clone(),
484 injection_tokens: cached.injection_tokens.clone(),
485 local_type_declarations: cached_local_types_to_module(&cached.local_type_declarations),
486 public_signature_type_references: cached_signature_refs_to_module(
487 &cached.public_signature_type_references,
488 ),
489 namespace_object_aliases: cached_namespace_aliases_to_module(
490 &cached.namespace_object_aliases,
491 ),
492 iconify_prefixes: cached.iconify_prefixes.clone(),
493 iconify_icon_names: cached.iconify_icon_names.clone(),
494 auto_import_candidates: cached.auto_import_candidates.clone(),
495 directives: cached.directives.clone(),
496 security_sinks: cached.security_sinks.clone(),
497 security_sinks_skipped: cached.security_sinks_skipped,
498 security_unresolved_callee_sites: cached.security_unresolved_callee_sites.clone(),
499 tainted_bindings: cached.tainted_bindings.clone(),
500 sanitized_sink_args: cached.sanitized_sink_args.clone(),
501 security_control_sites: cached.security_control_sites.clone(),
502 callee_uses: cached.callee_uses.clone(),
503 }
504}
505
506#[must_use]
512pub fn module_to_cached(
513 module: &crate::ModuleInfo,
514 mtime_secs: u64,
515 file_size: u64,
516) -> CachedModule {
517 CachedModule {
518 content_hash: module.content_hash,
519 mtime_secs,
520 file_size,
521 last_access_secs: current_unix_seconds(),
522 exports: module_exports_to_cached(&module.exports),
523 imports: module_imports_to_cached(&module.imports),
524 re_exports: module_re_exports_to_cached(&module.re_exports),
525 dynamic_imports: module_dynamic_imports_to_cached(&module.dynamic_imports),
526 require_calls: module_require_calls_to_cached(&module.require_calls),
527 package_path_references: module.package_path_references.clone(),
528 member_accesses: module.member_accesses.clone(),
529 whole_object_uses: module.whole_object_uses.clone(),
530 dynamic_import_patterns: module_dynamic_patterns_to_cached(&module.dynamic_import_patterns),
531 has_cjs_exports: module.has_cjs_exports,
532 has_angular_component_template_url: module.has_angular_component_template_url,
533 unused_import_bindings: module.unused_import_bindings.clone(),
534 type_referenced_import_bindings: module.type_referenced_import_bindings.clone(),
535 value_referenced_import_bindings: module.value_referenced_import_bindings.clone(),
536 suppressions: module_suppressions_to_cached(&module.suppressions),
537 unknown_suppression_kinds: module_unknown_suppressions_to_cached(
538 &module.unknown_suppression_kinds,
539 ),
540 line_offsets: module.line_offsets.clone(),
541 complexity: module.complexity.clone(),
542 flag_uses: module.flag_uses.clone(),
543 class_heritage: module.class_heritage.clone(),
544 injection_tokens: module.injection_tokens.clone(),
545 local_type_declarations: module_local_types_to_cached(&module.local_type_declarations),
546 public_signature_type_references: module_signature_refs_to_cached(
547 &module.public_signature_type_references,
548 ),
549 namespace_object_aliases: module_namespace_aliases_to_cached(
550 &module.namespace_object_aliases,
551 ),
552 iconify_prefixes: module.iconify_prefixes.clone(),
553 iconify_icon_names: module.iconify_icon_names.clone(),
554 auto_import_candidates: module.auto_import_candidates.clone(),
555 directives: module.directives.clone(),
556 security_sinks: module.security_sinks.clone(),
557 security_sinks_skipped: module.security_sinks_skipped,
558 security_unresolved_callee_sites: module.security_unresolved_callee_sites.clone(),
559 tainted_bindings: module.tainted_bindings.clone(),
560 sanitized_sink_args: module.sanitized_sink_args.clone(),
561 security_control_sites: module.security_control_sites.clone(),
562 callee_uses: module.callee_uses.clone(),
563 }
564}