Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(linter): resolve ESM star exports #2682

Merged
merged 1 commit into from Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
51 changes: 42 additions & 9 deletions crates/oxc_linter/src/service.rs
Expand Up @@ -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::<Vec<_>>();
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![];
Expand Down
12 changes: 12 additions & 0 deletions crates/oxc_syntax/src/module_record.rs
Expand Up @@ -65,10 +65,21 @@ pub struct ModuleRecord {
/// not including export * as namespace declarations.
pub star_export_entries: Vec<ExportEntry>,

/// Local exported bindings
pub exported_bindings: FxHashMap<CompactStr, Span>,

/// Local duplicated exported bindings, for diagnostics
pub exported_bindings_duplicated: Vec<NameSpan>,

/// Reexported bindings from `export * from 'specifier'`
/// Keyed by resolved path
pub exported_bindings_from_star_export: DashMap<PathBuf, Vec<CompactStr>>,
Dunqing marked this conversation as resolved.
Show resolved Hide resolved

/// `export default name`
/// ^^^^^^^ span
pub export_default: Option<Span>,

/// Duplicated span of `export default` for diagnostics
pub export_default_duplicated: Vec<Span>,
}

Expand Down Expand Up @@ -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()
Expand Down