use oxc_allocator::{Allocator, HashMap, Vec};
use oxc_ast_macros::ast;
use oxc_estree::ESTree;
use oxc_span::{Span, Str};
#[derive(Debug)]
pub struct ModuleRecord<'a> {
pub has_module_syntax: bool,
pub requested_modules: HashMap<'a, Str<'a>, Vec<'a, RequestedModule>>,
pub import_entries: Vec<'a, ImportEntry<'a>>,
pub local_export_entries: Vec<'a, ExportEntry<'a>>,
pub indirect_export_entries: Vec<'a, ExportEntry<'a>>,
pub star_export_entries: Vec<'a, ExportEntry<'a>>,
pub exported_bindings: HashMap<'a, Str<'a>, Span>,
pub dynamic_imports: Vec<'a, DynamicImport>,
pub import_metas: Vec<'a, Span>,
}
impl<'a> ModuleRecord<'a> {
pub fn new(allocator: &'a Allocator) -> Self {
Self {
has_module_syntax: false,
requested_modules: HashMap::new_in(allocator),
import_entries: Vec::new_in(allocator),
local_export_entries: Vec::new_in(allocator),
indirect_export_entries: Vec::new_in(allocator),
star_export_entries: Vec::new_in(allocator),
exported_bindings: HashMap::new_in(allocator),
dynamic_imports: Vec::new_in(allocator),
import_metas: Vec::new_in(allocator),
}
}
}
#[ast]
#[derive(Debug, Clone, PartialEq, Eq)]
#[generate_derive(ESTree)]
#[estree(no_type, no_ts_def)]
pub struct NameSpan<'a> {
#[estree(rename = "value")]
pub name: Str<'a>,
pub span: Span,
}
impl<'a> NameSpan<'a> {
pub fn new(name: Str<'a>, span: Span) -> Self {
Self { span, name }
}
}
#[ast]
#[derive(Debug, Clone, PartialEq, Eq)]
#[generate_derive(ESTree)]
#[estree(no_type, no_ts_def)]
pub struct ImportEntry<'a> {
#[estree(skip)]
pub statement_span: Span,
#[estree(skip)]
pub module_request: NameSpan<'a>,
pub import_name: ImportImportName<'a>,
pub local_name: NameSpan<'a>,
pub is_type: bool,
}
#[ast]
#[derive(Debug, Clone, PartialEq, Eq)]
#[generate_derive(ESTree)]
#[estree(no_ts_def)]
pub enum ImportImportName<'a> {
#[estree(via = ImportOrExportNameName)]
Name(NameSpan<'a>) = 0,
#[estree(via = ImportImportNameNamespaceObject)]
NamespaceObject = 1,
#[estree(via = ImportOrExportNameDefault)]
Default(Span) = 2,
}
impl ImportImportName<'_> {
pub fn is_default(&self) -> bool {
matches!(self, Self::Default(_))
}
pub fn is_namespace_object(&self) -> bool {
matches!(self, Self::NamespaceObject)
}
}
#[ast]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[generate_derive(ESTree)]
#[estree(no_type, no_ts_def)]
pub struct ExportEntry<'a> {
#[estree(skip)]
pub statement_span: Span,
pub span: Span,
pub module_request: Option<NameSpan<'a>>,
pub import_name: ExportImportName<'a>,
pub export_name: ExportExportName<'a>,
pub local_name: ExportLocalName<'a>,
pub is_type: bool,
}
#[ast]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[generate_derive(ESTree)]
#[estree(no_ts_def)]
pub enum ExportImportName<'a> {
#[estree(via = ImportOrExportNameName)]
Name(NameSpan<'a>) = 0,
#[estree(via = ExportImportNameAll)]
All = 1,
#[estree(via = ExportImportNameAllButDefault)]
AllButDefault = 2,
#[default]
#[estree(via = ExportNameNull)]
Null = 3,
}
impl ExportImportName<'_> {
pub fn is_all(&self) -> bool {
matches!(self, Self::All)
}
pub fn is_all_but_default(&self) -> bool {
matches!(self, Self::AllButDefault)
}
}
#[ast]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[generate_derive(ESTree)]
#[estree(no_ts_def)]
pub enum ExportExportName<'a> {
#[estree(via = ImportOrExportNameName)]
Name(NameSpan<'a>) = 0,
#[estree(via = ImportOrExportNameDefault)]
Default(Span) = 1,
#[estree(via = ExportNameNull)]
#[default]
Null = 2,
}
impl ExportExportName<'_> {
pub fn is_default(&self) -> bool {
matches!(self, Self::Default(_))
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn span(&self) -> Option<Span> {
match self {
Self::Name(name) => Some(name.span),
Self::Default(span) => Some(*span),
Self::Null => None,
}
}
pub fn default_export_span(&self) -> Option<Span> {
match self {
Self::Default(span) => Some(*span),
Self::Name(name_span) if name_span.name == "default" => Some(name_span.span),
_ => None,
}
}
}
#[ast]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[generate_derive(ESTree)]
#[estree(no_ts_def)]
pub enum ExportLocalName<'a> {
#[estree(via = ImportOrExportNameName)]
Name(NameSpan<'a>) = 0,
#[estree(via = ExportLocalNameDefault)]
Default(NameSpan<'a>) = 1,
#[default]
#[estree(via = ExportNameNull)]
Null = 2,
}
impl<'a> ExportLocalName<'a> {
pub fn is_default(&self) -> bool {
matches!(self, Self::Default(_))
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn name(&self) -> Option<Str<'a>> {
match self {
Self::Name(name) | Self::Default(name) => Some(name.name),
Self::Null => None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct RequestedModule {
pub statement_span: Span,
pub span: Span,
pub is_type: bool,
pub is_import: bool,
}
#[ast]
#[derive(Debug, Clone, Copy)]
#[generate_derive(ESTree)]
#[estree(no_type, no_ts_def)]
pub struct DynamicImport {
pub span: Span,
#[estree(no_flatten)]
pub module_request: Span,
}
pub trait VisitMutModuleRecord {
fn visit_module_record(&mut self, module_record: &mut ModuleRecord) {
module_record.requested_modules.values_mut().for_each(|e| {
e.iter_mut().for_each(|e| self.visit_requested_module(e));
});
module_record.import_entries.iter_mut().for_each(|e| self.visit_import_entry(e));
module_record.local_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
module_record.indirect_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
module_record.star_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
module_record.dynamic_imports.iter_mut().for_each(|e| self.visit_dynamic_import(e));
module_record.import_metas.iter_mut().for_each(|e| self.visit_span(e));
}
fn visit_requested_module(&mut self, requested_module: &mut RequestedModule) {
self.visit_span(&mut requested_module.span);
self.visit_span(&mut requested_module.statement_span);
}
fn visit_import_entry(&mut self, import_entry: &mut ImportEntry) {
self.visit_span(&mut import_entry.statement_span);
self.visit_name_span(&mut import_entry.module_request);
self.visit_name_span(&mut import_entry.local_name);
self.visit_import_import_name(&mut import_entry.import_name);
}
fn visit_import_import_name(&mut self, import_import_name: &mut ImportImportName) {
match import_import_name {
ImportImportName::Name(name_span) => self.visit_name_span(name_span),
ImportImportName::NamespaceObject => {}
ImportImportName::Default(span) => self.visit_span(span),
}
}
fn visit_export_entry(&mut self, export_entry: &mut ExportEntry) {
self.visit_span(&mut export_entry.statement_span);
self.visit_span(&mut export_entry.span);
if let Some(module_request) = &mut export_entry.module_request {
self.visit_name_span(module_request);
}
self.visit_export_import_name(&mut export_entry.import_name);
self.visit_export_export_name(&mut export_entry.export_name);
self.visit_export_local_name(&mut export_entry.local_name);
}
fn visit_export_import_name(&mut self, export_import_name: &mut ExportImportName) {
match export_import_name {
ExportImportName::Name(name_span) => self.visit_name_span(name_span),
ExportImportName::All | ExportImportName::AllButDefault | ExportImportName::Null => {}
}
}
fn visit_export_export_name(&mut self, export_export_name: &mut ExportExportName) {
match export_export_name {
ExportExportName::Name(name_span) => self.visit_name_span(name_span),
ExportExportName::Default(span) => self.visit_span(span),
ExportExportName::Null => {}
}
}
fn visit_export_local_name(&mut self, export_local_name: &mut ExportLocalName) {
match export_local_name {
ExportLocalName::Name(name_span) | ExportLocalName::Default(name_span) => {
self.visit_name_span(name_span);
}
ExportLocalName::Null => {}
}
}
fn visit_dynamic_import(&mut self, dynamic_import: &mut DynamicImport) {
self.visit_span(&mut dynamic_import.module_request);
self.visit_span(&mut dynamic_import.span);
}
fn visit_name_span(&mut self, name_span: &mut NameSpan) {
self.visit_span(&mut name_span.span);
}
#[expect(unused_variables)]
#[inline(always)]
fn visit_span(&mut self, span: &mut Span) {}
}
#[cfg(test)]
mod test {
use oxc_span::Span;
use super::{ExportExportName, ExportLocalName, ImportImportName, NameSpan};
#[test]
fn import_import_name() {
let name = NameSpan::new("name".into(), Span::new(0, 0));
assert!(!ImportImportName::Name(name.clone()).is_default());
assert!(!ImportImportName::NamespaceObject.is_default());
assert!(ImportImportName::Default(Span::new(0, 0)).is_default());
assert!(!ImportImportName::Name(name.clone()).is_namespace_object());
assert!(ImportImportName::NamespaceObject.is_namespace_object());
assert!(!ImportImportName::Default(Span::new(0, 0)).is_namespace_object());
}
#[test]
fn export_import_name() {
let name = NameSpan::new("name".into(), Span::new(0, 0));
assert!(!ExportExportName::Name(name.clone()).is_default());
assert!(ExportExportName::Default(Span::new(0, 0)).is_default());
assert!(!ExportExportName::Null.is_default());
assert!(!ExportExportName::Name(name).is_null());
assert!(!ExportExportName::Default(Span::new(0, 0)).is_null());
assert!(ExportExportName::Null.is_null());
}
#[test]
fn export_local_name() {
let name = NameSpan::new("name".into(), Span::new(0, 0));
assert!(!ExportLocalName::Name(name.clone()).is_default());
assert!(ExportLocalName::Default(name.clone()).is_default());
assert!(!ExportLocalName::Null.is_default());
assert!(!ExportLocalName::Name(name.clone()).is_null());
assert!(!ExportLocalName::Default(name.clone()).is_null());
assert!(ExportLocalName::Null.is_null());
}
}