From 366a87975d8c15dfefbc48e6a9022685deb99048 Mon Sep 17 00:00:00 2001 From: Boshen Date: Mon, 11 Mar 2024 21:43:11 +0800 Subject: [PATCH] feat(linter): resolve ESM star exports (#2682) --- crates/oxc_linter/src/service.rs | 51 +++++++++++++++++++++----- crates/oxc_syntax/src/module_record.rs | 12 ++++++ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/crates/oxc_linter/src/service.rs b/crates/oxc_linter/src/service.rs index fcd00b5f4..1384d8635 100644 --- a/crates/oxc_linter/src/service.rs +++ b/crates/oxc_linter/src/service.rs @@ -289,17 +289,50 @@ impl Runtime { .for_each_with(tx_error, |tx_error, (specifier, resolution)| { let path = resolution.path(); self.process_path(path, tx_error); - if let Some(target_module_record_ref) = self.module_map.get(path) { - if let ModuleState::Resolved(target_module_record) = - target_module_record_ref.value() - { - module_record - .loaded_modules - .insert(specifier.clone(), Arc::clone(target_module_record)); - } - } + let Some(target_module_record_ref) = self.module_map.get(path) else { return }; + let ModuleState::Resolved(target_module_record) = + target_module_record_ref.value() + else { + return; + }; + // Append target_module to loaded_modules + module_record + .loaded_modules + .insert(specifier.clone(), Arc::clone(target_module_record)); }); + // The thread is blocked here until all dependent modules are resolved. + + // Resolve and append `star_export_bindings` + for export_entry in &module_record.star_export_entries { + let Some(remote_module_record_ref) = + export_entry.module_request.as_ref().and_then(|module_request| { + module_record.loaded_modules.get(module_request.name()) + }) + else { + continue; + }; + let remote_module_record = remote_module_record_ref.value(); + + // Append both remote `bindings` and `exported_bindings_from_star_export` + let remote_exported_bindings_from_star_export = remote_module_record + .exported_bindings_from_star_export + .iter() + .flat_map(|r| r.value().clone()); + let remote_bindings = remote_module_record + .exported_bindings + .keys() + .cloned() + .chain(remote_exported_bindings_from_star_export) + .collect::>(); + module_record + .exported_bindings_from_star_export + .entry(remote_module_record.resolved_absolute_path.clone()) + .or_default() + .value_mut() + .extend(remote_bindings); + } + // Stop if the current module is not marked for lint. if !self.paths.contains(path) { return vec![]; diff --git a/crates/oxc_syntax/src/module_record.rs b/crates/oxc_syntax/src/module_record.rs index 64df6cf3a..fca0d7c5d 100644 --- a/crates/oxc_syntax/src/module_record.rs +++ b/crates/oxc_syntax/src/module_record.rs @@ -65,10 +65,21 @@ pub struct ModuleRecord { /// not including export * as namespace declarations. pub star_export_entries: Vec, + /// Local exported bindings pub exported_bindings: FxHashMap, + + /// Local duplicated exported bindings, for diagnostics pub exported_bindings_duplicated: Vec, + /// Reexported bindings from `export * from 'specifier'` + /// Keyed by resolved path + pub exported_bindings_from_star_export: DashMap>, + + /// `export default name` + /// ^^^^^^^ span pub export_default: Option, + + /// Duplicated span of `export default` for diagnostics pub export_default_duplicated: Vec, } @@ -99,6 +110,7 @@ impl fmt::Debug for ModuleRecord { .field("star_export_entries", &self.star_export_entries) .field("exported_bindings", &self.exported_bindings) .field("exported_bindings_duplicated", &self.exported_bindings_duplicated) + .field("exported_bindings_from_star_export", &self.exported_bindings_from_star_export) .field("export_default", &self.export_default) .field("export_default_duplicated", &self.export_default_duplicated) .finish()