mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(linter): remove all commonjs logic for import plugin (#2537)
This commit is contained in:
parent
3efbbb2e1f
commit
d41dcc316e
11 changed files with 58 additions and 186 deletions
|
|
@ -1,2 +0,0 @@
|
|||
module.exports.foo = 1;
|
||||
exports.bar = 1;
|
||||
|
|
@ -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")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}])),
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"?
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
· ───────
|
||||
╰────
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue