feat(linter): remove all commonjs logic for import plugin (#2537)

This commit is contained in:
Boshen 2024-02-28 18:13:44 +08:00 committed by GitHub
parent 3efbbb2e1f
commit d41dcc316e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 58 additions and 186 deletions

View file

@ -1,2 +0,0 @@
module.exports.foo = 1;
exports.bar = 1;

View file

@ -47,7 +47,9 @@ impl Rule for Default {
let Some(remote_module_record_ref) = module_record.loaded_modules.get(specifier) else {
continue;
};
if remote_module_record_ref.not_esm {
continue;
}
if remote_module_record_ref.export_default.is_none()
&& !remote_module_record_ref.exported_bindings.contains_key("default")
{

View file

@ -51,6 +51,9 @@ impl Rule for Named {
continue;
};
let remote_module_record = remote_module_record_ref.value();
if remote_module_record.not_esm {
continue;
}
// Check remote bindings
if remote_module_record.exported_bindings.contains_key(import_name.name()) {
continue;
@ -164,8 +167,6 @@ fn test() {
// "import { foo } from './export-all'",
// TypeScript export assignment
"import x from './typescript-export-assign-object'",
// oxc
"import { foo, bar } from './oxc_named'",
];
let fail = vec![
@ -200,8 +201,6 @@ fn test() {
// Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.
"import { NotExported } from './typescript-export-assign-object'",
"import { FooBar } from './typescript-export-assign-object'",
// oxc
"import { baz, quaz } from './oxc_named'",
];
Tester::new(Named::NAME, pass, fail)

View file

@ -220,14 +220,14 @@ fn test() {
// (r#"import { foo } from "./external-depth-two""#, None),
// (r#"import { foo } from "./es6/depth-one""#, None),
(r#"import { foo } from "./es6/depth-one""#, Some(json!([{"maxDepth":1}]))),
(r#"const { foo } = require("./es6/depth-one")"#, Some(json!([{"commonjs":true}]))),
// (r#"const { foo } = require("./es6/depth-one")"#, Some(json!([{"commonjs":true}]))),
// TODO: amd
// (r#"require(["./es6/depth-one"], d1 => {})"#, Some(json!([{"amd":true}]))),
// (r#"define(["./es6/depth-one"], d1 => {})"#, Some(json!([{"amd":true}]))),
(r#"import { foo } from "./es6/depth-one-reexport""#, None),
(r#"import { foo } from "./es6/depth-two""#, None),
(r#"import { foo } from "./es6/depth-two""#, Some(json!([{"maxDepth":2}]))),
(r#"const { foo } = require("./es6/depth-two")"#, Some(json!([{"commonjs":true}]))),
// (r#"const { foo } = require("./es6/depth-two")"#, Some(json!([{"commonjs":true}]))),
(r#"import { two } from "./es6/depth-three-star""#, None),
(r#"import one, { two, three } from "./es6/depth-three-star""#, None),
(r#"import { bar } from "./es6/depth-three-indirect""#, None),
@ -242,10 +242,10 @@ fn test() {
r#"import { foo } from "./es6/depth-one""#,
Some(json!([{"allowUnsafeDynamicCyclicDependency":true,"maxDepth":1}])),
),
(
r#"const { foo } = require("./es6/depth-one")"#,
Some(json!([{"allowUnsafeDynamicCyclicDependency":true,"commonjs":true}])),
),
// (
// r#"const { foo } = require("./es6/depth-one")"#,
// Some(json!([{"allowUnsafeDynamicCyclicDependency":true,"commonjs":true}])),
// ),
// TODO: amd
// (
// r#"require(["./es6/depth-one"], d1 => {})"#,
@ -267,10 +267,10 @@ fn test() {
r#"import { foo } from "./es6/depth-two""#,
Some(json!([{"allowUnsafeDynamicCyclicDependency":true,"maxDepth":2}])),
),
(
r#"const { foo } = require("./es6/depth-two")"#,
Some(json!([{"allowUnsafeDynamicCyclicDependency":true,"commonjs":true}])),
),
// (
// r#"const { foo } = require("./es6/depth-two")"#,
// Some(json!([{"allowUnsafeDynamicCyclicDependency":true,"commonjs":true}])),
// ),
(
r#"import { two } from "./es6/depth-three-star""#,
Some(json!([{"allowUnsafeDynamicCyclicDependency":true}])),

View file

@ -73,8 +73,8 @@ fn test() {
let fail = vec![
"import bar from './no-self-import'",
"var bar = require('./no-self-import')",
"var bar = require('./no-self-import.js')",
// "var bar = require('./no-self-import')",
// "var bar = require('./no-self-import.js')",
];
Tester::new(NoSelfImport::NAME, pass, fail)
@ -83,37 +83,37 @@ fn test() {
.test();
}
{
let pass = vec!["var bar = require('./bar')"];
let fail = vec![];
// {
// let pass = vec!["var bar = require('./bar')"];
// let fail = vec![];
Tester::new(NoSelfImport::NAME, pass, fail)
.with_import_plugin(true)
.change_rule_path("bar/index.js")
.test();
}
// Tester::new(NoSelfImport::NAME, pass, fail)
// .with_import_plugin(true)
// .change_rule_path("bar/index.js")
// .test();
// }
{
let pass = vec![];
let fail = vec![
"var bar = require('.')",
"var bar = require('./')",
"var bar = require('././././')",
];
// {
// let pass = vec![];
// let fail = vec![
// "var bar = require('.')",
// "var bar = require('./')",
// "var bar = require('././././')",
// ];
Tester::new(NoSelfImport::NAME, pass, fail)
.with_import_plugin(true)
.change_rule_path("index.js")
.test();
}
// Tester::new(NoSelfImport::NAME, pass, fail)
// .with_import_plugin(true)
// .change_rule_path("index.js")
// .test();
// }
{
let pass = vec![];
let fail = vec!["var bar = require('../no-self-import-folder')"];
// {
// let pass = vec![];
// let fail = vec!["var bar = require('../no-self-import-folder')"];
Tester::new(NoSelfImport::NAME, pass, fail)
.with_import_plugin(true)
.change_rule_path("no-self-import-folder/index.js")
.test();
}
// Tester::new(NoSelfImport::NAME, pass, fail)
// .with_import_plugin(true)
// .change_rule_path("no-self-import-folder/index.js")
// .test();
// }
}

View file

@ -117,7 +117,7 @@ fn test() {
// r#"import('in-alternate-root').then(function({DEEP}) {});"#,
r#"export * as bar from "./does-not-exist""#,
r#"export bar from "./does-not-exist""#,
r#"var bar = require("./baz")"#,
// r#"var bar = require("./baz")"#,
// TODO: require expression
// r#"require("./baz")"#,
// TODO: amd

View file

@ -140,17 +140,3 @@ expression: named
· ──────
╰────
help: does "./typescript-export-assign-object" have the export "FooBar"?
⚠ eslint-plugin-import(named): named import "baz" not found
╭─[index.js:1:10]
1 │ import { baz, quaz } from './oxc_named'
· ───
╰────
help: does "./oxc_named" have the export "baz"?
⚠ eslint-plugin-import(named): named import "quaz" not found
╭─[index.js:1:15]
1 │ import { baz, quaz } from './oxc_named'
· ────
╰────
help: does "./oxc_named" have the export "quaz"?

View file

@ -11,15 +11,6 @@ expression: no_cycle
-> ./es6/depth-one - fixtures/import/cycles/es6/depth-one.js
-> ../depth-zero - fixtures/import/cycles/depth-zero.js
⚠ eslint-plugin-import(no-cycle): Dependency cycle detected
╭─[cycles/depth-zero.js:1:25]
1 │ const { foo } = require("./es6/depth-one")
· ─────────────────
╰────
help: These paths form a cycle:
-> ./es6/depth-one - fixtures/import/cycles/es6/depth-one.js
-> ../depth-zero - fixtures/import/cycles/depth-zero.js
⚠ eslint-plugin-import(no-cycle): Dependency cycle detected
╭─[cycles/depth-zero.js:1:21]
1 │ import { foo } from "./es6/depth-one-reexport"
@ -49,16 +40,6 @@ expression: no_cycle
-> ./depth-one - fixtures/import/cycles/es6/depth-one.js
-> ../depth-zero - fixtures/import/cycles/depth-zero.js
⚠ eslint-plugin-import(no-cycle): Dependency cycle detected
╭─[cycles/depth-zero.js:1:25]
1 │ const { foo } = require("./es6/depth-two")
· ─────────────────
╰────
help: These paths form a cycle:
-> ./es6/depth-two - fixtures/import/cycles/es6/depth-two.js
-> ./depth-one - fixtures/import/cycles/es6/depth-one.js
-> ../depth-zero - fixtures/import/cycles/depth-zero.js
⚠ eslint-plugin-import(no-cycle): Dependency cycle detected
╭─[cycles/depth-zero.js:1:21]
1 │ import { two } from "./es6/depth-three-star"
@ -141,15 +122,6 @@ expression: no_cycle
-> ./es6/depth-one - fixtures/import/cycles/es6/depth-one.js
-> ../depth-zero - fixtures/import/cycles/depth-zero.js
⚠ eslint-plugin-import(no-cycle): Dependency cycle detected
╭─[cycles/depth-zero.js:1:25]
1 │ const { foo } = require("./es6/depth-one")
· ─────────────────
╰────
help: These paths form a cycle:
-> ./es6/depth-one - fixtures/import/cycles/es6/depth-one.js
-> ../depth-zero - fixtures/import/cycles/depth-zero.js
⚠ eslint-plugin-import(no-cycle): Dependency cycle detected
╭─[cycles/depth-zero.js:1:21]
1 │ import { foo } from "./es6/depth-one-reexport"
@ -179,16 +151,6 @@ expression: no_cycle
-> ./depth-one - fixtures/import/cycles/es6/depth-one.js
-> ../depth-zero - fixtures/import/cycles/depth-zero.js
⚠ eslint-plugin-import(no-cycle): Dependency cycle detected
╭─[cycles/depth-zero.js:1:25]
1 │ const { foo } = require("./es6/depth-two")
· ─────────────────
╰────
help: These paths form a cycle:
-> ./es6/depth-two - fixtures/import/cycles/es6/depth-two.js
-> ./depth-one - fixtures/import/cycles/es6/depth-one.js
-> ../depth-zero - fixtures/import/cycles/depth-zero.js
⚠ eslint-plugin-import(no-cycle): Dependency cycle detected
╭─[cycles/depth-zero.js:1:21]
1 │ import { two } from "./es6/depth-three-star"

View file

@ -55,9 +55,3 @@ expression: no_unresolved
1 │ export bar from "./does-not-exist"
· ───
╰────
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved
╭─[index.js:1:19]
1 │ var bar = require("./baz")
· ───────
╰────

View file

@ -18,19 +18,13 @@ impl ModuleRecordBuilder {
}
pub fn visit(&mut self, program: &Program) {
self.module_record.not_esm = true;
// This avoids additional checks on TypeScript `TsModuleBlock` which
// also has `ModuleDeclaration`s.
for stmt in &program.body {
match stmt {
Statement::ModuleDeclaration(module_decl) => {
self.visit_module_declaration(module_decl);
}
Statement::ExpressionStatement(expr_stmt) => {
self.handle_cjs_export(&expr_stmt.expression);
}
stmt => {
self.search_top_level_cjs_require(stmt);
}
if let Statement::ModuleDeclaration(module_decl) = stmt {
self.module_record.not_esm = false;
self.visit_module_declaration(module_decl);
}
}
@ -345,73 +339,4 @@ impl ModuleRecordBuilder {
);
}
}
/// Try to find require calls by searching all top-level statements
/// and add them to the module record
fn search_top_level_cjs_require(&mut self, stmt: &Statement) {
match stmt {
// var foo = require('bar');
Statement::Declaration(Declaration::VariableDeclaration(var_decl)) => {
for declaration in &var_decl.declarations {
if let Some(init) = &declaration.init {
self.handle_cjs_require(init);
}
}
}
// require('foo')
Statement::ExpressionStatement(expr_stmt) => {
self.handle_cjs_require(&expr_stmt.expression);
}
_ => {}
}
}
fn handle_cjs_require(&mut self, expr: &Expression) {
let Expression::CallExpression(call) = &expr else {
return;
};
let Expression::Identifier(ident) = &call.callee else {
return;
};
if ident.name != "require" {
return;
}
if let Some(Argument::Expression(Expression::StringLiteral(module))) =
call.arguments.first()
{
let module_request = NameSpan::new(module.value.to_compact_string(), module.span);
self.add_module_request(&module_request);
}
}
// 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() {
// 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);
}
_ => {}
}
}
}

View file

@ -8,13 +8,18 @@ use rustc_hash::{FxHashMap, FxHasher};
use oxc_span::{CompactString, Span};
/// Module Record
/// ESM Module Record
///
/// All data inside this data structure are for ESM, no commonjs data is allowed.
///
/// See
/// * <https://tc39.es/ecma262/#table-additional-fields-of-source-text-module-records>
/// * <https://tc39.es/ecma262/#cyclic-module-record>
#[derive(Default)]
pub struct ModuleRecord {
/// This module has no import / export statements
pub not_esm: bool,
/// Resolved absolute path to this module record
pub resolved_absolute_path: PathBuf,
@ -84,6 +89,7 @@ impl fmt::Debug for ModuleRecord {
.unwrap_or_default();
let loaded_modules = format!("{{ {loaded_modules} }}");
f.debug_struct("ModuleRecord")
.field("not_esm", &self.not_esm)
.field("resolved_absolute_path", &self.resolved_absolute_path)
.field("requested_modules", &self.requested_modules)
.field("loaded_modules", &loaded_modules)