Skip to content
44 changes: 15 additions & 29 deletions crates/oxc_linter/src/rules/eslint/curly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use oxc_ast::{AstKind, ast::IfStatement, ast::Statement};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;

fn curly_diagnostic(span: Span, keyword: &str, expected: bool) -> OxcDiagnostic {
Expand All @@ -15,7 +17,8 @@ fn curly_diagnostic(span: Span, keyword: &str, expected: bool) -> OxcDiagnostic
OxcDiagnostic::warn(message).with_label(span)
}

#[derive(Debug, Default, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
enum CurlyType {
#[default]
All,
Expand All @@ -38,9 +41,17 @@ impl CurlyType {
#[derive(Debug, Default, Clone)]
pub struct Curly(CurlyConfig);

#[derive(Debug, Clone)]
#[derive(Debug, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", default)]
pub struct CurlyConfig {
/// Which type of curly brace enforcement to use.
///
/// - `"all"`: require braces in all cases
/// - `"multi"`: require braces only for multi-statement blocks
/// - `"multi-line"`: require braces only for multi-line blocks
/// - `"multi-or-nest"`: require braces for multi-line blocks or when nested
curly_type: CurlyType,
/// Whether to enforce consistent use of curly braces in if-else chains.
consistent: bool,
}

Expand Down Expand Up @@ -70,32 +81,6 @@ declare_oxc_lint!(
/// It can also lead to bugs if additional statements are added later without properly enclosing them in braces.
/// Using curly braces consistently makes the code safer and easier to modify.
///
/// ### Options
///
/// First option:
/// - Type: `string`
/// - Default: `"all"`
/// - Possible values:
/// - `"all"`: require braces in all cases
/// - `"multi"`: require braces only for multi-statement blocks
/// - `"multi-line"`: require braces only for multi-line blocks
/// - `"multi-or-nest"`: require braces for multi-line blocks or when nested
///
/// Second option:
/// - Type: `string`
/// - Default: `undefined`
/// - Possible values:
/// - `"consistent"`: require braces if any other branch in the `if-else` chain has braces
///
/// Note : The second option can only be used in conjunction with the first option.
///
/// Example configuration:
/// ```json
/// {
/// "curly": ["error", "multi-or-nest", "consistent"]
/// }
/// ```
///
/// ### Examples
///
/// #### `"all"` (default)
Expand Down Expand Up @@ -272,7 +257,8 @@ declare_oxc_lint!(
Curly,
eslint,
style,
fix
fix,
config = CurlyConfig,
);

impl Rule for Curly {
Expand Down
145 changes: 68 additions & 77 deletions crates/oxc_linter/src/rules/promise/always_return.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use oxc_macros::declare_oxc_lint;
use oxc_semantic::NodeId;
use oxc_span::{GetSpan, Span};
use rustc_hash::FxHashSet;
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{AstNode, context::LintContext, rule::Rule};
Expand All @@ -27,81 +28,9 @@ fn always_return_diagnostic(span: Span) -> OxcDiagnostic {
#[derive(Debug, Default, Clone)]
pub struct AlwaysReturn(Box<AlwaysReturnConfig>);

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default)]
pub struct AlwaysReturnConfig {
#[serde(default)]
ignore_last_callback: bool,
#[serde(default)]
ignore_assignment_variable: FxHashSet<Cow<'static, str>>,
}

impl Default for AlwaysReturnConfig {
fn default() -> Self {
Self {
ignore_last_callback: false,
ignore_assignment_variable: FxHashSet::from_iter([Cow::Borrowed("globalThis")]),
}
}
}

impl std::ops::Deref for AlwaysReturn {
type Target = AlwaysReturnConfig;

fn deref(&self) -> &Self::Target {
&self.0
}
}

declare_oxc_lint!(
/// ### What it does
///
/// Require returning inside each `then()` to create readable and reusable Promise chains.
/// We also allow someone to throw inside a `then()` which is essentially the same as return `Promise.reject()`.
///
/// ### Why is this bad?
///
/// Broken Promise Chain.
/// Inside the first `then()` callback, a function is called but not returned.
/// This causes the next `then()` in the chain to execute immediately without waiting for the called function to complete.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```javascript
/// myPromise.then(function (val) {})
/// myPromise.then(() => {
/// doSomething()
/// })
/// myPromise.then((b) => {
/// if (b) {
/// return 'yes'
/// } else {
/// forgotToReturn()
/// }
/// })
/// ```
///
/// Examples of **correct** code for this rule:
/// ```javascript
/// myPromise.then((val) => val * 2)
/// myPromise.then(function (val) {
/// return val * 2
///})
/// myPromise.then(doSomething) // could be either
/// myPromise.then((b) => {
/// if (b) {
/// return 'yes'
/// } else {
/// return 'no'
/// }
/// })
/// ```
///
/// ### Options
///
/// #### `ignoreLastCallback`
///
/// You can pass an `{ ignoreLastCallback: true }` as an option to this rule so that
/// the last `then()` callback in a promise chain does not warn if it does not have
/// a `return`. Default is `false`.
Expand Down Expand Up @@ -145,9 +74,7 @@ declare_oxc_lint!(
/// })
/// }
/// ```
///
/// #### `ignoreAssignmentVariable`
///
ignore_last_callback: bool,
/// You can pass an `{ ignoreAssignmentVariable: [] }` as an option to this rule
/// with a list of variable names so that the last `then()` callback in a promise
/// chain does not warn if it does an assignment to a global variable. Default is
Expand Down Expand Up @@ -185,10 +112,74 @@ declare_oxc_lint!(
/// x()
/// })
/// ```
ignore_assignment_variable: FxHashSet<Cow<'static, str>>,
}

impl Default for AlwaysReturnConfig {
fn default() -> Self {
Self {
ignore_last_callback: false,
ignore_assignment_variable: FxHashSet::from_iter([Cow::Borrowed("globalThis")]),
}
}
}

impl std::ops::Deref for AlwaysReturn {
type Target = AlwaysReturnConfig;

fn deref(&self) -> &Self::Target {
&self.0
}
}

declare_oxc_lint!(
/// ### What it does
///
/// Require returning inside each `then()` to create readable and reusable Promise chains.
/// We also allow someone to throw inside a `then()` which is essentially the same as return `Promise.reject()`.
///
/// ### Why is this bad?
///
/// Broken Promise Chain.
/// Inside the first `then()` callback, a function is called but not returned.
/// This causes the next `then()` in the chain to execute immediately without waiting for the called function to complete.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```javascript
/// myPromise.then(function (val) {})
/// myPromise.then(() => {
/// doSomething()
/// })
/// myPromise.then((b) => {
/// if (b) {
/// return 'yes'
/// } else {
/// forgotToReturn()
/// }
/// })
/// ```
///
/// Examples of **correct** code for this rule:
/// ```javascript
/// myPromise.then((val) => val * 2)
/// myPromise.then(function (val) {
/// return val * 2
/// })
/// myPromise.then(doSomething) // could be either
/// myPromise.then((b) => {
/// if (b) {
/// return 'yes'
/// } else {
/// return 'no'
/// }
/// })
/// ```
AlwaysReturn,
promise,
suspicious,
config = AlwaysReturnConfig,
);

const PROCESS_METHODS: [&str; 2] = ["exit", "abort"];
Expand Down
23 changes: 6 additions & 17 deletions crates/oxc_linter/src/rules/react/exhaustive_deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{borrow::Cow, hash::Hash};
use itertools::Itertools;
use lazy_regex::Regex;
use rustc_hash::FxHashSet;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use oxc_ast::{
Expand Down Expand Up @@ -207,9 +208,10 @@ pub struct ExhaustiveDepsConfig {
additional_hooks: Option<Regex>,
}

#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Default, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", default)]
struct ExhaustiveDepsConfigJson {
#[serde(rename = "additionalHooks")]
/// Optionally provide a regex of additional hooks to check.
additional_hooks: Option<String>,
}

Expand Down Expand Up @@ -244,24 +246,11 @@ declare_oxc_lint!(
/// return <div />;
/// }
/// ```
///
/// ### Options
///
/// #### additionalHooks
///
/// `{ type: string }`
///
/// Optionally provide a regex of additional hooks to check.
///
/// Example:
///
/// ```json
/// { "react/exhaustive-deps": ["error", { "additionalHooks": "useSpecialEffect" }] }
/// ```
ExhaustiveDeps,
react,
correctness,
safe_fixes_and_dangerous_suggestions
safe_fixes_and_dangerous_suggestions,
config = ExhaustiveDepsConfigJson,
);

const HOOKS_USELESS_WITHOUT_DEPENDENCIES: [&str; 2] = ["useCallback", "useMemo"];
Expand Down
34 changes: 12 additions & 22 deletions crates/oxc_linter/src/rules/typescript/array_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::AstNode;
use oxc_span::Span;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
ast_util::outermost_paren_parent,
Expand Down Expand Up @@ -67,27 +69,11 @@ declare_oxc_lint!(
/// const c: string[] = ['a', 'b'];
/// const d: MyType[] = ['a', 'b'];
/// ```
///
/// ### Options
///
/// ```json
/// {
/// "typescript/array-type": ["error", { "default": "array", "readonly": "array" }]
/// }
/// ```
/// - `default`: The array type expected for mutable cases.
/// - `readonly`: The array type expected for readonly cases. If omitted, the value for `default` will be used.
///
/// Both `default` and `readonly` can be one of:
/// - `"array"`
/// - `"generic"`
/// - `"array-simple"`
///
/// The default config will enforce that all mutable and readonly arrays use the 'array' syntax.
ArrayType,
typescript,
style,
fix
fix,
config = ArrayTypeConfig,
);

fn generic(readonly_prefix: &str, name: &str, type_name: &str, span: Span) -> OxcDiagnostic {
Expand Down Expand Up @@ -123,11 +109,13 @@ fn array_simple(
.with_label(span)
}

#[derive(Debug, Default, Clone)]
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", default)]
pub struct ArrayTypeConfig {
// The array type expected for mutable cases.
/// The array type expected for mutable cases.
default: ArrayOption,
// The array type expected for readonly cases. If omitted, the value for `default` will be used.
/// The array type expected for readonly cases. If omitted, the value for `default` will be used.
#[schemars(with = "ArrayOption")]
readonly: Option<ArrayOption>,
}

Expand All @@ -138,7 +126,9 @@ impl std::ops::Deref for ArrayType {
&self.0
}
}
#[derive(Debug, Default, Clone)]

#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum ArrayOption {
#[default]
Array,
Expand Down
Loading
Loading