1use oxc_allocator::{Allocator, HashMap, Vec};
4use oxc_ast_macros::ast;
5use oxc_estree::ESTree;
6use oxc_span::{Atom, Span};
7
8#[derive(Debug)]
16pub struct ModuleRecord<'a> {
17 pub has_module_syntax: bool,
19
20 pub requested_modules: HashMap<'a, Atom<'a>, Vec<'a, RequestedModule>>,
31
32 pub import_entries: Vec<'a, ImportEntry<'a>>,
36
37 pub local_export_entries: Vec<'a, ExportEntry<'a>>,
42
43 pub indirect_export_entries: Vec<'a, ExportEntry<'a>>,
49
50 pub star_export_entries: Vec<'a, ExportEntry<'a>>,
56
57 pub exported_bindings: HashMap<'a, Atom<'a>, Span>,
59
60 pub dynamic_imports: Vec<'a, DynamicImport>,
62
63 pub import_metas: Vec<'a, Span>,
65}
66
67impl<'a> ModuleRecord<'a> {
68 pub fn new(allocator: &'a Allocator) -> Self {
70 Self {
71 has_module_syntax: false,
72 requested_modules: HashMap::new_in(allocator),
73 import_entries: Vec::new_in(allocator),
74 local_export_entries: Vec::new_in(allocator),
75 indirect_export_entries: Vec::new_in(allocator),
76 star_export_entries: Vec::new_in(allocator),
77 exported_bindings: HashMap::new_in(allocator),
78 dynamic_imports: Vec::new_in(allocator),
79 import_metas: Vec::new_in(allocator),
80 }
81 }
82}
83
84#[ast]
86#[derive(Debug, Clone, PartialEq, Eq)]
87#[generate_derive(ESTree)]
88#[estree(no_type, no_ts_def)]
89pub struct NameSpan<'a> {
90 #[estree(rename = "value")]
92 pub name: Atom<'a>,
93
94 pub span: Span,
96}
97
98impl<'a> NameSpan<'a> {
99 pub fn new(name: Atom<'a>, span: Span) -> Self {
101 Self { span, name }
102 }
103}
104
105#[ast]
121#[derive(Debug, Clone, PartialEq, Eq)]
122#[generate_derive(ESTree)]
123#[estree(no_type, no_ts_def)]
124pub struct ImportEntry<'a> {
125 #[estree(skip)]
127 pub statement_span: Span,
128
129 #[estree(skip)]
138 pub module_request: NameSpan<'a>,
139
140 pub import_name: ImportImportName<'a>,
151
152 pub local_name: NameSpan<'a>,
163
164 pub is_type: bool,
181}
182
183#[ast]
185#[derive(Debug, Clone, PartialEq, Eq)]
186#[generate_derive(ESTree)]
187#[estree(no_ts_def)]
188pub enum ImportImportName<'a> {
189 #[estree(via = ImportOrExportNameName)]
191 Name(NameSpan<'a>) = 0,
192 #[estree(via = ImportImportNameNamespaceObject)]
194 NamespaceObject = 1,
195 #[estree(via = ImportOrExportNameDefault)]
197 Default(Span) = 2,
198}
199
200impl ImportImportName<'_> {
201 pub fn is_default(&self) -> bool {
203 matches!(self, Self::Default(_))
204 }
205
206 pub fn is_namespace_object(&self) -> bool {
208 matches!(self, Self::NamespaceObject)
209 }
210}
211
212#[ast]
232#[derive(Debug, Default, Clone, PartialEq, Eq)]
233#[generate_derive(ESTree)]
234#[estree(no_type, no_ts_def)]
235pub struct ExportEntry<'a> {
236 #[estree(skip)]
238 pub statement_span: Span,
239
240 pub span: Span,
242
243 pub module_request: Option<NameSpan<'a>>,
246
247 pub import_name: ExportImportName<'a>,
252
253 pub export_name: ExportExportName<'a>,
255
256 pub local_name: ExportLocalName<'a>,
259
260 pub is_type: bool,
272}
273
274#[ast]
276#[derive(Debug, Default, Clone, PartialEq, Eq)]
277#[generate_derive(ESTree)]
278#[estree(no_ts_def)]
279pub enum ExportImportName<'a> {
280 #[estree(via = ImportOrExportNameName)]
282 Name(NameSpan<'a>) = 0,
283 #[estree(via = ExportImportNameAll)]
285 All = 1,
286 #[estree(via = ExportImportNameAllButDefault)]
288 AllButDefault = 2,
289 #[default]
291 #[estree(via = ExportNameNull)]
292 Null = 3,
293}
294
295impl ExportImportName<'_> {
297 pub fn is_all(&self) -> bool {
299 matches!(self, Self::All)
300 }
301
302 pub fn is_all_but_default(&self) -> bool {
304 matches!(self, Self::AllButDefault)
305 }
306}
307
308#[ast]
310#[derive(Debug, Default, Clone, PartialEq, Eq)]
311#[generate_derive(ESTree)]
312#[estree(no_ts_def)]
313pub enum ExportExportName<'a> {
314 #[estree(via = ImportOrExportNameName)]
316 Name(NameSpan<'a>) = 0,
317 #[estree(via = ImportOrExportNameDefault)]
319 Default(Span) = 1,
320 #[estree(via = ExportNameNull)]
322 #[default]
323 Null = 2,
324}
325
326impl ExportExportName<'_> {
327 pub fn is_default(&self) -> bool {
329 matches!(self, Self::Default(_))
330 }
331
332 pub fn is_null(&self) -> bool {
334 matches!(self, Self::Null)
335 }
336
337 pub fn span(&self) -> Option<Span> {
339 match self {
340 Self::Name(name) => Some(name.span),
341 Self::Default(span) => Some(*span),
342 Self::Null => None,
343 }
344 }
345
346 pub fn default_export_span(&self) -> Option<Span> {
350 match self {
351 Self::Default(span) => Some(*span),
352 Self::Name(name_span) if name_span.name == "default" => Some(name_span.span),
353 _ => None,
354 }
355 }
356}
357
358#[ast]
360#[derive(Debug, Default, Clone, PartialEq, Eq)]
361#[generate_derive(ESTree)]
362#[estree(no_ts_def)]
363pub enum ExportLocalName<'a> {
364 #[estree(via = ImportOrExportNameName)]
366 Name(NameSpan<'a>) = 0,
367 #[estree(via = ExportLocalNameDefault)]
369 Default(NameSpan<'a>) = 1,
370 #[default]
372 #[estree(via = ExportNameNull)]
373 Null = 2,
374}
375
376impl<'a> ExportLocalName<'a> {
377 pub fn is_default(&self) -> bool {
379 matches!(self, Self::Default(_))
380 }
381
382 pub fn is_null(&self) -> bool {
384 matches!(self, Self::Null)
385 }
386
387 pub fn name(&self) -> Option<Atom<'a>> {
389 match self {
390 Self::Name(name) | Self::Default(name) => Some(name.name),
391 Self::Null => None,
392 }
393 }
394}
395
396#[derive(Debug, Clone, Copy)]
398pub struct RequestedModule {
399 pub statement_span: Span,
401
402 pub span: Span,
404
405 pub is_type: bool,
414
415 pub is_import: bool,
417}
418
419#[ast]
421#[derive(Debug, Clone, Copy)]
422#[generate_derive(ESTree)]
423#[estree(no_type, no_ts_def)]
424pub struct DynamicImport {
425 pub span: Span,
427 #[estree(no_flatten)]
429 pub module_request: Span,
430}
431
432#[expect(missing_docs)]
433pub trait VisitMutModuleRecord {
434 fn visit_module_record(&mut self, module_record: &mut ModuleRecord) {
435 module_record.requested_modules.values_mut().for_each(|e| {
436 e.iter_mut().for_each(|e| self.visit_requested_module(e));
437 });
438 module_record.import_entries.iter_mut().for_each(|e| self.visit_import_entry(e));
439 module_record.local_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
440 module_record.indirect_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
441 module_record.star_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
442 module_record.dynamic_imports.iter_mut().for_each(|e| self.visit_dynamic_import(e));
443 module_record.import_metas.iter_mut().for_each(|e| self.visit_span(e));
444 }
445
446 fn visit_requested_module(&mut self, requested_module: &mut RequestedModule) {
447 self.visit_span(&mut requested_module.span);
448 self.visit_span(&mut requested_module.statement_span);
449 }
450
451 fn visit_import_entry(&mut self, import_entry: &mut ImportEntry) {
452 self.visit_span(&mut import_entry.statement_span);
453 self.visit_name_span(&mut import_entry.module_request);
454 self.visit_name_span(&mut import_entry.local_name);
455 self.visit_import_import_name(&mut import_entry.import_name);
456 }
457
458 fn visit_import_import_name(&mut self, import_import_name: &mut ImportImportName) {
459 match import_import_name {
460 ImportImportName::Name(name_span) => self.visit_name_span(name_span),
461 ImportImportName::NamespaceObject => {}
462 ImportImportName::Default(span) => self.visit_span(span),
463 }
464 }
465
466 fn visit_export_entry(&mut self, export_entry: &mut ExportEntry) {
467 self.visit_span(&mut export_entry.statement_span);
468 self.visit_span(&mut export_entry.span);
469 if let Some(module_request) = &mut export_entry.module_request {
470 self.visit_name_span(module_request);
471 }
472 self.visit_export_import_name(&mut export_entry.import_name);
473 self.visit_export_export_name(&mut export_entry.export_name);
474 self.visit_export_local_name(&mut export_entry.local_name);
475 }
476
477 fn visit_export_import_name(&mut self, export_import_name: &mut ExportImportName) {
478 match export_import_name {
479 ExportImportName::Name(name_span) => self.visit_name_span(name_span),
480 ExportImportName::All | ExportImportName::AllButDefault | ExportImportName::Null => {}
481 }
482 }
483
484 fn visit_export_export_name(&mut self, export_export_name: &mut ExportExportName) {
485 match export_export_name {
486 ExportExportName::Name(name_span) => self.visit_name_span(name_span),
487 ExportExportName::Default(span) => self.visit_span(span),
488 ExportExportName::Null => {}
489 }
490 }
491
492 fn visit_export_local_name(&mut self, export_local_name: &mut ExportLocalName) {
493 match export_local_name {
494 ExportLocalName::Name(name_span) | ExportLocalName::Default(name_span) => {
495 self.visit_name_span(name_span);
496 }
497 ExportLocalName::Null => {}
498 }
499 }
500
501 fn visit_dynamic_import(&mut self, dynamic_import: &mut DynamicImport) {
502 self.visit_span(&mut dynamic_import.module_request);
503 self.visit_span(&mut dynamic_import.span);
504 }
505
506 fn visit_name_span(&mut self, name_span: &mut NameSpan) {
507 self.visit_span(&mut name_span.span);
508 }
509
510 #[expect(unused_variables)]
511 #[inline(always)]
512 fn visit_span(&mut self, span: &mut Span) {}
513}
514
515#[cfg(test)]
516mod test {
517 use oxc_span::Span;
518
519 use super::{ExportExportName, ExportLocalName, ImportImportName, NameSpan};
520
521 #[test]
522 fn import_import_name() {
523 let name = NameSpan::new("name".into(), Span::new(0, 0));
524 assert!(!ImportImportName::Name(name.clone()).is_default());
525 assert!(!ImportImportName::NamespaceObject.is_default());
526 assert!(ImportImportName::Default(Span::new(0, 0)).is_default());
527
528 assert!(!ImportImportName::Name(name.clone()).is_namespace_object());
529 assert!(ImportImportName::NamespaceObject.is_namespace_object());
530 assert!(!ImportImportName::Default(Span::new(0, 0)).is_namespace_object());
531 }
532
533 #[test]
534 fn export_import_name() {
535 let name = NameSpan::new("name".into(), Span::new(0, 0));
536 assert!(!ExportExportName::Name(name.clone()).is_default());
537 assert!(ExportExportName::Default(Span::new(0, 0)).is_default());
538 assert!(!ExportExportName::Null.is_default());
539
540 assert!(!ExportExportName::Name(name).is_null());
541 assert!(!ExportExportName::Default(Span::new(0, 0)).is_null());
542 assert!(ExportExportName::Null.is_null());
543 }
544
545 #[test]
546 fn export_local_name() {
547 let name = NameSpan::new("name".into(), Span::new(0, 0));
548 assert!(!ExportLocalName::Name(name.clone()).is_default());
549 assert!(ExportLocalName::Default(name.clone()).is_default());
550 assert!(!ExportLocalName::Null.is_default());
551
552 assert!(!ExportLocalName::Name(name.clone()).is_null());
553 assert!(!ExportLocalName::Default(name.clone()).is_null());
554 assert!(ExportLocalName::Null.is_null());
555 }
556}