mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(linter): handle cjs `module.exports = {} as default export (#2493)
This commit is contained in:
parent
f64c7e04a3
commit
f5aadc767f
4 changed files with 36 additions and 30 deletions
|
|
@ -665,11 +665,7 @@ impl<'a> MemberExpression<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn through_optional_is_specific_member_access(
|
||||
&'a self,
|
||||
object: &str,
|
||||
property: &str,
|
||||
) -> bool {
|
||||
pub fn through_optional_is_specific_member_access(&self, object: &str, property: &str) -> bool {
|
||||
let object_matches = match self.object().without_parenthesized() {
|
||||
Expression::ChainExpression(x) => match &x.expression {
|
||||
ChainElement::CallExpression(_) => false,
|
||||
|
|
@ -686,7 +682,7 @@ impl<'a> MemberExpression<'a> {
|
|||
}
|
||||
|
||||
/// Whether it is a static member access `object.property`
|
||||
pub fn is_specific_member_access(&'a self, object: &str, property: &str) -> bool {
|
||||
pub fn is_specific_member_access(&self, object: &str, property: &str) -> bool {
|
||||
self.object().is_specific_id(object)
|
||||
&& self.static_property_name().is_some_and(|p| p == property)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ impl Rule for Default {
|
|||
fn run_once(&self, ctx: &LintContext<'_>) {
|
||||
let module_record = ctx.semantic().module_record();
|
||||
for import_entry in &module_record.import_entries {
|
||||
let ImportImportName::Default(_) = import_entry.import_name else { continue };
|
||||
let ImportImportName::Default(default_span) = import_entry.import_name else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let specifier = import_entry.module_request.name();
|
||||
let Some(remote_module_record_ref) = module_record.loaded_modules.get(specifier) else {
|
||||
|
|
@ -49,10 +51,7 @@ impl Rule for Default {
|
|||
if remote_module_record_ref.export_default.is_none()
|
||||
&& !remote_module_record_ref.exported_bindings.contains_key("default")
|
||||
{
|
||||
ctx.diagnostic(DefaultDiagnostic(
|
||||
specifier.to_string(),
|
||||
import_entry.module_request.span(),
|
||||
));
|
||||
ctx.diagnostic(DefaultDiagnostic(specifier.to_string(), default_span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -72,9 +71,7 @@ fn test() {
|
|||
r#"import CoolClass from "./default-class""#,
|
||||
r#"import bar, { baz } from "./default-export""#,
|
||||
r#"import crypto from "crypto""#,
|
||||
// TODO: module.exports
|
||||
// r#"import common from "./common""#,
|
||||
|
||||
r#"import common from "./common""#,
|
||||
// No longer valid syntax
|
||||
// r#"export bar from "./bar""#,
|
||||
// r#"export bar, { foo } from "./bar""#,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ source: crates/oxc_linter/src/tester.rs
|
|||
expression: default
|
||||
---
|
||||
⚠ eslint-plugin-import(default): No default export found in imported module "./named-exports"
|
||||
╭─[index.js:1:17]
|
||||
╭─[index.js:1:8]
|
||||
1 │ import baz from "./named-exports"
|
||||
· ─────────────────
|
||||
· ───
|
||||
╰────
|
||||
help: does "./named-exports" have the default export?
|
||||
|
||||
|
|
@ -28,15 +28,15 @@ expression: default
|
|||
╰────
|
||||
|
||||
⚠ eslint-plugin-import(default): No default export found in imported module "./re-export"
|
||||
╭─[index.js:1:24]
|
||||
╭─[index.js:1:8]
|
||||
1 │ import barDefault from "./re-export"
|
||||
· ─────────────
|
||||
· ──────────
|
||||
╰────
|
||||
help: does "./re-export" have the default export?
|
||||
|
||||
⚠ eslint-plugin-import(default): No default export found in imported module "./typescript"
|
||||
╭─[index.js:1:20]
|
||||
╭─[index.js:1:8]
|
||||
1 │ import foobar from "./typescript"
|
||||
· ──────────────
|
||||
· ──────
|
||||
╰────
|
||||
help: does "./typescript" have the default export?
|
||||
|
|
|
|||
|
|
@ -367,21 +367,34 @@ impl ModuleRecordBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
// Add export binding `foo` of `
|
||||
// * exports.foo = bar`
|
||||
// * module.exports.foo = bar`
|
||||
// Add export binding for
|
||||
// * exports.foo = bar
|
||||
// * module.exports.foo = bar
|
||||
// * module.exports = foo
|
||||
fn handle_cjs_export(&mut self, expr: &Expression) {
|
||||
let Expression::AssignmentExpression(assign_expr) = expr else { return };
|
||||
let AssignmentTarget::SimpleAssignmentTarget(target) = &assign_expr.left else { return };
|
||||
let SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) = target else { return };
|
||||
match member_expr.object() {
|
||||
Expression::Identifier(ident) if ident.name == "exports" => {}
|
||||
Expression::MemberExpression(member_expr)
|
||||
if matches!(member_expr.object(), Expression::Identifier(ident) if ident.name == "module")
|
||||
&& member_expr.static_property_name() == Some("exports") => {}
|
||||
_ => return,
|
||||
// exports.foo = bar
|
||||
Expression::Identifier(ident) if ident.name == "exports" => {
|
||||
let Some((span, name)) = member_expr.static_property_info() else { return };
|
||||
self.add_export_binding(name.into(), span);
|
||||
}
|
||||
// module.exports = {}
|
||||
Expression::Identifier(_)
|
||||
if member_expr.is_specific_member_access("module", "exports") =>
|
||||
{
|
||||
self.add_default_export(assign_expr.right.span());
|
||||
}
|
||||
// module.exports.foo = bar
|
||||
Expression::MemberExpression(inner_member_expr)
|
||||
if inner_member_expr.is_specific_member_access("module", "exports") =>
|
||||
{
|
||||
let Some((span, name)) = member_expr.static_property_info() else { return };
|
||||
self.add_export_binding(name.into(), span);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let Some((span, name)) = member_expr.static_property_info() else { return };
|
||||
self.add_export_binding(name.into(), span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue