mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(linter): clean up APIs for ModuleRecord (#7556)
This commit is contained in:
parent
c2ced15dfd
commit
823353a6fc
13 changed files with 45 additions and 54 deletions
|
|
@ -1,5 +1,4 @@
|
||||||
//! [ECMAScript Module Record](https://tc39.es/ecma262/#sec-abstract-module-records)
|
//! [ECMAScript Module Record](https://tc39.es/ecma262/#sec-abstract-module-records)
|
||||||
#![allow(missing_docs)] // fixme
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
|
|
@ -124,7 +123,7 @@ impl fmt::Debug for ModuleRecord {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct NameSpan {
|
pub struct NameSpan {
|
||||||
name: CompactStr,
|
pub name: CompactStr,
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,11 +132,11 @@ impl NameSpan {
|
||||||
Self { name, span }
|
Self { name, span }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn name(&self) -> &CompactStr {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
self.name.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn span(&self) -> Span {
|
pub fn span(&self) -> Span {
|
||||||
self.span
|
self.span
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -413,9 +412,9 @@ impl ExportLocalName {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bound name of this export. [`None`] for [`ExportLocalName::Null`].
|
/// Get the bound name of this export. [`None`] for [`ExportLocalName::Null`].
|
||||||
pub const fn name(&self) -> Option<&CompactStr> {
|
pub fn name(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
Self::Name(name) | Self::Default(name) => Some(name.name()),
|
Self::Name(name) | Self::Default(name) => Some(name.name.as_str()),
|
||||||
Self::Null => None,
|
Self::Null => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ impl Rule for NoDuplicateImports {
|
||||||
let mut side_effect_import_map: FxHashMap<&CompactStr, Vec<Span>> = FxHashMap::default();
|
let mut side_effect_import_map: FxHashMap<&CompactStr, Vec<Span>> = FxHashMap::default();
|
||||||
|
|
||||||
for entry in &module_record.import_entries {
|
for entry in &module_record.import_entries {
|
||||||
let source = entry.module_request.name();
|
let source = &entry.module_request.name;
|
||||||
let span = entry.module_request.span();
|
let span = entry.module_request.span();
|
||||||
|
|
||||||
let same_statement = if let Some(curr_span) = current_span {
|
let same_statement = if let Some(curr_span) = current_span {
|
||||||
|
|
@ -153,7 +153,7 @@ impl Rule for NoDuplicateImports {
|
||||||
if self.include_exports {
|
if self.include_exports {
|
||||||
for entry in &module_record.star_export_entries {
|
for entry in &module_record.star_export_entries {
|
||||||
if let Some(module_request) = &entry.module_request {
|
if let Some(module_request) = &entry.module_request {
|
||||||
let source = module_request.name();
|
let source = &module_request.name;
|
||||||
let span = entry.span;
|
let span = entry.span;
|
||||||
|
|
||||||
if entry.import_name.is_all_but_default() {
|
if entry.import_name.is_all_but_default() {
|
||||||
|
|
@ -208,7 +208,7 @@ impl Rule for NoDuplicateImports {
|
||||||
|
|
||||||
for entry in &module_record.indirect_export_entries {
|
for entry in &module_record.indirect_export_entries {
|
||||||
if let Some(module_request) = &entry.module_request {
|
if let Some(module_request) = &entry.module_request {
|
||||||
let source = module_request.name();
|
let source = &module_request.name;
|
||||||
let span = entry.span;
|
let span = entry.span;
|
||||||
|
|
||||||
if let Some(existing) = import_map.get(source) {
|
if let Some(existing) = import_map.get(source) {
|
||||||
|
|
|
||||||
|
|
@ -106,25 +106,25 @@ impl Rule for Named {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let import_span = import_name.span();
|
let import_span = import_name.span();
|
||||||
let import_name = import_name.name();
|
let name = import_name.name();
|
||||||
// Check `import { default as foo } from 'bar'`
|
// Check `import { default as foo } from 'bar'`
|
||||||
if import_name.as_str() == "default" && remote_module_record.export_default.is_some() {
|
if name == "default" && remote_module_record.export_default.is_some() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Check remote bindings
|
// Check remote bindings
|
||||||
if remote_module_record.exported_bindings.contains_key(import_name) {
|
if remote_module_record.exported_bindings.contains_key(name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// check re-export
|
// check re-export
|
||||||
if remote_module_record
|
if remote_module_record
|
||||||
.exported_bindings_from_star_export
|
.exported_bindings_from_star_export
|
||||||
.iter()
|
.iter()
|
||||||
.any(|entry| entry.value().contains(import_name))
|
.any(|entry| entry.value().contains(&import_name.name))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.diagnostic(named_diagnostic(import_name, specifier, import_span));
|
ctx.diagnostic(named_diagnostic(name, specifier, import_span));
|
||||||
}
|
}
|
||||||
|
|
||||||
for export_entry in &module_record.indirect_export_entries {
|
for export_entry in &module_record.indirect_export_entries {
|
||||||
|
|
@ -146,7 +146,7 @@ impl Rule for Named {
|
||||||
// Check remote bindings
|
// Check remote bindings
|
||||||
let name = import_name.name();
|
let name = import_name.name();
|
||||||
// `export { default as foo } from './source'` <> `export default xxx`
|
// `export { default as foo } from './source'` <> `export default xxx`
|
||||||
if *name == "default" && remote_module_record.export_default.is_some() {
|
if name == "default" && remote_module_record.export_default.is_some() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if remote_module_record.exported_bindings.contains_key(name) {
|
if remote_module_record.exported_bindings.contains_key(name) {
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,7 @@ impl Rule for Namespace {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(symbol_id) = ctx.scopes().get_root_binding(entry.local_name.name().as_str())
|
let Some(symbol_id) = ctx.scopes().get_root_binding(entry.local_name.name()) else {
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -235,16 +234,15 @@ fn get_module_request_name(name: &str, module_record: &ModuleRecord) -> Option<S
|
||||||
module_record.indirect_export_entries.iter().find(|e| match &e.import_name {
|
module_record.indirect_export_entries.iter().find(|e| match &e.import_name {
|
||||||
ExportImportName::All => {
|
ExportImportName::All => {
|
||||||
if let ExportExportName::Name(name_span) = &e.export_name {
|
if let ExportExportName::Name(name_span) = &e.export_name {
|
||||||
return name_span.name().as_str() == name;
|
return name_span.name() == name;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
ExportImportName::Name(name_span) => {
|
ExportImportName::Name(name_span) => {
|
||||||
name_span.name().as_str() == name
|
name_span.name() == name
|
||||||
&& module_record.import_entries.iter().any(|entry| {
|
&& module_record.import_entries.iter().any(|entry| {
|
||||||
entry.local_name.name().as_str() == name
|
entry.local_name.name() == name && entry.import_name.is_namespace_object()
|
||||||
&& entry.import_name.is_namespace_object()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|
@ -256,9 +254,7 @@ fn get_module_request_name(name: &str, module_record: &ModuleRecord) -> Option<S
|
||||||
module_record
|
module_record
|
||||||
.import_entries
|
.import_entries
|
||||||
.iter()
|
.iter()
|
||||||
.find(|entry| {
|
.find(|entry| entry.local_name.name() == name && entry.import_name.is_namespace_object())
|
||||||
entry.local_name.name().as_str() == name && entry.import_name.is_namespace_object()
|
|
||||||
})
|
|
||||||
.map(|entry| entry.module_request.name().to_string())
|
.map(|entry| entry.module_request.name().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,16 +90,13 @@ impl Rule for NoNamedAsDefaultMember {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(symbol_id) =
|
let Some(symbol_id) = ctx.scopes().get_root_binding(import_entry.local_name.name())
|
||||||
ctx.scopes().get_root_binding(import_entry.local_name.name().as_str())
|
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
has_members_map.insert(
|
has_members_map
|
||||||
symbol_id,
|
.insert(symbol_id, (remote_module_record_ref, import_entry.module_request.clone()));
|
||||||
(remote_module_record_ref, import_entry.module_request.name().clone()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_members_map.is_empty() {
|
if has_members_map.is_empty() {
|
||||||
|
|
@ -134,7 +131,7 @@ impl Rule for NoNamedAsDefaultMember {
|
||||||
},
|
},
|
||||||
&ident.name,
|
&ident.name,
|
||||||
prop_str,
|
prop_str,
|
||||||
module_name,
|
module_name.name(),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -161,7 +158,7 @@ impl Rule for NoNamedAsDefaultMember {
|
||||||
decl.span,
|
decl.span,
|
||||||
&ident.name,
|
&ident.name,
|
||||||
&name,
|
&name,
|
||||||
module_name,
|
module_name.name(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ impl Rule for NoMocksImport {
|
||||||
let module_records = ctx.module_record();
|
let module_records = ctx.module_record();
|
||||||
|
|
||||||
for import_entry in &module_records.import_entries {
|
for import_entry in &module_records.import_entries {
|
||||||
let module_specifier = import_entry.module_request.name().as_str();
|
let module_specifier = import_entry.module_request.name();
|
||||||
if contains_mocks_dir(module_specifier) {
|
if contains_mocks_dir(module_specifier) {
|
||||||
ctx.diagnostic(no_mocks_import_diagnostic(import_entry.module_request.span()));
|
ctx.diagnostic(no_mocks_import_diagnostic(import_entry.module_request.span()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ impl Rule for NoBeforeInteractiveScriptOutsideDocument {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let next_script_import_local_name = get_next_script_import_local_name(ctx);
|
let next_script_import_local_name = get_next_script_import_local_name(ctx);
|
||||||
if !matches!(next_script_import_local_name, Some(import) if tag_name.as_str() == import.as_str())
|
if !matches!(next_script_import_local_name, Some(import) if tag_name == import)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,12 @@ fn is_inside_export_default(node: &AstNode<'_>, ctx: &LintContext<'_>) -> bool {
|
||||||
let Some(name) = name else {
|
let Some(name) = name else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if ctx.module_record().local_export_entries.iter().any(|e| {
|
if ctx
|
||||||
e.local_name.is_default()
|
.module_record()
|
||||||
&& e.local_name.name().is_some_and(|n| n.as_str() == name.as_str())
|
.local_export_entries
|
||||||
}) {
|
.iter()
|
||||||
|
.any(|e| e.local_name.is_default() && e.local_name.name().is_some_and(|n| n == name))
|
||||||
|
{
|
||||||
is_inside_export_default = true;
|
is_inside_export_default = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,7 @@ impl Rule for NoUnwantedPolyfillio {
|
||||||
|
|
||||||
if tag_name.as_str() != "script" {
|
if tag_name.as_str() != "script" {
|
||||||
let next_script_import_local_name = get_next_script_import_local_name(ctx);
|
let next_script_import_local_name = get_next_script_import_local_name(ctx);
|
||||||
if !matches!(next_script_import_local_name, Some(import) if tag_name.as_str() == import.as_str())
|
if !matches!(next_script_import_local_name, Some(import) if tag_name == import) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,8 @@ pub fn import_matcher<'a>(
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let expected_module_name = expected_module_name.cow_to_lowercase();
|
let expected_module_name = expected_module_name.cow_to_lowercase();
|
||||||
ctx.module_record().import_entries.iter().any(|import| {
|
ctx.module_record().import_entries.iter().any(|import| {
|
||||||
import.module_request.name().as_str() == expected_module_name
|
import.module_request.name() == expected_module_name
|
||||||
&& import.local_name.name().as_str() == actual_local_name
|
&& import.local_name.name() == actual_local_name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ fn is_inside_process_event_handler(ctx: &LintContext, node: &AstNode) -> bool {
|
||||||
|
|
||||||
fn is_worker_threads_imported(ctx: &LintContext) -> bool {
|
fn is_worker_threads_imported(ctx: &LintContext) -> bool {
|
||||||
ctx.module_record().import_entries.iter().any(|entry| {
|
ctx.module_record().import_entries.iter().any(|entry| {
|
||||||
matches!(entry.module_request.name().as_str(), "worker_threads" | "node:worker_threads")
|
matches!(entry.module_request.name(), "worker_threads" | "node:worker_threads")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use oxc_span::CompactStr;
|
|
||||||
|
|
||||||
use crate::LintContext;
|
use crate::LintContext;
|
||||||
|
|
||||||
pub fn is_in_app_dir(file_path: &str) -> bool {
|
pub fn is_in_app_dir(file_path: &str) -> bool {
|
||||||
|
|
@ -13,9 +11,9 @@ pub fn is_document_page(file_path: &str) -> bool {
|
||||||
page.starts_with("/_document") || page.starts_with("\\_document")
|
page.starts_with("/_document") || page.starts_with("\\_document")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_next_script_import_local_name<'a>(ctx: &'a LintContext) -> Option<&'a CompactStr> {
|
pub fn get_next_script_import_local_name<'a>(ctx: &'a LintContext) -> Option<&'a str> {
|
||||||
ctx.module_record().import_entries.iter().find_map(|entry| {
|
ctx.module_record().import_entries.iter().find_map(|entry| {
|
||||||
if entry.module_request.name().as_str() == "next/script" {
|
if entry.module_request.name() == "next/script" {
|
||||||
Some(entry.local_name.name())
|
Some(entry.local_name.name())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -184,18 +184,18 @@ impl PartialEq<CompactStr> for str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq<str> for CompactStr {
|
|
||||||
fn eq(&self, other: &str) -> bool {
|
|
||||||
self.as_str() == other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<CompactStr> for Cow<'_, str> {
|
impl PartialEq<CompactStr> for Cow<'_, str> {
|
||||||
fn eq(&self, other: &CompactStr) -> bool {
|
fn eq(&self, other: &CompactStr) -> bool {
|
||||||
self.as_ref() == other.as_str()
|
self.as_ref() == other.as_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<str> for CompactStr {
|
||||||
|
fn eq(&self, other: &str) -> bool {
|
||||||
|
self.as_str() == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Index<Span> for CompactStr {
|
impl Index<Span> for CompactStr {
|
||||||
type Output = str;
|
type Output = str;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue