diff --git a/crates/oxc_linter/src/rules/import/default.rs b/crates/oxc_linter/src/rules/import/default.rs index 529ea1bc64797..e15a84d877a35 100644 --- a/crates/oxc_linter/src/rules/import/default.rs +++ b/crates/oxc_linter/src/rules/import/default.rs @@ -18,10 +18,19 @@ pub struct Default; declare_oxc_lint!( /// ### What it does /// - /// If a default import is requested, this rule will report if there is no default export in the imported module. + /// If a default import is requested, this rule will report if there is no + /// default export in the imported module. /// - /// ### Example + /// ### Why is this bad? /// + /// Using a default import when there is no default export can lead to + /// confusion and runtime errors. It can make the code harder to understand + /// and maintain, as it may suggest that a module has a default export + /// when it does not, leading to unexpected behavior. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: /// ```javascript /// // ./bar.js /// export function bar() { return null } @@ -29,6 +38,15 @@ declare_oxc_lint!( /// // ./foo.js /// import bar from './bar' // no default export found in ./bar /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// // ./bar.js + /// export default function bar() { return null } + /// + /// // ./foo.js + /// import { bar } from './bar' // correct usage of named import + /// ``` Default, correctness ); diff --git a/crates/oxc_linter/src/rules/import/export.rs b/crates/oxc_linter/src/rules/import/export.rs index 88636e1476fd1..db1edca415142 100644 --- a/crates/oxc_linter/src/rules/import/export.rs +++ b/crates/oxc_linter/src/rules/import/export.rs @@ -8,7 +8,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{context::LintContext, rule::Rule}; -fn no_named_export(span: Span, module_name: &str) -> OxcDiagnostic { +fn no_named_export(module_name: &str, span: Span) -> OxcDiagnostic { OxcDiagnostic::warn(format!("No named exports found in module '{module_name}'")) .with_label(span) } @@ -19,13 +19,29 @@ pub struct Export; declare_oxc_lint!( /// ### What it does + /// /// Reports funny business with exports, like repeated exports of names or defaults. /// - /// ### Example + /// ### Why is this bad? + /// + /// Having multiple exports of the same name can lead to ambiguity and confusion + /// in the codebase. It makes it difficult to track which export is being used + /// and can result in runtime errors if the wrong export is referenced. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: /// ```javascript /// let foo; /// export { foo }; // Multiple exports of name 'foo'. - /// export * from "./export-all" // export-all.js also export foo + /// export * from "./export-all"; // Conflicts if export-all.js also exports foo + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// let foo; + /// export { foo as foo1 }; // Renamed export to avoid conflict + /// export * from "./export-all"; // No conflict if export-all.js also exports foo /// ``` Export, nursery @@ -58,7 +74,7 @@ impl Rule for Export { ); if export_names.is_empty() { - ctx.diagnostic(no_named_export(module_request.span(), module_request.name())); + ctx.diagnostic(no_named_export(module_request.name(), module_request.span())); } else { all_export_names.insert(star_export_entry.span, export_names); } diff --git a/crates/oxc_linter/src/rules/import/max_dependencies.rs b/crates/oxc_linter/src/rules/import/max_dependencies.rs index de8f4feada138..17bf68e302df8 100644 --- a/crates/oxc_linter/src/rules/import/max_dependencies.rs +++ b/crates/oxc_linter/src/rules/import/max_dependencies.rs @@ -9,11 +9,11 @@ use crate::{context::LintContext, rule::Rule}; fn max_dependencies_diagnostic>>( message: S, - span1: Span, + span: Span, ) -> OxcDiagnostic { OxcDiagnostic::warn(message) .with_help("Reduce the number of dependencies in this file") - .with_label(span1) + .with_label(span) } /// @@ -47,19 +47,26 @@ declare_oxc_lint!( /// /// ### Why is this bad? /// - /// This is a useful rule because a module with too many dependencies is a code smell, and - /// usually indicates the module is doing too much and/or should be broken up into smaller - /// modules. + /// This is a useful rule because a module with too many dependencies is a code smell, + /// and usually indicates the module is doing too much and/or should be broken up into + /// smaller modules. /// - /// ### Example + /// ### Examples /// /// Given `{"max": 2}` + /// + /// Examples of **incorrect** code for this rule: /// ```javascript /// import a from './a'; /// import b from './b'; - /// import c from './c'; + /// import c from './c'; // Too many dependencies: 3 (max: 2) + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// import a from './a'; + /// import b from './b'; // Allowed: 2 dependencies (max: 2) /// ``` - MaxDependencies, pedantic, ); diff --git a/crates/oxc_linter/src/rules/import/named.rs b/crates/oxc_linter/src/rules/import/named.rs index fa4f3223be4dd..a1988ab3f88e5 100644 --- a/crates/oxc_linter/src/rules/import/named.rs +++ b/crates/oxc_linter/src/rules/import/named.rs @@ -35,15 +35,33 @@ declare_oxc_lint!( /// /// ### Why is this bad? /// - /// ### Example + /// Importing or exporting names that do not exist in the referenced module + /// can lead to runtime errors and confusion. It may suggest that certain + /// functionality is available when it is not, making the code harder to + /// maintain and understand. This rule helps ensure that your code + /// accurately reflects the available exports, improving reliability. + /// + /// ### Examples + /// /// Given /// ```js /// // ./foo.js - /// export const foo = "I'm so foo" + /// export const foo = "I'm so foo"; /// ``` /// - /// The following is considered valid: + /// Examples of **incorrect** code for this rule: + /// ```js + /// // ./baz.js + /// import { notFoo } from './foo' + /// + /// // ES7 proposal + /// export { notFoo as defNotBar } from './foo' + /// + /// // will follow 'jsnext:main', if available + /// import { dontCreateStore } from 'redux' + /// ``` /// + /// Examples of **correct** code for this rule: /// ```js /// // ./bar.js /// import { foo } from './foo' @@ -55,19 +73,6 @@ declare_oxc_lint!( /// // (import/ignore setting) /// import { SomeNonsenseThatDoesntExist } from 'react' /// ``` - /// - /// ...and the following are reported: - /// - /// ```js - /// // ./baz.js - /// import { notFoo } from './foo' - /// - /// // ES7 proposal - /// export { notFoo as defNotBar } from './foo' - /// - /// // will follow 'jsnext:main', if available - /// import { dontCreateStore } from 'redux' - /// ``` Named, correctness ); diff --git a/crates/oxc_linter/src/rules/import/namespace.rs b/crates/oxc_linter/src/rules/import/namespace.rs index eabcddb2baad6..5bf1ea134d4bc 100644 --- a/crates/oxc_linter/src/rules/import/namespace.rs +++ b/crates/oxc_linter/src/rules/import/namespace.rs @@ -50,12 +50,54 @@ pub struct Namespace { declare_oxc_lint!( /// ### What it does + /// /// Enforces names exist at the time they are dereferenced, when imported as /// a full namespace (i.e. `import * as foo from './foo'; foo.bar();` will /// report if bar is not exported by `./foo.`). Will report at the import /// declaration if there are no exported names found. Also, will report for /// computed references (i.e. `foo["bar"]()`). Reports on assignment to a /// member of an imported namespace. + /// + /// ### Why is this bad? + /// + /// Dereferencing a name that does not exist can lead to runtime errors and + /// unexpected behavior in your code. It makes the code less reliable and + /// harder to maintain, as it may not be clear which names are valid. This + /// rule helps ensure that all referenced names are defined, improving + /// the clarity and robustness of your code. + /// + /// ### Examples + /// + /// Given + /// ```javascript + /// // ./foo.js + /// export const bar = "I'm bar"; + /// ``` + /// + /// Examples of **incorrect** code for this rule: + /// ```javascript + /// // ./qux.js + /// import * as foo from './foo'; + /// foo.notExported(); // Error: notExported is not exported + /// + /// // Assignment to a member of an imported namespace + /// foo.bar = "new value"; // Error: bar cannot be reassigned + /// + /// // Computed reference to a non-existent export + /// const method = "notExported"; + /// foo[method](); // Error: notExported does not exist + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// // ./baz.js + /// import * as foo from './foo'; + /// console.log(foo.bar); // Valid: bar is exported + /// + /// // Computed reference + /// const method = "bar"; + /// foo[method](); // Valid: method refers to an exported function + /// ``` Namespace, correctness ); diff --git a/crates/oxc_linter/src/rules/import/no_amd.rs b/crates/oxc_linter/src/rules/import/no_amd.rs index 7c2b7d15ffc44..417bb40c2226d 100644 --- a/crates/oxc_linter/src/rules/import/no_amd.rs +++ b/crates/oxc_linter/src/rules/import/no_amd.rs @@ -20,14 +20,20 @@ pub struct NoAmd; declare_oxc_lint!( /// ### What it does /// - /// Forbid AMD `require` and `define` calls. + /// Forbids the use of AMD `require` and `define` calls. /// /// ### Why is this bad? /// + /// AMD (Asynchronous Module Definition) is an older module format + /// that is less common in modern JavaScript development, especially + /// with the widespread use of ES6 modules and CommonJS in Node.js. + /// AMD introduces unnecessary complexity and is often considered outdated. + /// This rule enforces the use of more modern module systems to improve + /// maintainability and consistency across the codebase. + /// /// ### Examples /// /// Examples of **incorrect** code for this rule: - /// /// ```javascript /// require([a, b], function() {} ); /// ``` diff --git a/crates/oxc_linter/src/rules/import/no_cycle.rs b/crates/oxc_linter/src/rules/import/no_cycle.rs index 7e5e4317b0837..1a8bd8d40cb2d 100644 --- a/crates/oxc_linter/src/rules/import/no_cycle.rs +++ b/crates/oxc_linter/src/rules/import/no_cycle.rs @@ -55,20 +55,37 @@ declare_oxc_lint!( /// ### Why is this bad? /// /// Dependency cycles lead to confusing architectures where bugs become hard to find. - /// /// It is common to import an `undefined` value that is caused by a cyclic dependency. /// - /// ### Example + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: /// ```javascript /// // dep-b.js /// import './dep-a.js' /// export function b() { /* ... */ } /// ``` - /// /// ```javascript /// // dep-a.js /// import { b } from './dep-b.js' // reported: Dependency cycle detected. + /// export function a() { /* ... */ } /// ``` + /// + /// In this example, `dep-a.js` and `dep-b.js` import each other, creating a circular + /// dependency, which is problematic. + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// // dep-b.js + /// export function b() { /* ... */ } + /// ``` + /// ```javascript + /// // dep-a.js + /// import { b } from './dep-b.js' // no circular dependency + /// export function a() { /* ... */ } + /// ``` + /// + /// In this corrected version, `dep-b.js` no longer imports `dep-a.js`, breaking the cycle. NoCycle, restriction ); diff --git a/crates/oxc_linter/src/rules/import/no_default_export.rs b/crates/oxc_linter/src/rules/import/no_default_export.rs index 6bb8b4edeab8d..581101ba03c8f 100644 --- a/crates/oxc_linter/src/rules/import/no_default_export.rs +++ b/crates/oxc_linter/src/rules/import/no_default_export.rs @@ -14,7 +14,15 @@ pub struct NoDefaultExport; declare_oxc_lint!( /// ### What it does /// - /// Forbid a module to have a default exports. This help your editor to provide better auto imports. + /// Forbids a module from having default exports. This helps your editor + /// provide better auto-import functionality, as named exports offer more + /// explicit and predictable imports compared to default exports. + /// + /// ### Why is this bad? + /// + /// Default exports can lead to confusion, as the name of the imported value + /// can vary based on how it's imported. This can make refactoring and + /// auto-imports less reliable. /// /// ### Examples /// @@ -31,7 +39,6 @@ declare_oxc_lint!( /// export const foo = 'foo'; /// export const bar = 'bar'; /// ``` - /// NoDefaultExport, restriction ); @@ -48,6 +55,7 @@ impl Rule for NoDefaultExport { fn write_diagnostic(ctx: &LintContext<'_>, span: Span) { ctx.diagnostic(no_default_export_diagnostic(span)); } + fn write_diagnostic_optional(ctx: &LintContext<'_>, span_option: Option) { if let Some(span) = span_option { write_diagnostic(ctx, span); @@ -77,6 +85,7 @@ fn test() { "import {default as foo} from './foo';", "export type UserId = number;", ]; + let fail = vec![ "export default function bar() {};", "export const foo = 'foo';\nexport default bar;", diff --git a/crates/oxc_linter/src/rules/import/no_deprecated.rs b/crates/oxc_linter/src/rules/import/no_deprecated.rs index 10aa4b2bdd790..ee4020527c41f 100644 --- a/crates/oxc_linter/src/rules/import/no_deprecated.rs +++ b/crates/oxc_linter/src/rules/import/no_deprecated.rs @@ -2,7 +2,10 @@ use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; use oxc_macros::declare_oxc_lint; // use oxc_span::{CompactStr, Span}; -use crate::{context::{LintContext, ContextHost}, rule::Rule}; +use crate::{ + context::{ContextHost, LintContext}, + rule::Rule, +}; // #[derive(Debug, Error, Diagnostic)] // #[error("")] @@ -16,7 +19,20 @@ pub struct NoDeprecated; declare_oxc_lint!( /// ### What it does /// - /// Reports use of a deprecated name, as indicated by a JSDoc block with a @deprecated tag or TomDoc Deprecated: comment. + /// Reports use of a deprecated name, as indicated by a JSDoc block with + /// a @deprecated tag or TomDoc Deprecated: comment. + /// + /// ### Why is this bad? + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```javascript + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// ``` NoDeprecated, nursery ); diff --git a/crates/oxc_linter/src/rules/import/no_duplicates.rs b/crates/oxc_linter/src/rules/import/no_duplicates.rs index 0752279ee7336..525a3ce56d584 100644 --- a/crates/oxc_linter/src/rules/import/no_duplicates.rs +++ b/crates/oxc_linter/src/rules/import/no_duplicates.rs @@ -14,7 +14,27 @@ pub struct NoDuplicates { declare_oxc_lint!( /// ### What it does /// - /// Reports if a resolved path is imported more than once. + /// Reports if a resolved path is imported more than once in the same module. + /// This helps avoid unnecessary duplicate imports and keeps the code clean. + /// + /// ### Why is this bad? + /// + /// Importing the same module multiple times can lead to redundancy and + /// unnecessary complexity. It also affects maintainability, as it might + /// confuse developers and result in inconsistent usage of imports across the code. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```javascript + /// import { foo } from './module'; + /// import { bar } from './module'; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// import { foo, bar } from './module'; + /// ``` NoDuplicates, suspicious ); diff --git a/crates/oxc_linter/src/rules/import/no_dynamic_require.rs b/crates/oxc_linter/src/rules/import/no_dynamic_require.rs index 579af30e2b36f..3e98c50020f16 100644 --- a/crates/oxc_linter/src/rules/import/no_dynamic_require.rs +++ b/crates/oxc_linter/src/rules/import/no_dynamic_require.rs @@ -19,12 +19,15 @@ pub struct NoDynamicRequire { declare_oxc_lint!( /// ### What it does /// - /// Forbid imports which use an expression for the module argument. + /// Forbids imports that use an expression for the module argument. This includes + /// dynamically resolved paths in `require` or `import` statements. /// /// ### Why is this bad? /// - /// Import statements which use an expression resolved at runtime makes it to find where the - /// import comes from and some static code analysis tools might not be able to resolve them. + /// Using expressions that are resolved at runtime in import statements makes it + /// difficult to determine where the module is being imported from. This can complicate + /// code navigation and hinder static analysis tools, which rely on predictable module paths + /// for linting, bundling, and other optimizations. /// /// ### Examples /// diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default.rs b/crates/oxc_linter/src/rules/import/no_named_as_default.rs index 36563c0599ba6..a6e352057775b 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default.rs @@ -23,22 +23,36 @@ declare_oxc_lint!( /// ### What it does /// /// Reports use of an exported name as the locally imported name of a default export. + /// This happens when an imported default export is assigned a name that conflicts + /// with a named export from the same module. /// - /// ### Example + /// ### Why is this bad? /// + /// Using a named export's identifier for a default export can cause confusion + /// and errors in understanding which value is being imported. It also reduces + /// code clarity, making it harder for other developers to understand the intended + /// imports. + /// + /// + /// ### Examples + /// + /// Given /// ```javascript /// // foo.js /// export default 'foo'; /// export const bar = 'baz'; /// ``` - /// Valid: + /// + /// Examples of **incorrect** code for this rule: /// ```javascript - /// import foo from './foo.js'; + /// // Invalid: using exported name 'bar' as the identifier for default export. + /// import bar from './foo.js'; /// ``` - /// Invalid: + /// + /// Examples of **correct** code for this rule: /// ```javascript - /// // using exported name 'bar' as identifier for default export. - /// import bar from './foo.js'; + /// // Valid: correctly importing default export with a non-conflicting name. + /// import foo from './foo.js'; /// ``` NoNamedAsDefault, suspicious diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs index b54259d16b341..d4fca4c37e5c9 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs @@ -29,18 +29,37 @@ pub struct NoNamedAsDefaultMember; declare_oxc_lint!( /// ### What it does /// - /// Reports use of an exported name as a property on the default export. + /// Reports the use of an exported name (named export) as a property on the + /// default export. This occurs when trying to access a named export through + /// the default export, which is incorrect. /// - /// ### Example + /// ### Why is this bad? /// + /// Accessing a named export via the default export is incorrect and will not + /// work as expected. Named exports should be imported directly, while default + /// exports are accessed without properties. This mistake can lead to runtime + /// errors or undefined behavior. + /// + /// ### Examples + /// + /// Given /// ```javascript /// // ./bar.js /// export function bar() { return null } /// export default () => { return 1 } + /// ``` + /// + /// Examples of **incorrect** code for this rule: + /// ```javascript + /// // ./foo.js + /// import foo from './bar' + /// const bar = foo.bar; // Incorrect: trying to access named export via default + /// ``` /// + /// Examples of **correct** code for this rule: + /// ```javascript /// // ./foo.js - /// import bar from './bar' - /// const bar = foo.bar // trying to access named export via default + /// import { bar } from './bar'; // Correct: accessing named export directly /// ``` NoNamedAsDefaultMember, suspicious diff --git a/crates/oxc_linter/src/rules/import/no_self_import.rs b/crates/oxc_linter/src/rules/import/no_self_import.rs index a58ce5bd3c25c..8b924b4df7c83 100644 --- a/crates/oxc_linter/src/rules/import/no_self_import.rs +++ b/crates/oxc_linter/src/rules/import/no_self_import.rs @@ -14,14 +14,27 @@ pub struct NoSelfImport; declare_oxc_lint!( /// ### What it does /// - /// Forbid a module from importing itself. This can sometimes happen during refactoring. + /// Forbids a module from importing itself. This can sometimes happen accidentally, + /// especially during refactoring. /// - /// ### Example + /// ### Why is this bad? /// + /// Importing a module into itself creates a circular dependency, which can cause + /// runtime issues, including infinite loops, unresolved imports, or `undefined` values. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```javascript + /// // foo.js + /// import foo from './foo.js'; // Incorrect: module imports itself + /// const foo = require('./foo'); // Incorrect: module imports itself + /// ``` + /// + /// Examples of **correct** code for this rule: /// ```javascript /// // foo.js - /// import foo from './foo.js' - /// const foo = require('./foo') + /// import bar from './bar.js'; // Correct: module imports another module /// ``` NoSelfImport, suspicious diff --git a/crates/oxc_linter/src/rules/import/no_unused_modules.rs b/crates/oxc_linter/src/rules/import/no_unused_modules.rs index 8bf030d1153aa..8a4dd3f19a5a5 100644 --- a/crates/oxc_linter/src/rules/import/no_unused_modules.rs +++ b/crates/oxc_linter/src/rules/import/no_unused_modules.rs @@ -1,11 +1,13 @@ use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use crate::{context::{LintContext, ContextHost}, rule::Rule}; +use crate::{ + context::{ContextHost, LintContext}, + rule::Rule, +}; fn no_exports_found(span: Span) -> OxcDiagnostic { - OxcDiagnostic::warn("No exports found") - .with_label(span) + OxcDiagnostic::warn("No exports found").with_label(span) } /// @@ -23,6 +25,17 @@ declare_oxc_lint!( /// * individual exports not being statically imported or requireed from other modules in the same project /// * dynamic imports are supported if argument is a literal string /// + /// ### Why is this bad? + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```javascript + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// ``` NoUnusedModules, nursery ); diff --git a/crates/oxc_linter/src/rules/import/no_webpack_loader_syntax.rs b/crates/oxc_linter/src/rules/import/no_webpack_loader_syntax.rs index f81b92db1fffe..df2a0d6d73fb3 100644 --- a/crates/oxc_linter/src/rules/import/no_webpack_loader_syntax.rs +++ b/crates/oxc_linter/src/rules/import/no_webpack_loader_syntax.rs @@ -21,14 +21,16 @@ pub struct NoWebpackLoaderSyntax; declare_oxc_lint!( /// ### What it does /// - /// Forbid Webpack loader syntax in imports. + /// Forbids using Webpack loader syntax directly in import or require statements. /// /// ### Why is this bad? /// /// This loader syntax is non-standard, so it couples the code to Webpack. The recommended way to /// specify Webpack loader configuration is in a [Webpack configuration file](https://webpack.js.org/concepts/loaders/#configuration). /// - /// ### Example + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: /// ```javascript /// import myModule from 'my-loader!my-module'; /// import theme from 'style!css!./theme.css'; @@ -36,6 +38,15 @@ declare_oxc_lint!( /// var myModule = require('my-loader!./my-module'); /// var theme = require('style!css!./theme.css'); /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// import myModule from './my-module'; + /// import theme from './theme.css'; + /// + /// var myModule = require('./my-module'); + /// var theme = require('./theme.css'); + /// ``` NoWebpackLoaderSyntax, restriction, );