eslint-plugin-code-style 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/AGENTS.md +27 -29
  2. package/README.md +1427 -820
  3. package/index.d.ts +16 -0
  4. package/index.js +4720 -294
  5. package/package.json +1 -1
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  **A powerful ESLint plugin for enforcing consistent code formatting and style rules in React/JSX projects.**
20
20
 
21
- *47 auto-fixable rules to keep your codebase clean and consistent*
21
+ *56 auto-fixable rules to keep your codebase clean and consistent*
22
22
 
23
23
  </div>
24
24
 
@@ -26,7 +26,7 @@
26
26
 
27
27
  ## 🎯 Why This Plugin?
28
28
 
29
- This plugin provides **47 custom auto-fixable rules** for code formatting. Built for **ESLint v9 flat configs**.
29
+ This plugin provides **56 custom auto-fixable rules** for code formatting. Built for **ESLint v9 flat configs**.
30
30
 
31
31
  > **Note:** ESLint [deprecated 79 formatting rules](https://eslint.org/blog/2023/10/deprecating-formatting-rules/) in v8.53.0. Our recommended configs use `@stylistic/eslint-plugin` as the replacement for these deprecated rules.
32
32
 
@@ -35,7 +35,7 @@ This plugin provides **47 custom auto-fixable rules** for code formatting. Built
35
35
  - **Works alongside existing tools** — Complements ESLint's built-in rules and packages like eslint-plugin-react, eslint-plugin-import, etc
36
36
  - **Self-sufficient rules** — Each rule handles complete formatting independently
37
37
  - **Consistency at scale** — Reduces code-style differences between team members by enforcing uniform formatting across your projects
38
- - **Fully automated** — All 47 rules support auto-fix, eliminating manual style reviews
38
+ - **Fully automated** — All 56 rules support auto-fix, eliminating manual style reviews
39
39
 
40
40
  When combined with ESLint's native rules and other popular plugins, this package helps create a complete code style solution that keeps your codebase clean and consistent.
41
41
 
@@ -59,17 +59,19 @@ We provide **ready-to-use ESLint flat configuration files** that combine `eslint
59
59
 
60
60
  ### 💡 Why Use These Configs?
61
61
 
62
- - **Complete Coverage** — Combines ESLint built-in rules, third-party plugins, and all 47 code-style rules
62
+ - **Complete Coverage** — Combines ESLint built-in rules, third-party plugins, and all 56 code-style rules
63
63
  - **Ready-to-Use** — Copy the config file and start linting immediately
64
64
  - **Battle-Tested** — These configurations have been refined through real-world usage
65
65
  - **Fully Documented** — Each config includes detailed instructions and explanations
66
66
 
67
67
  ### 📋 Available Configurations
68
68
 
69
- | Configuration | Description | Link |
70
- |---------------|-------------|------|
69
+ | Configuration | Description | Status |
70
+ |---------------|-------------|--------|
71
71
  | **React** | React.js projects (JavaScript, JSX) | [View Config](./recommended-configs/react/) |
72
72
  | **React + TS + Tailwind** | React + TypeScript + Tailwind CSS | [View Config](./recommended-configs/react-ts-tw/) |
73
+ | **React + TypeScript** | React + TypeScript projects | Coming Soon |
74
+ | **React + Tailwind** | React + Tailwind CSS projects | Coming Soon |
73
75
 
74
76
  ### ⚡ Quick Start with Recommended Config
75
77
 
@@ -94,7 +96,7 @@ We provide **ready-to-use ESLint flat configuration files** that combine `eslint
94
96
  <td width="50%">
95
97
 
96
98
  ### 🔧 Auto-Fixable Rules
97
- All **47 rules** support automatic fixing with `eslint --fix`. No manual code changes needed.
99
+ All **56 rules** support automatic fixing with `eslint --fix`. No manual code changes needed.
98
100
 
99
101
  </td>
100
102
  <td width="50%">
@@ -187,8 +189,12 @@ rules: {
187
189
  "code-style/assignment-value-same-line": "error",
188
190
  "code-style/block-statement-newlines": "error",
189
191
  "code-style/comment-format": "error",
192
+ "code-style/component-props-destructure": "error",
193
+ "code-style/react-code-order": "error",
194
+ "code-style/component-props-inline-type": "error",
190
195
  "code-style/function-call-spacing": "error",
191
196
  "code-style/function-naming-convention": "error",
197
+ "code-style/function-object-destructure": "error",
192
198
  "code-style/function-params-per-line": "error",
193
199
  "code-style/hook-callback-format": "error",
194
200
  "code-style/hook-deps-per-line": "error",
@@ -225,6 +231,11 @@ rules: {
225
231
  "code-style/single-argument-on-one-line": "error",
226
232
  "code-style/string-property-spacing": "error",
227
233
  "code-style/variable-naming-convention": "error",
234
+ "code-style/enum-format": "error",
235
+ "code-style/interface-format": "error",
236
+ "code-style/type-annotation-spacing": "error",
237
+ "code-style/type-format": "error",
238
+ "code-style/typescript-definition-location": "error",
228
239
  }
229
240
  ```
230
241
 
@@ -234,34 +245,54 @@ rules: {
234
245
 
235
246
  ## 📖 Rules Summary
236
247
 
237
- > All **47 rules** are auto-fixable. See detailed examples for each rule in the [Rules Reference](#-rules-reference) section below.
248
+ > All **56 rules** are auto-fixable. See detailed examples for each rule in the [Rules Reference](#-rules-reference) section below.
238
249
  >
239
250
  > Rules marked with ⚙️ support customization options (e.g., extending default folder lists).
240
251
 
241
252
  | Rule | Description |
242
253
  |------|-------------|
254
+ | **Array Rules** | |
243
255
  | `array-items-per-line` | Collapse arrays ≤ threshold to one line; expand larger arrays with each item on own line (default: ≤3) ⚙️ |
244
256
  | `array-objects-on-new-lines` | Each object in an array starts on its own line for better visual scanning |
257
+ | **Arrow Function Rules** | |
245
258
  | `arrow-function-block-body` | Wrap multiline arrow function expressions in parentheses for clear boundaries |
246
259
  | `arrow-function-simple-jsx` | Collapse arrow functions returning simple single-element JSX to one line, remove unnecessary parens |
247
260
  | `arrow-function-simplify` | Convert block body with single return to implicit return: `() => { return x; }` → `() => x` |
248
261
  | `curried-arrow-same-line` | Curried arrow functions start on same line as `=>`, not on new line |
249
- | `assignment-value-same-line` | Assignment values start on same line as `=`, not on new line |
250
- | `block-statement-newlines` | Newline after `{` and before `}` in if/for/while/function blocks |
262
+ | **Call Expression Rules** | |
263
+ | `function-arguments-format` | Args threshold or multiline: first arg on new line, each on own line, closing `)` on new line (default: ≥2) ⚙️ |
264
+ | `nested-call-closing-brackets` | Chain closing brackets on same line: `}));` not scattered across lines |
265
+ | `no-empty-lines-in-function-calls` | No empty lines between arguments or after `(`/before `)` |
266
+ | `opening-brackets-same-line` | Opening `{`, `[`, or `(` on same line as function call, not on new line |
267
+ | `simple-call-single-line` | Collapse simple `fn(() => call())` patterns to single line |
268
+ | `single-argument-on-one-line` | Single simple argument stays on one line: `fn(x)` not expanded |
269
+ | **Comment Rules** | |
251
270
  | `comment-format` | Space after `//`, space inside `/* */`, convert single-line blocks to `//`, no blank lines between file-top comments |
271
+ | **Component Rules** | |
272
+ | `component-props-destructure` | Component props must be destructured `({ prop })` not received as `(props)` |
273
+ | `component-props-inline-type` | Inline type annotation `} : {` with matching props, proper spacing, commas, no interface reference |
274
+ | **Control Flow Rules** | |
275
+ | `block-statement-newlines` | Newline after `{` and before `}` in if/for/while/function blocks |
276
+ | `if-statement-format` | `{` on same line as `if`/`else if`, `else` on same line as `}`, proper spacing |
277
+ | `multiline-if-conditions` | Conditions exceeding threshold get one operand per line with proper indentation (default: >3) ⚙️ |
278
+ | `no-empty-lines-in-switch-cases` | No empty line after `case X:` before code, no empty lines between cases |
279
+ | **Function Rules** | |
252
280
  | `function-call-spacing` | No space between function name and `(`: `fn()` not `fn ()` |
253
281
  | `function-naming-convention` | Functions use camelCase, start with verb (get/set/handle/is/has), handlers end with Handler |
282
+ | `function-object-destructure` | Non-component functions: use typed params (not destructured), destructure in body; report dot notation access |
254
283
  | `function-params-per-line` | When multiline, each param on own line with consistent indentation |
284
+ | `no-empty-lines-in-function-params` | No empty lines between parameters or after `(`/before `)` |
285
+ | **Hook Rules** | |
255
286
  | `hook-callback-format` | React hooks: callback on new line, deps array on separate line, proper indentation |
256
287
  | `hook-deps-per-line` | Collapse deps ≤ threshold to one line; expand larger arrays with each dep on own line (default: >2) ⚙️ |
257
- | `if-statement-format` | `{` on same line as `if`/`else if`, `else` on same line as `}`, proper spacing |
258
- | `multiline-if-conditions` | Conditions exceeding threshold get one operand per line with proper indentation (default: >3) ⚙️ |
288
+ | **Import/Export Rules** | |
259
289
  | `absolute-imports-only` | Use alias imports from index files only (not deep paths), no relative imports (default: `@/`) ⚙️ |
260
290
  | `export-format` | `export {` on same line; collapse ≤ threshold to one line; expand larger with each specifier on own line (default: ≤3) ⚙️ |
261
291
  | `import-format` | `import {` and `} from` on same line; collapse ≤ threshold; expand larger with each specifier on own line (default: ≤3) ⚙️ |
262
292
  | `import-source-spacing` | No leading/trailing spaces inside import path quotes |
263
293
  | `index-export-style` | Index files: no blank lines, enforce shorthand or import-export style; Regular files: require blank lines between exports (default: shorthand) ⚙️ |
264
294
  | `module-index-exports` | Index files must export all folder contents (files and subfolders) ⚙️ |
295
+ | **JSX Rules** | |
265
296
  | `jsx-children-on-new-line` | Multiple JSX children: each on own line with proper indentation |
266
297
  | `jsx-closing-bracket-spacing` | No space before `>` or `/>` in JSX tags |
267
298
  | `jsx-element-child-new-line` | Nested JSX elements on new lines; text/expression children can stay inline |
@@ -271,21 +302,25 @@ rules: {
271
302
  | `jsx-simple-element-one-line` | Collapse simple JSX with single text/expression child to one line |
272
303
  | `jsx-string-value-trim` | No leading/trailing whitespace inside JSX string attribute values |
273
304
  | `jsx-ternary-format` | Simple ternaries on one line; complex branches get parens with proper indentation |
274
- | `member-expression-bracket-spacing` | No spaces inside brackets in computed member expressions: `arr[0]` not `arr[ 0 ]` |
275
- | `function-arguments-format` | Args ≥ threshold or multiline: first arg on new line, each on own line, closing `)` on new line (default: ≥2) ⚙️ |
276
- | `nested-call-closing-brackets` | Chain closing brackets on same line: `}));` not scattered across lines |
277
- | `no-empty-lines-in-function-calls` | No empty lines between arguments or after `(`/before `)` |
278
- | `no-empty-lines-in-function-params` | No empty lines between parameters or after `(`/before `)` |
279
305
  | `no-empty-lines-in-jsx` | No empty lines between children or after opening/before closing tags |
306
+ | **Object Rules** | |
280
307
  | `no-empty-lines-in-objects` | No empty lines between properties or after `{`/before `}` |
281
- | `no-empty-lines-in-switch-cases` | No empty line after `case X:` before code, no empty lines between cases |
282
308
  | `object-property-per-line` | Collapse ≤ threshold to one line; expand larger with `{`/`}` on own lines and each property on own line (default: ≥2) ⚙️ |
283
309
  | `object-property-value-brace` | Opening `{` of object value on same line as `:`, not on new line |
284
310
  | `object-property-value-format` | Simple property values on same line as `:`, not on new line |
285
- | `opening-brackets-same-line` | Opening `{`, `[`, or `(` on same line as function call, not on new line |
286
- | `simple-call-single-line` | Collapse simple `fn(() => call())` patterns to single line |
287
- | `single-argument-on-one-line` | Single simple argument stays on one line: `fn(x)` not expanded |
288
311
  | `string-property-spacing` | No leading/trailing spaces inside string property keys |
312
+ | **Spacing Rules** | |
313
+ | `assignment-value-same-line` | Assignment values start on same line as `=`, not on new line |
314
+ | `member-expression-bracket-spacing` | No spaces inside brackets in computed member expressions: `arr[0]` not `arr[ 0 ]` |
315
+ | **TypeScript Rules** | |
316
+ | `enum-format` | Enforce enum naming (PascalCase + Enum suffix), UPPER_CASE members, no empty lines, and trailing commas |
317
+ | `interface-format` | Enforce interface naming (PascalCase + Interface suffix), camelCase properties, no empty lines, and trailing commas |
318
+ | `type-annotation-spacing` | Enforce consistent spacing in type annotations: no space before colon/generic/array brackets, one space after colon |
319
+ | `type-format` | Enforce type naming (PascalCase + Type suffix), camelCase properties, no empty lines, and trailing commas |
320
+ | `typescript-definition-location` | Enforce TypeScript definitions (interfaces, types, enums) to be in designated folders ⚙️ |
321
+ | **React Rules** | |
322
+ | `react-code-order` | Enforce consistent ordering in components and hooks: props destructure → refs → state → redux → router → context → custom hooks → derived → memo → callback → handlers → effects → return |
323
+ | **Variable Rules** | |
289
324
  | `variable-naming-convention` | camelCase for variables, UPPER_CASE for constants, PascalCase for components, `use` prefix for hooks |
290
325
 
291
326
  <br />
@@ -484,412 +519,363 @@ const mapDispatch = () =>
484
519
 
485
520
  <br />
486
521
 
487
- ## 📐 Spacing & Formatting Rules
522
+ ## 📞 Call Expression Rules
488
523
 
489
- ### `assignment-value-same-line`
524
+ ### `function-arguments-format`
490
525
 
491
- **What it does:** Ensures the assigned value starts on the same line as the `=` sign, not on a new line.
526
+ **What it does:** Enforces consistent formatting for function call arguments:
527
+ - Single simple argument stays on one line
528
+ - 2+ arguments get one per line
529
+ - Multiline arguments trigger full expansion
530
+ - React hooks are skipped by default (they have their own rule)
492
531
 
493
- **Why use it:** Breaking after `=` creates awkward formatting and wastes vertical space. Keeping values on the same line as `=` is more readable.
532
+ **Why use it:** Consistent argument formatting makes function calls scannable and diffs clean when adding/removing arguments.
494
533
 
495
534
  ```javascript
496
- // ✅ Good — value starts on same line as =
497
- const name = "John";
498
- const config = {
499
- host: "localhost",
500
- port: 3000,
501
- };
502
- const items = [
503
- "first",
504
- "second",
505
- ];
506
-
507
- // ❌ Bad — value on new line after =
508
- const name =
509
- "John";
510
- const config =
511
- {
512
- host: "localhost",
513
- port: 3000,
514
- };
515
- const items =
516
- [
517
- "first",
518
- "second",
519
- ];
520
- ```
521
-
522
- ---
523
-
524
- ### `block-statement-newlines`
525
-
526
- **What it does:** Enforces newlines after the opening brace `{` and before the closing brace `}` in block statements (if, for, while, etc.).
527
-
528
- **Why use it:** Consistent block formatting improves readability. Single-line blocks are harder to scan and edit.
535
+ // ✅ Good — single argument stays compact
536
+ fetchUser(userId);
537
+ console.log(message);
538
+ dispatch(action);
529
539
 
530
- ```javascript
531
- // ✅ Good — proper block formatting
532
- if (condition) {
533
- doSomething();
534
- }
540
+ // ✅ Good — 2+ arguments get one per line
541
+ setValue(
542
+ "email",
543
+ "user@example.com",
544
+ );
535
545
 
536
- for (const item of items) {
537
- process(item);
538
- }
546
+ createUser(
547
+ name,
548
+ email,
549
+ password,
550
+ );
539
551
 
540
- while (running) {
541
- tick();
542
- }
552
+ // Good — multiline argument triggers expansion
553
+ processData(
554
+ {
555
+ id: 1,
556
+ name: "test",
557
+ },
558
+ );
543
559
 
544
- // Badeverything on one line
545
- if (condition) { doSomething(); }
560
+ // Goodcallback with body triggers expansion
561
+ items.forEach(
562
+ (item) => {
563
+ process(item);
564
+ save(item);
565
+ },
566
+ );
546
567
 
547
- // ❌ Bad — no space after brace
548
- if (condition) {doSomething();}
568
+ // ❌ Bad — multiple arguments on same line
569
+ setValue("email", "user@example.com");
570
+ createUser(name, email, password);
549
571
 
550
572
  // ❌ Bad — inconsistent formatting
551
- for (const item of items) { process(item);
552
- }
573
+ fn(arg1,
574
+ arg2, arg3);
553
575
  ```
554
576
 
555
- ---
556
-
557
- ### `comment-format`
558
-
559
- **What it does:** Enforces proper comment formatting:
560
- - Space after `//` in line comments
561
- - Space after `/*` and before `*/` in block comments
562
- - Single-line block comments converted to line comments
563
- - No blank lines between consecutive comments at file top
577
+ **Options:**
564
578
 
565
- **Why use it:** Consistent comment formatting improves readability and maintains a clean, professional codebase.
579
+ | Option | Type | Default | Description |
580
+ |--------|------|---------|-------------|
581
+ | `minArgs` | `integer` | `2` | Minimum arguments to enforce multiline |
582
+ | `skipHooks` | `boolean` | `true` | Skip React hooks (useEffect, etc.) |
583
+ | `skipSingleArg` | `boolean` | `true` | Skip calls with single complex argument |
566
584
 
567
585
  ```javascript
568
- // Good proper spacing
569
- // This is a comment
570
- /* This is a block comment */
571
-
572
- /*
573
- * This is a multi-line
574
- * block comment
575
- */
576
-
577
- // ✅ Good — file-top comments without gaps
578
- // File: utils.js
579
- // Author: John Doe
580
- // License: MIT
581
-
582
- // ❌ Bad — missing space after //
583
- //This is a comment
584
-
585
- // ❌ Bad — no space in block comment
586
- /*No space*/
586
+ // Example: Require multiline for 3+ arguments
587
+ "code-style/function-arguments-format": ["error", { minArgs: 3 }]
587
588
 
588
- // Bad single-line block should be line comment
589
- /* This should use // syntax */
589
+ // Example: Don't skip React hooks
590
+ "code-style/function-arguments-format": ["error", { skipHooks: false }]
590
591
  ```
591
592
 
592
593
  ---
593
594
 
594
- ### `member-expression-bracket-spacing`
595
+ ### `nested-call-closing-brackets`
595
596
 
596
- **What it does:** Removes spaces inside brackets in computed member expressions (array access, dynamic property access).
597
+ **What it does:** Ensures nested function calls (common in styled-components, HOCs) have closing brackets on the same line: `}));`
597
598
 
598
- **Why use it:** Consistent with JavaScript conventions. Spaces inside brackets look inconsistent with array literals and other bracket usage.
599
+ **Why use it:** Scattered closing brackets (`}\n);\n` ) waste vertical space and make it harder to see where expressions end.
599
600
 
600
601
  ```javascript
601
- // ✅ Good — no spaces inside brackets
602
- const value = arr[0];
603
- const name = obj[key];
604
- const item = data[index];
605
- const nested = matrix[row][col];
602
+ // ✅ Good — closing brackets together
603
+ const StyledCard = styled(Card)(({ theme }) => ({
604
+ color: theme.palette.text.primary,
605
+ padding: theme.spacing(2),
606
+ }));
606
607
 
607
- // Bad spaces inside brackets
608
- const value = arr[ 0 ];
609
- const name = obj[ key ];
610
- const item = data[ index ];
608
+ const StyledButton = styled("button")(({ theme }) => ({
609
+ backgroundColor: theme.colors.primary,
610
+ }));
611
+
612
+ // ✅ Good — multiple levels
613
+ const Component = connect(
614
+ mapStateToProps,
615
+ mapDispatchToProps,
616
+ )(withRouter(MyComponent));
617
+
618
+ // ❌ Bad — closing brackets scattered
619
+ const StyledCard = styled(Card)(({ theme }) => ({
620
+ color: theme.palette.text.primary,
621
+ })
622
+ );
623
+
624
+ // ❌ Bad — each bracket on its own line
625
+ const StyledCard = styled(Card)(({ theme }) => ({
626
+ color: theme.colors.primary,
627
+ })
628
+ )
629
+ ;
611
630
  ```
612
631
 
613
632
  ---
614
633
 
615
- ### `no-empty-lines-in-function-params`
634
+ ### `no-empty-lines-in-function-calls`
616
635
 
617
- **What it does:** Removes empty lines within function parameter lists — between parameters and after opening/before closing parentheses.
636
+ **What it does:** Removes empty lines within function call argument lists — between arguments and after opening/before closing parentheses.
618
637
 
619
- **Why use it:** Empty lines in parameter lists waste space and make parameters harder to scan as a group.
638
+ **Why use it:** Empty lines between arguments break visual grouping. Arguments should flow as a cohesive list.
620
639
 
621
640
  ```javascript
622
641
  // ✅ Good — no empty lines
623
- function createUser(
642
+ createUser(
624
643
  name,
625
644
  email,
645
+ password,
626
646
  role,
627
- ) {}
647
+ );
628
648
 
629
- const handler = (
630
- event,
631
- context,
632
- ) => {};
649
+ fetchData(
650
+ url,
651
+ {
652
+ method: "POST",
653
+ body: data,
654
+ },
655
+ );
633
656
 
634
- // ❌ Bad — empty line between params
635
- function createUser(
657
+ // ❌ Bad — empty line between arguments
658
+ createUser(
636
659
  name,
637
660
 
638
661
  email,
639
662
 
640
- role,
641
- ) {}
663
+ password,
664
+ );
642
665
 
643
666
  // ❌ Bad — empty line after opening paren
644
- const handler = (
667
+ fetchData(
645
668
 
646
- event,
647
- context,
648
- ) => {};
669
+ url,
670
+ options,
671
+ );
672
+
673
+ // ❌ Bad — empty line before closing paren
674
+ fetchData(
675
+ url,
676
+ options,
677
+
678
+ );
649
679
  ```
650
680
 
651
681
  ---
652
682
 
653
- ### `variable-naming-convention`
683
+ ### `opening-brackets-same-line`
654
684
 
655
- **What it does:** Enforces naming conventions for variables:
656
- - **camelCase** for regular variables and functions
657
- - **UPPER_CASE** for constants (primitive values)
658
- - **PascalCase** for React components and classes
659
- - **camelCase with `use` prefix** for hooks
685
+ **What it does:** Ensures opening brackets (`{`, `[`, `(`) in function arguments stay on the same line as the function call.
660
686
 
661
- **Why use it:** Consistent naming makes code predictable. You can tell what something is by how it's named.
687
+ **Why use it:** Opening brackets on new lines create unnecessary indentation and vertical space.
662
688
 
663
689
  ```javascript
664
- // ✅ Good — correct conventions
665
- const userName = "John"; // camelCase for variables
666
- const itemCount = 42; // camelCase for variables
667
- const MAX_RETRIES = 3; // UPPER_CASE for constants
668
- const API_BASE_URL = "/api"; // UPPER_CASE for constants
669
- const UserProfile = () => <div />; // PascalCase for components
670
- const useAuth = () => {}; // camelCase with use prefix for hooks
690
+ // ✅ Good — brackets on same line as call
691
+ fn({ key: value });
692
+ process([1, 2, 3]);
693
+ items.map(({ id }) => id);
694
+ configure({ debug: true });
671
695
 
672
- // Badwrong conventions
673
- const user_name = "John"; // snake_case
674
- const maxretries = 3; // should be UPPER_CASE
675
- const userProfile = () => <div />; // should be PascalCase
676
- const UseAuth = () => {}; // hooks should be camelCase
677
- ```
696
+ // Goodmultiline content is fine
697
+ fn({
698
+ key: value,
699
+ other: data,
700
+ });
678
701
 
679
- <br />
702
+ items.map(({ id, name }) => (
703
+ <Item key={id} name={name} />
704
+ ));
680
705
 
681
- ## Function Rules
706
+ // Bad — opening bracket on new line
707
+ fn(
708
+ { key: value }
709
+ );
682
710
 
683
- ### `function-call-spacing`
711
+ process(
712
+ [1, 2, 3]
713
+ );
684
714
 
685
- **What it does:** Removes any space between a function name and its opening parenthesis.
715
+ items.map(
716
+ ({ id }) => id
717
+ );
718
+ ```
686
719
 
687
- **Why use it:** Standard JavaScript convention. `fn()` is correct, `fn ()` looks like a typo and can cause confusion.
720
+ ---
721
+
722
+ ### `simple-call-single-line`
723
+
724
+ **What it does:** Collapses simple function calls with an arrow function containing a single call expression onto one line.
725
+
726
+ **Why use it:** Common patterns like `lazy(() => import(...))` don't need multiline formatting. Single line is cleaner.
688
727
 
689
728
  ```javascript
690
- // ✅ Good — no space before parenthesis
691
- useDispatch();
692
- myFunction(arg);
693
- console.log("message");
694
- array.map((x) => x * 2);
695
- obj.method();
729
+ // ✅ Good — simple patterns on one line
730
+ const Page = lazy(() => import("./Page"));
731
+ const Modal = lazy(() => import("./Modal"));
732
+ setTimeout(() => callback(), 100);
733
+ requestAnimationFrame(() => render());
734
+ items.filter(() => isValid(item));
696
735
 
697
- // Badspace before parenthesis
698
- useDispatch ();
699
- myFunction (arg);
700
- console.log ("message");
701
- array.map ((x) => x * 2);
736
+ // Goodcomplex callbacks stay multiline
737
+ const Page = lazy(() => {
738
+ console.log("Loading page");
739
+ return import("./Page");
740
+ });
741
+
742
+ // ❌ Bad — unnecessary multiline for simple pattern
743
+ const Page = lazy(
744
+ () => import("./Page"),
745
+ );
746
+
747
+ setTimeout(
748
+ () => callback(),
749
+ 100,
750
+ );
702
751
  ```
703
752
 
704
753
  ---
705
754
 
706
- ### `function-naming-convention`
755
+ ### `single-argument-on-one-line`
707
756
 
708
- **What it does:** Enforces naming conventions for functions:
709
- - **camelCase** required
710
- - **Verb prefix** recommended (get, set, handle, is, has, can, should, etc.)
711
- - **Event handlers** can use `handle` prefix or `Handler` suffix
757
+ **What it does:** Ensures function calls with a single simple argument (literal, identifier, member expression) stay on one line.
712
758
 
713
- **Why use it:** Function names should describe actions. Verb prefixes make the purpose immediately clear.
759
+ **Why use it:** Single-argument calls don't need multiline formatting. Expanding them wastes vertical space.
714
760
 
715
761
  ```javascript
716
- // ✅ Good — clear verb prefixes
717
- function getUserData() {}
718
- function setUserName(name) {}
719
- function handleClick() {}
720
- function handleSubmit() {}
721
- function isValidEmail(email) {}
722
- function hasPermission(user) {}
723
- function canAccess(resource) {}
724
- function shouldUpdate(props) {}
725
- const fetchUsers = async () => {};
726
- const submitHandler = () => {};
762
+ // ✅ Good — single argument on one line
763
+ fetchUser(userId);
764
+ console.log(message);
765
+ process(data.items);
766
+ dispatch(action);
767
+ setValue("key");
768
+ getElement(document.body);
727
769
 
728
- // Badno verb, unclear purpose
729
- function userData() {}
730
- function userName(name) {}
731
- function click() {}
732
- function valid(email) {}
770
+ // Goodcomplex single argument can be multiline
771
+ processConfig({
772
+ key: value,
773
+ other: data,
774
+ });
733
775
 
734
- // ❌ Bad — wrong case
735
- function GetUserData() {}
736
- function get_user_data() {}
776
+ // ❌ Bad — simple argument expanded unnecessarily
777
+ fetchUser(
778
+ userId,
779
+ );
780
+
781
+ console.log(
782
+ message,
783
+ );
784
+
785
+ dispatch(
786
+ action,
787
+ );
737
788
  ```
738
789
 
739
- ---
790
+ <br />
740
791
 
741
- ### `function-params-per-line`
792
+ ## 💬 Comment Rules
742
793
 
743
- **What it does:** When function parameters span multiple lines, ensures each parameter is on its own line with consistent indentation.
794
+ ### `comment-format`
744
795
 
745
- **Why use it:** Mixed formatting (some params on same line, some on different lines) is confusing. One per line is scannable and easy to edit.
796
+ **What it does:** Enforces proper comment formatting:
797
+ - Space after `//` in line comments
798
+ - Space after `/*` and before `*/` in block comments
799
+ - Single-line block comments converted to line comments
800
+ - No blank lines between consecutive comments at file top
801
+
802
+ **Why use it:** Consistent comment formatting improves readability and maintains a clean, professional codebase.
746
803
 
747
804
  ```javascript
748
- // ✅ Good — each param on own line
749
- function createUser(
750
- name,
751
- email,
752
- password,
753
- role,
754
- ) {}
805
+ // ✅ Good — proper spacing
806
+ // This is a comment
807
+ /* This is a block comment */
755
808
 
756
- const handler = (
757
- event,
758
- context,
759
- callback,
760
- ) => {};
809
+ /*
810
+ * This is a multi-line
811
+ * block comment
812
+ */
761
813
 
762
- // ✅ Good — short params can stay on one line
763
- function add(a, b) {}
814
+ // ✅ Good — file-top comments without gaps
815
+ // File: utils.js
816
+ // Author: John Doe
817
+ // License: MIT
764
818
 
765
- // ❌ Bad — mixed formatting
766
- function createUser(name,
767
- email, password,
768
- role) {}
819
+ // ❌ Bad — missing space after //
820
+ //This is a comment
769
821
 
770
- // ❌ Bad — some on same line, some not
771
- const handler = (event, context,
772
- callback) => {};
822
+ // ❌ Bad — no space in block comment
823
+ /*No space*/
824
+
825
+ // ❌ Bad — single-line block should be line comment
826
+ /* This should use // syntax */
773
827
  ```
774
828
 
775
829
  <br />
776
830
 
777
- ## 🪝 React Hooks Rules
831
+ ## 🔀 Control Flow Rules
778
832
 
779
- ### `hook-callback-format`
833
+ ### `block-statement-newlines`
780
834
 
781
- **What it does:** Enforces consistent multi-line formatting for React hooks that take a callback and dependency array (useEffect, useCallback, useMemo, useLayoutEffect).
835
+ **What it does:** Enforces newlines after the opening brace `{` and before the closing brace `}` in block statements (if, for, while, etc.).
782
836
 
783
- **Why use it:** Hooks with callbacks and dependencies are complex. Multi-line formatting makes the callback, return cleanup, and dependencies clearly visible.
837
+ **Why use it:** Consistent block formatting improves readability. Single-line blocks are harder to scan and edit.
784
838
 
785
839
  ```javascript
786
- // ✅ Good — callback and deps clearly separated
787
- useEffect(
788
- () => {
789
- fetchData();
790
- },
791
- [userId],
792
- );
793
-
794
- useCallback(
795
- () => {
796
- handleSubmit(data);
797
- },
798
- [data, handleSubmit],
799
- );
840
+ // ✅ Good — proper block formatting
841
+ if (condition) {
842
+ doSomething();
843
+ }
800
844
 
801
- useMemo(
802
- () => expensiveCalculation(items),
803
- [items],
804
- );
845
+ for (const item of items) {
846
+ process(item);
847
+ }
805
848
 
806
- // Good — cleanup function visible
807
- useEffect(
808
- () => {
809
- const subscription = subscribe();
849
+ while (running) {
850
+ tick();
851
+ }
810
852
 
811
- return () => subscription.unsubscribe();
812
- },
813
- [subscribe],
814
- );
853
+ // Bad — everything on one line
854
+ if (condition) { doSomething(); }
815
855
 
816
- // ❌ Bad — everything crammed on one line
817
- useEffect(() => { fetchData(); }, [userId]);
856
+ // ❌ Bad — no space after brace
857
+ if (condition) {doSomething();}
818
858
 
819
- // ❌ Bad — hard to see dependencies
820
- useCallback(() => { handleSubmit(data); }, [data, handleSubmit]);
859
+ // ❌ Bad — inconsistent formatting
860
+ for (const item of items) { process(item);
861
+ }
821
862
  ```
822
863
 
823
864
  ---
824
865
 
825
- ### `hook-deps-per-line`
866
+ ### `if-statement-format`
826
867
 
827
- **What it does:** When a hook's dependency array exceeds the threshold (default: 2), each dependency goes on its own line.
868
+ **What it does:** Enforces consistent if/else formatting:
869
+ - Opening `{` on the same line as `if`/`else if`/`else`
870
+ - `else` on the same line as the closing `}`
871
+ - Proper spacing around keywords
828
872
 
829
- **Why use it:** Long dependency arrays are hard to scan and diff. One per line makes it easy to see what changed and catch missing/extra dependencies.
873
+ **Why use it:** Consistent brace placement reduces visual noise and follows the most common JavaScript style (K&R / "one true brace style").
830
874
 
831
875
  ```javascript
832
- // ✅ Good — 2 or fewer deps stay inline
833
- useEffect(() => {}, [userId]);
834
- useEffect(() => {}, [userId, token]);
835
-
836
- // ✅ Good — 3+ deps get one per line
837
- useEffect(
838
- () => {},
839
- [
840
- userId,
841
- token,
842
- refreshToken,
843
- ],
844
- );
845
-
846
- useCallback(
847
- () => handleSubmit(data),
848
- [
849
- data,
850
- handleSubmit,
851
- validateForm,
852
- showError,
853
- ],
854
- );
855
-
856
- // ❌ Bad — too many deps on one line
857
- useEffect(() => {}, [userId, token, refreshToken, apiUrl]);
858
-
859
- // ❌ Bad — deps should be one per line when expanded
860
- useEffect(() => {}, [
861
- userId, token, refreshToken,
862
- ]);
863
- ```
864
-
865
- **Options:**
866
-
867
- | Option | Type | Default | Description |
868
- |--------|------|---------|-------------|
869
- | `maxDeps` | `integer` | `2` | Maximum dependencies to keep on single line |
870
-
871
- ```javascript
872
- // Example: Allow up to 3 dependencies on single line
873
- "code-style/hook-deps-per-line": ["error", { maxDeps: 3 }]
874
- ```
875
-
876
- <br />
877
-
878
- ## 🔀 Control Flow Rules
879
-
880
- ### `if-statement-format`
881
-
882
- **What it does:** Enforces consistent if/else formatting:
883
- - Opening `{` on the same line as `if`/`else if`/`else`
884
- - `else` on the same line as the closing `}`
885
- - Proper spacing around keywords
886
-
887
- **Why use it:** Consistent brace placement reduces visual noise and follows the most common JavaScript style (K&R / "one true brace style").
888
-
889
- ```javascript
890
- // ✅ Good — consistent formatting
891
- if (condition) {
892
- doSomething();
876
+ // ✅ Good — consistent formatting
877
+ if (condition) {
878
+ doSomething();
893
879
 
894
880
  doMore();
895
881
  }
@@ -1059,336 +1045,622 @@ switch (status) {
1059
1045
 
1060
1046
  <br />
1061
1047
 
1062
- ## 📥 Import/Export Rules
1048
+ ## Function Rules
1063
1049
 
1064
- ### `absolute-imports-only`
1050
+ ### `function-call-spacing`
1065
1051
 
1066
- **What it does:** Enforces importing from folder index files using absolute paths (aliases like `@/`) instead of relative paths or deep file imports.
1052
+ **What it does:** Removes any space between a function name and its opening parenthesis.
1067
1053
 
1068
- **Why use it:**
1069
- - Absolute imports are cleaner than `../../../components`
1070
- - Index imports create a public API for each folder
1071
- - Refactoring file locations doesn't break imports
1072
- - Encourages proper module organization
1054
+ **Why use it:** Standard JavaScript convention. `fn()` is correct, `fn ()` looks like a typo and can cause confusion.
1073
1055
 
1074
1056
  ```javascript
1075
- // ✅ Good — import from index files using alias
1076
- import { Button, Input } from "@/components";
1077
- import { useAuth, useUser } from "@/hooks";
1078
- import { fetchUsers } from "@/apis";
1079
- import { formatDate } from "@/utils";
1080
-
1081
- // ✅ Good — assets allow deep imports by default
1082
- import logo from "@/assets/images/logo.png";
1083
-
1084
- // ❌ Bad — relative imports
1085
- import { Button } from "../../components";
1086
- import { useAuth } from "../../../hooks";
1057
+ // ✅ Good — no space before parenthesis
1058
+ useDispatch();
1059
+ myFunction(arg);
1060
+ console.log("message");
1061
+ array.map((x) => x * 2);
1062
+ obj.method();
1087
1063
 
1088
- // ❌ Bad — deep imports into component internals
1089
- import { Button } from "@/components/buttons/primary-button";
1090
- import { useAuth } from "@/hooks/auth/useAuth";
1091
- import { fetchUsers } from "@/apis/users/fetchUsers";
1064
+ // ❌ Bad — space before parenthesis
1065
+ useDispatch ();
1066
+ myFunction (arg);
1067
+ console.log ("message");
1068
+ array.map ((x) => x * 2);
1092
1069
  ```
1093
1070
 
1094
- **Default Allowed Folders:**
1095
- `actions`, `apis`, `assets`, `atoms`, `components`, `constants`, `contexts`, `data`, `hooks`, `layouts`, `middlewares`, `providers`, `reducers`, `redux`, `requests`, `routes`, `schemas`, `services`, `store`, `styles`, `theme`, `thunks`, `types`, `utils`, `views`
1071
+ ---
1096
1072
 
1097
- **Customization Options:**
1073
+ ### `function-naming-convention`
1098
1074
 
1099
- | Option | Type | Description |
1100
- |--------|------|-------------|
1101
- | `extraAllowedFolders` | `string[]` | Add extra folders to the default list |
1102
- | `extraReduxSubfolders` | `string[]` | Add extra redux subfolders (default: `actions`, `reducers`, `store`, `thunks`, `types`) |
1103
- | `extraDeepImportFolders` | `string[]` | Add extra folders that allow deep imports (default: `assets`) |
1104
- | `aliasPrefix` | `string` | Change the import alias prefix (default: `@/`) |
1105
- | `allowedFolders` | `string[]` | Replace default folders entirely |
1106
- | `reduxSubfolders` | `string[]` | Replace default redux subfolders entirely |
1107
- | `deepImportFolders` | `string[]` | Replace default deep import folders entirely |
1075
+ **What it does:** Enforces naming conventions for functions:
1076
+ - **camelCase** required
1077
+ - **Verb prefix** recommended (get, set, handle, is, has, can, should, etc.)
1078
+ - **Event handlers** can use `handle` prefix or `Handler` suffix
1079
+
1080
+ **Why use it:** Function names should describe actions. Verb prefixes make the purpose immediately clear.
1108
1081
 
1109
1082
  ```javascript
1110
- // Example: Add custom folders to the defaults
1111
- "code-style/absolute-imports-only": ["error", {
1112
- extraAllowedFolders: ["features", "modules", "lib"],
1113
- extraDeepImportFolders: ["images", "fonts"]
1114
- }]
1083
+ // Good clear verb prefixes
1084
+ function getUserData() {}
1085
+ function setUserName(name) {}
1086
+ function handleClick() {}
1087
+ function handleSubmit() {}
1088
+ function isValidEmail(email) {}
1089
+ function hasPermission(user) {}
1090
+ function canAccess(resource) {}
1091
+ function shouldUpdate(props) {}
1092
+ const fetchUsers = async () => {};
1093
+ const submitHandler = () => {};
1094
+
1095
+ // ❌ Bad — no verb, unclear purpose
1096
+ function userData() {}
1097
+ function userName(name) {}
1098
+ function click() {}
1099
+ function valid(email) {}
1100
+
1101
+ // ❌ Bad — wrong case
1102
+ function GetUserData() {}
1103
+ function get_user_data() {}
1115
1104
  ```
1116
1105
 
1117
1106
  ---
1118
1107
 
1119
- ### `export-format`
1108
+ ### `function-object-destructure`
1120
1109
 
1121
- **What it does:** Formats export statements consistently:
1122
- - `export {` always on the same line as `export` keyword
1123
- - ≤3 specifiers stay on one line (collapsed)
1124
- - 4+ specifiers get one per line (expanded)
1125
- - Proper spacing and trailing commas
1110
+ **What it does:** Enforces that non-component functions should not destructure parameters in the function signature. Instead, use a typed parameter and destructure at the top of the function body. Also reports when parameters are accessed via dot notation (suggesting destructuring).
1126
1111
 
1127
- **Why use it:** Consistent export formatting improves readability. Short exports stay compact, long exports become scannable.
1112
+ **Why use it:** Keeping function signatures clean and short improves readability. Destructuring in the body makes it clear what properties are being used. For React components, this rule does NOT apply — components should destructure props in the signature.
1128
1113
 
1129
- ```javascript
1130
- // ✅ Good — 3 or fewer specifiers stay compact
1131
- export { Button };
1132
- export { Button, Input };
1133
- export { Button, Input, Select };
1114
+ ```typescript
1115
+ // ✅ Good — typed param with destructuring in body
1116
+ const createUserHandler = async (data: CreateUserParamsInterface) => {
1117
+ const { age, email, isActive, name } = data;
1134
1118
 
1135
- // Good 4+ specifiers expand with one per line
1136
- export {
1137
- Button,
1138
- Input,
1139
- Select,
1140
- Checkbox,
1119
+ // Use age, email, isActive, name...
1141
1120
  };
1142
1121
 
1143
- // Good re-exports follow same rules
1144
- export { Button, Input, Select } from "./components";
1145
- export {
1146
- createUser,
1147
- updateUser,
1148
- deleteUser,
1149
- getUser,
1150
- } from "./api";
1122
+ const updateUserHandler = (params: UpdateParamsInterface) => {
1123
+ const { id, updates } = params;
1151
1124
 
1152
- // Bad — no spaces
1153
- export {Button,Input,Select};
1154
-
1155
- // ❌ Bad — keyword on different line
1156
- export
1157
- { Button };
1158
-
1159
- // ❌ Bad — too many on one line
1160
- export { Button, Input, Select, Checkbox, Radio };
1161
- ```
1125
+ // Use id, updates...
1126
+ };
1162
1127
 
1163
- **Options:**
1128
+ // ✅ Good — React components CAN destructure in signature
1129
+ const UserCard = ({
1130
+ name,
1131
+ email,
1132
+ } : {
1133
+ name: string,
1134
+ email: string,
1135
+ }) => (
1136
+ <div>{name} - {email}</div>
1137
+ );
1164
1138
 
1165
- | Option | Type | Default | Description |
1166
- |--------|------|---------|-------------|
1167
- | `maxSpecifiers` | `integer` | `3` | Maximum specifiers to keep on single line |
1139
+ // Bad non-component function destructures in signature
1140
+ const createUserHandler = async ({
1141
+ age,
1142
+ email,
1143
+ isActive,
1144
+ name,
1145
+ }: CreateUserParamsInterface) => {
1146
+ // ...
1147
+ };
1168
1148
 
1169
- ```javascript
1170
- "code-style/export-format": ["error", { maxSpecifiers: 4 }]
1149
+ // ❌ Bad — accessing param via dot notation (should destructure)
1150
+ const processDataHandler = (data: DataInterface) => {
1151
+ console.log(data.id); // Bad: use destructuring
1152
+ console.log(data.name); // Bad: use destructuring
1153
+ return data.value * 2; // Bad: use destructuring
1154
+ };
1171
1155
  ```
1172
1156
 
1173
1157
  ---
1174
1158
 
1175
- ### `import-format`
1159
+ ### `function-params-per-line`
1176
1160
 
1177
- **What it does:** Formats import statements consistently:
1178
- - `import {` on the same line as `import` keyword
1179
- - `} from` on the same line as closing brace
1180
- - ≤3 specifiers stay on one line (collapsed)
1181
- - 4+ specifiers get one per line (expanded)
1161
+ **What it does:** When function parameters span multiple lines, ensures each parameter is on its own line with consistent indentation.
1182
1162
 
1183
- **Why use it:** Consistent import formatting improves readability and makes diffs cleaner when adding/removing imports.
1163
+ **Why use it:** Mixed formatting (some params on same line, some on different lines) is confusing. One per line is scannable and easy to edit.
1184
1164
 
1185
1165
  ```javascript
1186
- // ✅ Good — 3 or fewer specifiers stay compact
1187
- import { useState } from "react";
1188
- import { Button, Input } from "@/components";
1189
- import { get, post, put } from "@/api";
1166
+ // ✅ Good — each param on own line
1167
+ function createUser(
1168
+ name,
1169
+ email,
1170
+ password,
1171
+ role,
1172
+ ) {}
1190
1173
 
1191
- // Good — 4+ specifiers expand with one per line
1192
- import {
1193
- useState,
1194
- useEffect,
1195
- useCallback,
1196
- useMemo,
1197
- } from "react";
1174
+ const handler = (
1175
+ event,
1176
+ context,
1177
+ callback,
1178
+ ) => {};
1198
1179
 
1199
- import {
1200
- Button,
1201
- Input,
1202
- Select,
1203
- Checkbox,
1204
- } from "@/components";
1180
+ // ✅ Good — short params can stay on one line
1181
+ function add(a, b) {}
1205
1182
 
1206
- // ❌ Bad — no spaces
1207
- import {useState,useEffect} from "react";
1183
+ // ❌ Bad — mixed formatting
1184
+ function createUser(name,
1185
+ email, password,
1186
+ role) {}
1208
1187
 
1209
- // ❌ Bad — keyword on different line
1210
- import
1211
- { Button } from "@/components";
1188
+ // ❌ Bad — some on same line, some not
1189
+ const handler = (event, context,
1190
+ callback) => {};
1191
+ ```
1212
1192
 
1213
- // ❌ Bad — from on different line
1214
- import { Button }
1215
- from "@/components";
1193
+ ---
1216
1194
 
1217
- // ❌ Bad — too many on one line
1218
- import { useState, useEffect, useCallback, useMemo, useRef } from "react";
1219
- ```
1195
+ ### `no-empty-lines-in-function-params`
1220
1196
 
1221
- **Options:**
1197
+ **What it does:** Removes empty lines within function parameter lists — between parameters and after opening/before closing parentheses.
1222
1198
 
1223
- | Option | Type | Default | Description |
1224
- |--------|------|---------|-------------|
1225
- | `maxSpecifiers` | `integer` | `3` | Maximum specifiers to keep on single line |
1199
+ **Why use it:** Empty lines in parameter lists waste space and make parameters harder to scan as a group.
1226
1200
 
1227
1201
  ```javascript
1228
- "code-style/import-format": ["error", { maxSpecifiers: 4 }]
1202
+ // Good no empty lines
1203
+ function createUser(
1204
+ name,
1205
+ email,
1206
+ role,
1207
+ ) {}
1208
+
1209
+ const handler = (
1210
+ event,
1211
+ context,
1212
+ ) => {};
1213
+
1214
+ // ❌ Bad — empty line between params
1215
+ function createUser(
1216
+ name,
1217
+
1218
+ email,
1219
+
1220
+ role,
1221
+ ) {}
1222
+
1223
+ // ❌ Bad — empty line after opening paren
1224
+ const handler = (
1225
+
1226
+ event,
1227
+ context,
1228
+ ) => {};
1229
1229
  ```
1230
1230
 
1231
- ---
1231
+ <br />
1232
1232
 
1233
- ### `import-source-spacing`
1233
+ ## 🪝 Hook Rules
1234
1234
 
1235
- **What it does:** Removes any leading or trailing whitespace inside import path strings.
1235
+ ### `hook-callback-format`
1236
1236
 
1237
- **Why use it:** Spaces in module paths are almost always typos and can cause import resolution issues.
1237
+ **What it does:** Enforces consistent multi-line formatting for React hooks that take a callback and dependency array (useEffect, useCallback, useMemo, useLayoutEffect).
1238
+
1239
+ **Why use it:** Hooks with callbacks and dependencies are complex. Multi-line formatting makes the callback, return cleanup, and dependencies clearly visible.
1238
1240
 
1239
1241
  ```javascript
1240
- // ✅ Good — no extra spaces
1241
- import { Button } from "@mui/material";
1242
- import React from "react";
1243
- import styles from "./styles.css";
1242
+ // ✅ Good — callback and deps clearly separated
1243
+ useEffect(
1244
+ () => {
1245
+ fetchData();
1246
+ },
1247
+ [userId],
1248
+ );
1244
1249
 
1245
- // ❌ Bad — leading space
1246
- import { Button } from " @mui/material";
1250
+ useCallback(
1251
+ () => {
1252
+ handleSubmit(data);
1253
+ },
1254
+ [data, handleSubmit],
1255
+ );
1247
1256
 
1248
- // ❌ Bad — trailing space
1249
- import React from "react ";
1257
+ useMemo(
1258
+ () => expensiveCalculation(items),
1259
+ [items],
1260
+ );
1250
1261
 
1251
- // Badboth
1252
- import styles from " ./styles.css ";
1262
+ // Goodcleanup function visible
1263
+ useEffect(
1264
+ () => {
1265
+ const subscription = subscribe();
1266
+
1267
+ return () => subscription.unsubscribe();
1268
+ },
1269
+ [subscribe],
1270
+ );
1271
+
1272
+ // ❌ Bad — everything crammed on one line
1273
+ useEffect(() => { fetchData(); }, [userId]);
1274
+
1275
+ // ❌ Bad — hard to see dependencies
1276
+ useCallback(() => { handleSubmit(data); }, [data, handleSubmit]);
1253
1277
  ```
1254
1278
 
1255
1279
  ---
1256
1280
 
1257
- ### `index-export-style`
1281
+ ### `hook-deps-per-line`
1258
1282
 
1259
- **What it does:** Enforces different export formatting rules for index files vs regular files:
1260
- - **Index files**: No blank lines between exports, use shorthand or import-export style
1261
- - **Regular files**: Require blank lines between exports
1283
+ **What it does:** When a hook's dependency array exceeds the threshold (default: 2), each dependency goes on its own line.
1262
1284
 
1263
- **Why use it:** Index files are re-export aggregators and should be compact. Regular files benefit from spacing between exports for readability.
1285
+ **Why use it:** Long dependency arrays are hard to scan and diff. One per line makes it easy to see what changed and catch missing/extra dependencies.
1264
1286
 
1265
- **Regular files (non-index):**
1266
1287
  ```javascript
1267
- // ✅ Good — blank lines between exports
1268
- export const API_URL = "/api";
1269
-
1270
- export const MAX_RETRIES = 3;
1271
-
1272
- export const fetchData = async () => {};
1288
+ // ✅ Good — 2 or fewer deps stay inline
1289
+ useEffect(() => {}, [userId]);
1290
+ useEffect(() => {}, [userId, token]);
1273
1291
 
1274
- // Badno blank lines in regular file
1275
- export const API_URL = "/api";
1276
- export const MAX_RETRIES = 3;
1277
- export const fetchData = async () => {};
1278
- ```
1292
+ // Good3+ deps get one per line
1293
+ useEffect(
1294
+ () => {},
1295
+ [
1296
+ userId,
1297
+ token,
1298
+ refreshToken,
1299
+ ],
1300
+ );
1279
1301
 
1280
- **Index files — Style: "shorthand" (default):**
1281
- ```javascript
1282
- // ✅ Good — shorthand re-exports, no blank lines
1283
- export { Button } from "./button";
1284
- export { Input, Select } from "./form";
1285
- export { Modal } from "./modal";
1286
- export { useAuth, useUser } from "./hooks";
1287
- ```
1302
+ useCallback(
1303
+ () => handleSubmit(data),
1304
+ [
1305
+ data,
1306
+ handleSubmit,
1307
+ validateForm,
1308
+ showError,
1309
+ ],
1310
+ );
1288
1311
 
1289
- **Index filesStyle: "import-export":**
1290
- ```javascript
1291
- // ✅ Good — imports grouped, single export at bottom
1292
- import { Button } from "./button";
1293
- import { Input, Select } from "./form";
1294
- import { Modal } from "./modal";
1312
+ // Bad too many deps on one line
1313
+ useEffect(() => {}, [userId, token, refreshToken, apiUrl]);
1295
1314
 
1296
- export {
1297
- Button,
1298
- Input,
1299
- Modal,
1300
- Select,
1301
- };
1315
+ // ❌ Bad — deps should be one per line when expanded
1316
+ useEffect(() => {}, [
1317
+ userId, token, refreshToken,
1318
+ ]);
1302
1319
  ```
1303
1320
 
1304
1321
  **Options:**
1305
1322
 
1306
1323
  | Option | Type | Default | Description |
1307
1324
  |--------|------|---------|-------------|
1308
- | `style` | `"shorthand"` \| `"import-export"` | `"shorthand"` | Export style for index files |
1325
+ | `maxDeps` | `integer` | `2` | Maximum dependencies to keep on single line |
1309
1326
 
1310
1327
  ```javascript
1311
- "code-style/index-export-style": ["error", { style: "import-export" }]
1328
+ // Example: Allow up to 3 dependencies on single line
1329
+ "code-style/hook-deps-per-line": ["error", { maxDeps: 3 }]
1312
1330
  ```
1313
1331
 
1314
- ---
1332
+ <br />
1315
1333
 
1316
- ### `module-index-exports`
1334
+ ## 📥 Import/Export Rules
1317
1335
 
1318
- **What it does:** Ensures module folders have index files that export all their contents, creating a proper public API for each module.
1336
+ ### `absolute-imports-only`
1319
1337
 
1320
- **Why use it:** Index files allow importing from the folder level (`@/components`) instead of deep paths (`@/components/Button/Button`). This enforces proper module boundaries.
1338
+ **What it does:** Enforces importing from folder index files using absolute paths (aliases like `@/`) instead of relative paths or deep file imports.
1339
+
1340
+ **Why use it:**
1341
+ - Absolute imports are cleaner than `../../../components`
1342
+ - Index imports create a public API for each folder
1343
+ - Refactoring file locations doesn't break imports
1344
+ - Encourages proper module organization
1321
1345
 
1322
1346
  ```javascript
1323
- // ✅ Good — components/index.js exports everything
1324
- export { Button } from "./Button";
1325
- export { Input } from "./Input";
1326
- export { Select } from "./Select";
1327
- export { Modal } from "./Modal";
1347
+ // ✅ Good — import from index files using alias
1348
+ import { Button, Input } from "@/components";
1349
+ import { useAuth, useUser } from "@/hooks";
1350
+ import { fetchUsers } from "@/apis";
1351
+ import { formatDate } from "@/utils";
1328
1352
 
1329
- // Then consumers can import cleanly:
1330
- import { Button, Input, Select } from "@/components";
1353
+ // Good assets allow deep imports by default
1354
+ import logo from "@/assets/images/logo.png";
1331
1355
 
1332
- // ❌ Bad — missing exports in index.js
1333
- // If Button exists but isn't exported from index.js,
1334
- // consumers have to use deep imports:
1335
- import { Button } from "@/components/Button/Button"; // Avoid this!
1336
- ```
1356
+ // ❌ Bad — relative imports
1357
+ import { Button } from "../../components";
1358
+ import { useAuth } from "../../../hooks";
1337
1359
 
1338
- **Default Module Folders:**
1339
- `apis`, `assets`, `atoms`, `components`, `constants`, `contexts`, `data`, `hooks`, `layouts`, `middlewares`, `providers`, `redux`, `requests`, `routes`, `schemas`, `services`, `styles`, `theme`, `utils`, `views`
1360
+ // Bad — deep imports into component internals
1361
+ import { Button } from "@/components/buttons/primary-button";
1362
+ import { useAuth } from "@/hooks/auth/useAuth";
1363
+ import { fetchUsers } from "@/apis/users/fetchUsers";
1364
+ ```
1340
1365
 
1341
- **Default Ignore Patterns:**
1342
- `index.js`, `index.jsx`, `index.ts`, `index.tsx`, `.DS_Store`, `__tests__`, `__mocks__`, `*.test.js`, `*.test.jsx`, `*.test.ts`, `*.test.tsx`, `*.spec.js`, `*.spec.jsx`, `*.spec.ts`, `*.spec.tsx`
1366
+ **Default Allowed Folders:**
1367
+ `actions`, `apis`, `assets`, `atoms`, `components`, `config`, `configs`, `constants`, `contexts`, `data`, `enums`, `helpers`, `hooks`, `interfaces`, `layouts`, `lib`, `middlewares`, `providers`, `reducers`, `redux`, `requests`, `routes`, `schemas`, `services`, `store`, `styles`, `theme`, `thunks`, `types`, `ui`, `utils`, `utilities`, `views`
1343
1368
 
1344
1369
  **Customization Options:**
1345
1370
 
1346
1371
  | Option | Type | Description |
1347
1372
  |--------|------|-------------|
1348
- | `extraModuleFolders` | `string[]` | Add extra module folders to the default list |
1349
- | `extraLazyLoadFolders` | `string[]` | Add extra lazy load folders (default: `views`) |
1350
- | `extraIgnorePatterns` | `string[]` | Add extra ignore patterns (supports wildcards) |
1351
- | `moduleFolders` | `string[]` | Replace default module folders entirely |
1352
- | `lazyLoadFolders` | `string[]` | Replace default lazy load folders entirely |
1353
- | `ignorePatterns` | `string[]` | Replace default ignore patterns entirely |
1373
+ | `extraAllowedFolders` | `string[]` | Add extra folders to the default list |
1374
+ | `extraReduxSubfolders` | `string[]` | Add extra redux subfolders (default: `actions`, `reducers`, `store`, `thunks`, `types`) |
1375
+ | `extraDeepImportFolders` | `string[]` | Add extra folders that allow deep imports (default: `assets`) |
1376
+ | `aliasPrefix` | `string` | Change the import alias prefix (default: `@/`) |
1377
+ | `allowedFolders` | `string[]` | Replace default folders entirely |
1378
+ | `reduxSubfolders` | `string[]` | Replace default redux subfolders entirely |
1379
+ | `deepImportFolders` | `string[]` | Replace default deep import folders entirely |
1354
1380
 
1355
1381
  ```javascript
1356
- // Example: Add custom folders and patterns
1357
- "code-style/module-index-exports": ["error", {
1358
- extraModuleFolders: ["features", "modules", "lib"],
1359
- extraLazyLoadFolders: ["pages"],
1360
- extraIgnorePatterns: ["*.stories.js", "*.mock.js"]
1382
+ // Example: Add custom folders to the defaults
1383
+ "code-style/absolute-imports-only": ["error", {
1384
+ extraAllowedFolders: ["features", "modules", "lib"],
1385
+ extraDeepImportFolders: ["images", "fonts"]
1361
1386
  }]
1362
1387
  ```
1363
1388
 
1364
- <br />
1365
-
1366
- ## ⚛️ JSX Rules
1389
+ ---
1367
1390
 
1368
- ### `jsx-children-on-new-line`
1391
+ ### `export-format`
1369
1392
 
1370
- **What it does:** When a JSX element has multiple children, ensures each child is on its own line with proper indentation.
1393
+ **What it does:** Formats export statements consistently:
1394
+ - `export {` always on the same line as `export` keyword
1395
+ - ≤3 specifiers stay on one line (collapsed)
1396
+ - 4+ specifiers get one per line (expanded)
1397
+ - Proper spacing and trailing commas
1371
1398
 
1372
- **Why use it:** Multiple children on one line are hard to scan. Individual lines make the component structure clear.
1399
+ **Why use it:** Consistent export formatting improves readability. Short exports stay compact, long exports become scannable.
1373
1400
 
1374
1401
  ```javascript
1375
- // ✅ Good — each child on its own line
1376
- <Container>
1377
- <Header />
1378
- <Content />
1379
- <Footer />
1380
- </Container>
1381
-
1382
- <Form>
1383
- <Input name="email" />
1384
- <Input name="password" />
1385
- <Button type="submit">Login</Button>
1386
- </Form>
1387
-
1388
- // ✅ Good — single child can stay inline
1389
- <Button><Icon /></Button>
1402
+ // ✅ Good — 3 or fewer specifiers stay compact
1403
+ export { Button };
1404
+ export { Button, Input };
1405
+ export { Button, Input, Select };
1390
1406
 
1391
- // Badmultiple children crammed together
1407
+ // Good4+ specifiers expand with one per line
1408
+ export {
1409
+ Button,
1410
+ Input,
1411
+ Select,
1412
+ Checkbox,
1413
+ };
1414
+
1415
+ // ✅ Good — re-exports follow same rules
1416
+ export { Button, Input, Select } from "./components";
1417
+ export {
1418
+ createUser,
1419
+ updateUser,
1420
+ deleteUser,
1421
+ getUser,
1422
+ } from "./api";
1423
+
1424
+ // ❌ Bad — no spaces
1425
+ export {Button,Input,Select};
1426
+
1427
+ // ❌ Bad — keyword on different line
1428
+ export
1429
+ { Button };
1430
+
1431
+ // ❌ Bad — too many on one line
1432
+ export { Button, Input, Select, Checkbox, Radio };
1433
+ ```
1434
+
1435
+ **Options:**
1436
+
1437
+ | Option | Type | Default | Description |
1438
+ |--------|------|---------|-------------|
1439
+ | `maxSpecifiers` | `integer` | `3` | Maximum specifiers to keep on single line |
1440
+
1441
+ ```javascript
1442
+ "code-style/export-format": ["error", { maxSpecifiers: 4 }]
1443
+ ```
1444
+
1445
+ ---
1446
+
1447
+ ### `import-format`
1448
+
1449
+ **What it does:** Formats import statements consistently:
1450
+ - `import {` on the same line as `import` keyword
1451
+ - `} from` on the same line as closing brace
1452
+ - ≤3 specifiers stay on one line (collapsed)
1453
+ - 4+ specifiers get one per line (expanded)
1454
+
1455
+ **Why use it:** Consistent import formatting improves readability and makes diffs cleaner when adding/removing imports.
1456
+
1457
+ ```javascript
1458
+ // ✅ Good — 3 or fewer specifiers stay compact
1459
+ import { useState } from "react";
1460
+ import { Button, Input } from "@/components";
1461
+ import { get, post, put } from "@/api";
1462
+
1463
+ // ✅ Good — 4+ specifiers expand with one per line
1464
+ import {
1465
+ useState,
1466
+ useEffect,
1467
+ useCallback,
1468
+ useMemo,
1469
+ } from "react";
1470
+
1471
+ import {
1472
+ Button,
1473
+ Input,
1474
+ Select,
1475
+ Checkbox,
1476
+ } from "@/components";
1477
+
1478
+ // ❌ Bad — no spaces
1479
+ import {useState,useEffect} from "react";
1480
+
1481
+ // ❌ Bad — keyword on different line
1482
+ import
1483
+ { Button } from "@/components";
1484
+
1485
+ // ❌ Bad — from on different line
1486
+ import { Button }
1487
+ from "@/components";
1488
+
1489
+ // ❌ Bad — too many on one line
1490
+ import { useState, useEffect, useCallback, useMemo, useRef } from "react";
1491
+ ```
1492
+
1493
+ **Options:**
1494
+
1495
+ | Option | Type | Default | Description |
1496
+ |--------|------|---------|-------------|
1497
+ | `maxSpecifiers` | `integer` | `3` | Maximum specifiers to keep on single line |
1498
+
1499
+ ```javascript
1500
+ "code-style/import-format": ["error", { maxSpecifiers: 4 }]
1501
+ ```
1502
+
1503
+ ---
1504
+
1505
+ ### `import-source-spacing`
1506
+
1507
+ **What it does:** Removes any leading or trailing whitespace inside import path strings.
1508
+
1509
+ **Why use it:** Spaces in module paths are almost always typos and can cause import resolution issues.
1510
+
1511
+ ```javascript
1512
+ // ✅ Good — no extra spaces
1513
+ import { Button } from "@mui/material";
1514
+ import React from "react";
1515
+ import styles from "./styles.css";
1516
+
1517
+ // ❌ Bad — leading space
1518
+ import { Button } from " @mui/material";
1519
+
1520
+ // ❌ Bad — trailing space
1521
+ import React from "react ";
1522
+
1523
+ // ❌ Bad — both
1524
+ import styles from " ./styles.css ";
1525
+ ```
1526
+
1527
+ ---
1528
+
1529
+ ### `index-export-style`
1530
+
1531
+ **What it does:** Enforces different export formatting rules for index files vs regular files:
1532
+ - **Index files**: No blank lines between exports, use shorthand or import-export style
1533
+ - **Regular files**: Require blank lines between exports
1534
+
1535
+ **Why use it:** Index files are re-export aggregators and should be compact. Regular files benefit from spacing between exports for readability.
1536
+
1537
+ **Regular files (non-index):**
1538
+ ```javascript
1539
+ // ✅ Good — blank lines between exports
1540
+ export const API_URL = "/api";
1541
+
1542
+ export const MAX_RETRIES = 3;
1543
+
1544
+ export const fetchData = async () => {};
1545
+
1546
+ // ❌ Bad — no blank lines in regular file
1547
+ export const API_URL = "/api";
1548
+ export const MAX_RETRIES = 3;
1549
+ export const fetchData = async () => {};
1550
+ ```
1551
+
1552
+ **Index files — Style: "shorthand" (default):**
1553
+ ```javascript
1554
+ // ✅ Good — shorthand re-exports, no blank lines
1555
+ export { Button } from "./button";
1556
+ export { Input, Select } from "./form";
1557
+ export { Modal } from "./modal";
1558
+ export { useAuth, useUser } from "./hooks";
1559
+ ```
1560
+
1561
+ **Index files — Style: "import-export":**
1562
+ ```javascript
1563
+ // ✅ Good — imports grouped, single export at bottom
1564
+ import { Button } from "./button";
1565
+ import { Input, Select } from "./form";
1566
+ import { Modal } from "./modal";
1567
+
1568
+ export {
1569
+ Button,
1570
+ Input,
1571
+ Modal,
1572
+ Select,
1573
+ };
1574
+ ```
1575
+
1576
+ **Options:**
1577
+
1578
+ | Option | Type | Default | Description |
1579
+ |--------|------|---------|-------------|
1580
+ | `style` | `"shorthand"` \| `"import-export"` | `"shorthand"` | Export style for index files |
1581
+
1582
+ ```javascript
1583
+ "code-style/index-export-style": ["error", { style: "import-export" }]
1584
+ ```
1585
+
1586
+ ---
1587
+
1588
+ ### `module-index-exports`
1589
+
1590
+ **What it does:** Ensures module folders have index files that export all their contents, creating a proper public API for each module.
1591
+
1592
+ **Why use it:** Index files allow importing from the folder level (`@/components`) instead of deep paths (`@/components/Button/Button`). This enforces proper module boundaries.
1593
+
1594
+ ```javascript
1595
+ // ✅ Good — components/index.js exports everything
1596
+ export { Button } from "./Button";
1597
+ export { Input } from "./Input";
1598
+ export { Select } from "./Select";
1599
+ export { Modal } from "./Modal";
1600
+
1601
+ // Then consumers can import cleanly:
1602
+ import { Button, Input, Select } from "@/components";
1603
+
1604
+ // ❌ Bad — missing exports in index.js
1605
+ // If Button exists but isn't exported from index.js,
1606
+ // consumers have to use deep imports:
1607
+ import { Button } from "@/components/Button/Button"; // Avoid this!
1608
+ ```
1609
+
1610
+ **Default Module Folders:**
1611
+ `actions`, `apis`, `assets`, `atoms`, `components`, `config`, `configs`, `constants`, `contexts`, `data`, `enums`, `helpers`, `hooks`, `interfaces`, `layouts`, `lib`, `middlewares`, `providers`, `reducers`, `redux`, `requests`, `routes`, `schemas`, `services`, `store`, `styles`, `theme`, `thunks`, `types`, `ui`, `utils`, `utilities`, `views`
1612
+
1613
+ **Default Ignore Patterns:**
1614
+ `index.js`, `index.jsx`, `index.ts`, `index.tsx`, `.DS_Store`, `__tests__`, `__mocks__`, `*.test.js`, `*.test.jsx`, `*.test.ts`, `*.test.tsx`, `*.spec.js`, `*.spec.jsx`, `*.spec.ts`, `*.spec.tsx`
1615
+
1616
+ **Customization Options:**
1617
+
1618
+ | Option | Type | Description |
1619
+ |--------|------|-------------|
1620
+ | `extraModuleFolders` | `string[]` | Add extra module folders to the default list |
1621
+ | `extraLazyLoadFolders` | `string[]` | Add extra lazy load folders (default: `views`) |
1622
+ | `extraIgnorePatterns` | `string[]` | Add extra ignore patterns (supports wildcards) |
1623
+ | `moduleFolders` | `string[]` | Replace default module folders entirely |
1624
+ | `lazyLoadFolders` | `string[]` | Replace default lazy load folders entirely |
1625
+ | `ignorePatterns` | `string[]` | Replace default ignore patterns entirely |
1626
+
1627
+ ```javascript
1628
+ // Example: Add custom folders and patterns
1629
+ "code-style/module-index-exports": ["error", {
1630
+ extraModuleFolders: ["features", "modules", "lib"],
1631
+ extraLazyLoadFolders: ["pages"],
1632
+ extraIgnorePatterns: ["*.stories.js", "*.mock.js"]
1633
+ }]
1634
+ ```
1635
+
1636
+ <br />
1637
+
1638
+ ## ⚛️ JSX Rules
1639
+
1640
+ ### `jsx-children-on-new-line`
1641
+
1642
+ **What it does:** When a JSX element has multiple children, ensures each child is on its own line with proper indentation.
1643
+
1644
+ **Why use it:** Multiple children on one line are hard to scan. Individual lines make the component structure clear.
1645
+
1646
+ ```javascript
1647
+ // ✅ Good — each child on its own line
1648
+ <Container>
1649
+ <Header />
1650
+ <Content />
1651
+ <Footer />
1652
+ </Container>
1653
+
1654
+ <Form>
1655
+ <Input name="email" />
1656
+ <Input name="password" />
1657
+ <Button type="submit">Login</Button>
1658
+ </Form>
1659
+
1660
+ // ✅ Good — single child can stay inline
1661
+ <Button><Icon /></Button>
1662
+
1663
+ // ❌ Bad — multiple children crammed together
1392
1664
  <Container><Header /><Content /><Footer /></Container>
1393
1665
 
1394
1666
  // ❌ Bad — inconsistent formatting
@@ -1702,279 +1974,9 @@ function Card() {
1702
1974
 
1703
1975
  // ❌ Bad — empty line before closing tag
1704
1976
  <div>
1705
- <Content />
1706
-
1707
- </div>
1708
- ```
1709
-
1710
- <br />
1711
-
1712
- ## 📞 Call Expression Rules
1713
-
1714
- ### `function-arguments-format`
1715
-
1716
- **What it does:** Enforces consistent formatting for function call arguments:
1717
- - Single simple argument stays on one line
1718
- - 2+ arguments get one per line
1719
- - Multiline arguments trigger full expansion
1720
- - React hooks are skipped by default (they have their own rule)
1721
-
1722
- **Why use it:** Consistent argument formatting makes function calls scannable and diffs clean when adding/removing arguments.
1723
-
1724
- ```javascript
1725
- // ✅ Good — single argument stays compact
1726
- fetchUser(userId);
1727
- console.log(message);
1728
- dispatch(action);
1729
-
1730
- // ✅ Good — 2+ arguments get one per line
1731
- setValue(
1732
- "email",
1733
- "user@example.com",
1734
- );
1735
-
1736
- createUser(
1737
- name,
1738
- email,
1739
- password,
1740
- );
1741
-
1742
- // ✅ Good — multiline argument triggers expansion
1743
- processData(
1744
- {
1745
- id: 1,
1746
- name: "test",
1747
- },
1748
- );
1749
-
1750
- // ✅ Good — callback with body triggers expansion
1751
- items.forEach(
1752
- (item) => {
1753
- process(item);
1754
- save(item);
1755
- },
1756
- );
1757
-
1758
- // ❌ Bad — multiple arguments on same line
1759
- setValue("email", "user@example.com");
1760
- createUser(name, email, password);
1761
-
1762
- // ❌ Bad — inconsistent formatting
1763
- fn(arg1,
1764
- arg2, arg3);
1765
- ```
1766
-
1767
- **Options:**
1768
-
1769
- | Option | Type | Default | Description |
1770
- |--------|------|---------|-------------|
1771
- | `minArgs` | `integer` | `2` | Minimum arguments to enforce multiline |
1772
- | `skipHooks` | `boolean` | `true` | Skip React hooks (useEffect, etc.) |
1773
- | `skipSingleArg` | `boolean` | `true` | Skip calls with single complex argument |
1774
-
1775
- ```javascript
1776
- // Example: Require multiline for 3+ arguments
1777
- "code-style/function-arguments-format": ["error", { minArgs: 3 }]
1778
-
1779
- // Example: Don't skip React hooks
1780
- "code-style/function-arguments-format": ["error", { skipHooks: false }]
1781
- ```
1782
-
1783
- ---
1784
-
1785
- ### `nested-call-closing-brackets`
1786
-
1787
- **What it does:** Ensures nested function calls (common in styled-components, HOCs) have closing brackets on the same line: `}));`
1788
-
1789
- **Why use it:** Scattered closing brackets (`}\n);\n` ) waste vertical space and make it harder to see where expressions end.
1790
-
1791
- ```javascript
1792
- // ✅ Good — closing brackets together
1793
- const StyledCard = styled(Card)(({ theme }) => ({
1794
- color: theme.palette.text.primary,
1795
- padding: theme.spacing(2),
1796
- }));
1797
-
1798
- const StyledButton = styled("button")(({ theme }) => ({
1799
- backgroundColor: theme.colors.primary,
1800
- }));
1801
-
1802
- // ✅ Good — multiple levels
1803
- const Component = connect(
1804
- mapStateToProps,
1805
- mapDispatchToProps,
1806
- )(withRouter(MyComponent));
1807
-
1808
- // ❌ Bad — closing brackets scattered
1809
- const StyledCard = styled(Card)(({ theme }) => ({
1810
- color: theme.palette.text.primary,
1811
- })
1812
- );
1813
-
1814
- // ❌ Bad — each bracket on its own line
1815
- const StyledCard = styled(Card)(({ theme }) => ({
1816
- color: theme.colors.primary,
1817
- })
1818
- )
1819
- ;
1820
- ```
1821
-
1822
- ---
1823
-
1824
- ### `no-empty-lines-in-function-calls`
1825
-
1826
- **What it does:** Removes empty lines within function call argument lists — between arguments and after opening/before closing parentheses.
1827
-
1828
- **Why use it:** Empty lines between arguments break visual grouping. Arguments should flow as a cohesive list.
1829
-
1830
- ```javascript
1831
- // ✅ Good — no empty lines
1832
- createUser(
1833
- name,
1834
- email,
1835
- password,
1836
- role,
1837
- );
1838
-
1839
- fetchData(
1840
- url,
1841
- {
1842
- method: "POST",
1843
- body: data,
1844
- },
1845
- );
1846
-
1847
- // ❌ Bad — empty line between arguments
1848
- createUser(
1849
- name,
1850
-
1851
- email,
1852
-
1853
- password,
1854
- );
1855
-
1856
- // ❌ Bad — empty line after opening paren
1857
- fetchData(
1858
-
1859
- url,
1860
- options,
1861
- );
1862
-
1863
- // ❌ Bad — empty line before closing paren
1864
- fetchData(
1865
- url,
1866
- options,
1867
-
1868
- );
1869
- ```
1870
-
1871
- ---
1872
-
1873
- ### `opening-brackets-same-line`
1874
-
1875
- **What it does:** Ensures opening brackets (`{`, `[`, `(`) in function arguments stay on the same line as the function call.
1876
-
1877
- **Why use it:** Opening brackets on new lines create unnecessary indentation and vertical space.
1878
-
1879
- ```javascript
1880
- // ✅ Good — brackets on same line as call
1881
- fn({ key: value });
1882
- process([1, 2, 3]);
1883
- items.map(({ id }) => id);
1884
- configure({ debug: true });
1885
-
1886
- // ✅ Good — multiline content is fine
1887
- fn({
1888
- key: value,
1889
- other: data,
1890
- });
1891
-
1892
- items.map(({ id, name }) => (
1893
- <Item key={id} name={name} />
1894
- ));
1895
-
1896
- // ❌ Bad — opening bracket on new line
1897
- fn(
1898
- { key: value }
1899
- );
1900
-
1901
- process(
1902
- [1, 2, 3]
1903
- );
1904
-
1905
- items.map(
1906
- ({ id }) => id
1907
- );
1908
- ```
1909
-
1910
- ---
1911
-
1912
- ### `simple-call-single-line`
1913
-
1914
- **What it does:** Collapses simple function calls with an arrow function containing a single call expression onto one line.
1915
-
1916
- **Why use it:** Common patterns like `lazy(() => import(...))` don't need multiline formatting. Single line is cleaner.
1917
-
1918
- ```javascript
1919
- // ✅ Good — simple patterns on one line
1920
- const Page = lazy(() => import("./Page"));
1921
- const Modal = lazy(() => import("./Modal"));
1922
- setTimeout(() => callback(), 100);
1923
- requestAnimationFrame(() => render());
1924
- items.filter(() => isValid(item));
1925
-
1926
- // ✅ Good — complex callbacks stay multiline
1927
- const Page = lazy(() => {
1928
- console.log("Loading page");
1929
- return import("./Page");
1930
- });
1931
-
1932
- // ❌ Bad — unnecessary multiline for simple pattern
1933
- const Page = lazy(
1934
- () => import("./Page"),
1935
- );
1936
-
1937
- setTimeout(
1938
- () => callback(),
1939
- 100,
1940
- );
1941
- ```
1942
-
1943
- ---
1944
-
1945
- ### `single-argument-on-one-line`
1946
-
1947
- **What it does:** Ensures function calls with a single simple argument (literal, identifier, member expression) stay on one line.
1948
-
1949
- **Why use it:** Single-argument calls don't need multiline formatting. Expanding them wastes vertical space.
1950
-
1951
- ```javascript
1952
- // ✅ Good — single argument on one line
1953
- fetchUser(userId);
1954
- console.log(message);
1955
- process(data.items);
1956
- dispatch(action);
1957
- setValue("key");
1958
- getElement(document.body);
1959
-
1960
- // ✅ Good — complex single argument can be multiline
1961
- processConfig({
1962
- key: value,
1963
- other: data,
1964
- });
1965
-
1966
- // ❌ Bad — simple argument expanded unnecessarily
1967
- fetchUser(
1968
- userId,
1969
- );
1970
-
1971
- console.log(
1972
- message,
1973
- );
1977
+ <Content />
1974
1978
 
1975
- dispatch(
1976
- action,
1977
- );
1979
+ </div>
1978
1980
  ```
1979
1981
 
1980
1982
  <br />
@@ -2200,6 +2202,611 @@ const styles = {
2200
2202
 
2201
2203
  <br />
2202
2204
 
2205
+ ## 📐 Spacing Rules
2206
+
2207
+ ### `assignment-value-same-line`
2208
+
2209
+ **What it does:** Ensures the assigned value starts on the same line as the `=` sign, not on a new line.
2210
+
2211
+ **Why use it:** Breaking after `=` creates awkward formatting and wastes vertical space. Keeping values on the same line as `=` is more readable.
2212
+
2213
+ ```javascript
2214
+ // ✅ Good — value starts on same line as =
2215
+ const name = "John";
2216
+ const config = {
2217
+ host: "localhost",
2218
+ port: 3000,
2219
+ };
2220
+ const items = [
2221
+ "first",
2222
+ "second",
2223
+ ];
2224
+
2225
+ // ❌ Bad — value on new line after =
2226
+ const name =
2227
+ "John";
2228
+ const config =
2229
+ {
2230
+ host: "localhost",
2231
+ port: 3000,
2232
+ };
2233
+ const items =
2234
+ [
2235
+ "first",
2236
+ "second",
2237
+ ];
2238
+ ```
2239
+
2240
+ ---
2241
+
2242
+ ### `member-expression-bracket-spacing`
2243
+
2244
+ **What it does:** Removes spaces inside brackets in computed member expressions (array access, dynamic property access).
2245
+
2246
+ **Why use it:** Consistent with JavaScript conventions. Spaces inside brackets look inconsistent with array literals and other bracket usage.
2247
+
2248
+ ```javascript
2249
+ // ✅ Good — no spaces inside brackets
2250
+ const value = arr[0];
2251
+ const name = obj[key];
2252
+ const item = data[index];
2253
+ const nested = matrix[row][col];
2254
+
2255
+ // ❌ Bad — spaces inside brackets
2256
+ const value = arr[ 0 ];
2257
+ const name = obj[ key ];
2258
+ const item = data[ index ];
2259
+ ```
2260
+
2261
+ <br />
2262
+
2263
+ ## 🧩 Component Rules
2264
+
2265
+ ### `component-props-destructure`
2266
+
2267
+ **What it does:** Enforces that React component props must be destructured in the function parameter, not received as a single `props` object.
2268
+
2269
+ **Why use it:** Destructured props make it immediately clear what props a component uses. It improves readability and helps catch unused props.
2270
+
2271
+ ```typescript
2272
+ // ✅ Good — props are destructured
2273
+ export const Button = ({ label, onClick, variant = "primary" }) => (
2274
+ <button onClick={onClick} type="button">
2275
+ {label}
2276
+ </button>
2277
+ );
2278
+
2279
+ export const Card = ({
2280
+ children,
2281
+ className = "",
2282
+ title,
2283
+ } : {
2284
+ children: ReactNode,
2285
+ className?: string,
2286
+ title: string,
2287
+ }) => (
2288
+ <div className={className}>
2289
+ <h2>{title}</h2>
2290
+ {children}
2291
+ </div>
2292
+ );
2293
+
2294
+ // ❌ Bad — props received as single object
2295
+ export const Button = (props) => (
2296
+ <button onClick={props.onClick} type="button">
2297
+ {props.label}
2298
+ </button>
2299
+ );
2300
+
2301
+ export const Card = (props: CardPropsInterface) => (
2302
+ <div className={props.className}>
2303
+ <h2>{props.title}</h2>
2304
+ {props.children}
2305
+ </div>
2306
+ );
2307
+ ```
2308
+
2309
+ ---
2310
+
2311
+ ### `component-props-inline-type`
2312
+
2313
+ **What it does:** Enforces that React component props must use inline type annotation instead of referencing an interface or type alias. Also enforces:
2314
+ - Exactly one space before and after colon: `} : {`
2315
+ - Props in type must match exactly with destructured props (no missing or extra)
2316
+ - Each prop type on its own line when there are multiple props
2317
+ - First prop type must be on new line after `{` when multiple props
2318
+ - No empty lines after opening brace or before closing brace
2319
+ - No space before `?` in optional properties (`prop?: type` not `prop ?: type`)
2320
+ - Trailing commas (not semicolons) for each prop type
2321
+ - No empty lines between prop types
2322
+
2323
+ **Why use it:** Inline types keep the prop definitions colocated with the component, making it easier to understand and modify the component without jumping to separate interface definitions. Enforcing prop matching ensures type safety and prevents unused type properties.
2324
+
2325
+ ```typescript
2326
+ // ✅ Good — inline type annotation with matching props
2327
+ export const Button = ({ label } : { label: string }) => (
2328
+ <button type="button">{label}</button>
2329
+ );
2330
+
2331
+ export const Card = ({
2332
+ className = "",
2333
+ description,
2334
+ title,
2335
+ } : {
2336
+ className?: string,
2337
+ description?: string,
2338
+ title: string,
2339
+ }) => (
2340
+ <div className={className}>
2341
+ <h1>{title}</h1>
2342
+ {description && <p>{description}</p>}
2343
+ </div>
2344
+ );
2345
+
2346
+ // ❌ Bad — interface reference instead of inline type
2347
+ interface ButtonPropsInterface {
2348
+ label: string,
2349
+ }
2350
+ export const Button = ({ label }: ButtonPropsInterface) => (
2351
+ <button type="button">{label}</button>
2352
+ );
2353
+
2354
+ // ❌ Bad — missing space before and after colon
2355
+ export const Button = ({ label }:{ label: string }) => (
2356
+ <button type="button">{label}</button>
2357
+ );
2358
+
2359
+ // ❌ Bad — props don't match (extra 'flag' in type, missing in destructured)
2360
+ export const Card = ({
2361
+ title,
2362
+ } : {
2363
+ flag: boolean,
2364
+ title: string,
2365
+ }) => (
2366
+ <div>{title}</div>
2367
+ );
2368
+
2369
+ // ❌ Bad — semicolons instead of commas
2370
+ export const Card = ({ title } : { title: string; }) => (
2371
+ <div>{title}</div>
2372
+ );
2373
+
2374
+ // ❌ Bad — first prop on same line as opening brace
2375
+ export const Card = ({
2376
+ title,
2377
+ } : { title: string,
2378
+ className?: string,
2379
+ }) => (
2380
+ <div>{title}</div>
2381
+ );
2382
+
2383
+ // ❌ Bad — space before ? in optional property
2384
+ export const Card = ({ title } : { title ?: string }) => (
2385
+ <div>{title}</div>
2386
+ );
2387
+
2388
+ // ❌ Bad — props on same line when multiple
2389
+ export const Card = ({ a, b } : { a: string, b: string }) => (
2390
+ <div>{a}{b}</div>
2391
+ );
2392
+ ```
2393
+
2394
+ <br />
2395
+
2396
+ ## 🔷 TypeScript Rules
2397
+
2398
+ ### `enum-format`
2399
+
2400
+ **What it does:** Enforces consistent formatting for TypeScript enums:
2401
+ - Enum names must be PascalCase and end with `Enum` suffix
2402
+ - Enum members must be UPPER_CASE (for string enums) or PascalCase (for numeric enums)
2403
+ - No empty lines between enum members
2404
+ - Members must end with commas, not semicolons
2405
+
2406
+ **Why use it:** Consistent enum naming makes enums instantly recognizable. UPPER_CASE members follow common conventions for constants.
2407
+
2408
+ ```typescript
2409
+ // ✅ Good — proper enum format
2410
+ export enum StatusEnum {
2411
+ ACTIVE = "active",
2412
+ INACTIVE = "inactive",
2413
+ PENDING = "pending",
2414
+ }
2415
+
2416
+ export enum HttpMethodEnum {
2417
+ DELETE = "DELETE",
2418
+ GET = "GET",
2419
+ POST = "POST",
2420
+ PUT = "PUT",
2421
+ }
2422
+
2423
+ // ❌ Bad — wrong naming
2424
+ export enum Status { // Missing Enum suffix
2425
+ active = "active", // Should be UPPER_CASE
2426
+ inactive = "inactive"; // Should use comma, not semicolon
2427
+ }
2428
+
2429
+ // ❌ Bad — empty lines between members
2430
+ export enum UserStatusEnum {
2431
+ ACTIVE = "active",
2432
+
2433
+ INACTIVE = "inactive",
2434
+ }
2435
+ ```
2436
+
2437
+ ---
2438
+
2439
+ ### `interface-format`
2440
+
2441
+ **What it does:** Enforces consistent formatting for TypeScript interfaces:
2442
+ - Interface names must be PascalCase and end with `Interface` suffix
2443
+ - Properties must be camelCase
2444
+ - No empty lines between properties
2445
+ - Properties must end with commas, not semicolons
2446
+
2447
+ **Why use it:** Consistent interface naming makes interfaces instantly recognizable. The suffix clearly distinguishes interfaces from types and classes.
2448
+
2449
+ ```typescript
2450
+ // ✅ Good — proper interface format
2451
+ export interface UserInterface {
2452
+ email: string,
2453
+ id: string,
2454
+ isActive: boolean,
2455
+ name: string,
2456
+ }
2457
+
2458
+ export interface ApiResponseInterface<T> {
2459
+ data: T,
2460
+ error: string | null,
2461
+ status: number,
2462
+ success: boolean,
2463
+ }
2464
+
2465
+ // ❌ Bad — wrong naming
2466
+ export interface User { // Missing Interface suffix
2467
+ Email: string; // Should be camelCase
2468
+ ID: string; // Should be camelCase
2469
+ is_active: boolean; // Should be camelCase, use comma
2470
+ }
2471
+
2472
+ // ❌ Bad — semicolons and empty lines
2473
+ export interface UserInterface {
2474
+ email: string; // Should use comma
2475
+
2476
+ name: string; // Empty line not allowed
2477
+ }
2478
+ ```
2479
+
2480
+ ---
2481
+
2482
+ ### `type-format`
2483
+
2484
+ **What it does:** Enforces consistent formatting for TypeScript type aliases:
2485
+ - Type names must be PascalCase and end with `Type` suffix
2486
+ - Properties must be camelCase
2487
+ - No empty lines between properties
2488
+ - Properties must end with commas, not semicolons
2489
+
2490
+ **Why use it:** Consistent type naming makes types instantly recognizable. The suffix clearly distinguishes types from interfaces and classes.
2491
+
2492
+ ```typescript
2493
+ // ✅ Good — proper type format
2494
+ export type UserType = {
2495
+ email: string,
2496
+ id: string,
2497
+ name: string,
2498
+ };
2499
+
2500
+ export type ApiResponseType<T> = {
2501
+ data: T,
2502
+ error: string | null,
2503
+ status: number,
2504
+ };
2505
+
2506
+ // ❌ Bad — wrong naming
2507
+ export type User = { // Missing Type suffix
2508
+ Email: string; // Should be camelCase
2509
+ ID: string; // Should use comma
2510
+ };
2511
+
2512
+ // ❌ Bad — empty lines
2513
+ export type ConfigType = {
2514
+ debug: boolean,
2515
+
2516
+ port: number, // Empty line not allowed
2517
+ };
2518
+ ```
2519
+
2520
+ ---
2521
+
2522
+ ### `type-annotation-spacing`
2523
+
2524
+ **What it does:** Enforces consistent spacing in TypeScript type annotations:
2525
+ - No space before the colon in type annotations: `name: string` not `name : string`
2526
+ - One space after the colon: `name: string` not `name:string`
2527
+ - No space before generic type parameters: `Array<T>` not `Array <T>`
2528
+ - No space before array brackets: `string[]` not `string []`
2529
+
2530
+ **Why use it:** Consistent type annotation spacing follows TypeScript conventions and improves code readability.
2531
+
2532
+ ```typescript
2533
+ // ✅ Good — proper spacing
2534
+ const name: string = "John";
2535
+ const items: string[] = [];
2536
+ const data: Array<number> = [];
2537
+ const handler = (value: string): boolean => true;
2538
+
2539
+ function getData<T>(id: string): Promise<T> {
2540
+ return fetch(id);
2541
+ }
2542
+
2543
+ // ❌ Bad — space before colon
2544
+ const name : string = "John";
2545
+ const handler = (value : string) : boolean => true;
2546
+
2547
+ // ❌ Bad — no space after colon
2548
+ const name:string = "John";
2549
+ const handler = (value:string):boolean => true;
2550
+
2551
+ // ❌ Bad — space before generic
2552
+ const data: Array <number> = [];
2553
+ function getData <T>(id: string): Promise <T> {}
2554
+
2555
+ // ❌ Bad — space before array brackets
2556
+ const items: string [] = [];
2557
+ ```
2558
+
2559
+ ---
2560
+
2561
+ ### `typescript-definition-location`
2562
+
2563
+ **What it does:** Enforces that TypeScript definitions are placed in their designated folders:
2564
+ - Interfaces must be in files inside the `interfaces` folder
2565
+ - Types must be in files inside the `types` folder
2566
+ - Enums must be in files inside the `enums` folder
2567
+
2568
+ **Why use it:** Separating type definitions by category makes them easier to find, maintain, and share across the codebase. It promotes a clean and organized project structure.
2569
+
2570
+ ```typescript
2571
+ // ✅ Good — definitions in correct folders
2572
+ // src/interfaces/user.ts
2573
+ export interface UserInterface {
2574
+ id: string,
2575
+ name: string,
2576
+ }
2577
+
2578
+ // src/types/config.ts
2579
+ export type ConfigType = {
2580
+ apiUrl: string,
2581
+ timeout: number,
2582
+ };
2583
+
2584
+ // src/enums/status.ts
2585
+ export enum UserRoleEnum {
2586
+ ADMIN = "admin",
2587
+ USER = "user",
2588
+ }
2589
+
2590
+ // ❌ Bad — definitions in wrong folders
2591
+ // src/components/user-card.tsx
2592
+ interface UserProps { // Should be in interfaces folder
2593
+ name: string,
2594
+ }
2595
+
2596
+ // src/types/user.ts
2597
+ export interface UserInterface { // Should be in interfaces folder, not types
2598
+ id: string,
2599
+ }
2600
+
2601
+ export enum StatusEnum { // Should be in enums folder, not types
2602
+ ACTIVE = "active",
2603
+ }
2604
+ ```
2605
+
2606
+ <br />
2607
+
2608
+ ## ⚛️ React Rules
2609
+
2610
+ ### `react-code-order`
2611
+
2612
+ **What it does:** Enforces a consistent ordering of code blocks within React components and custom hooks. The order follows a logical dependency chain where declarations appear before their usage.
2613
+
2614
+ **Order (top to bottom):**
2615
+ 1. Props/params destructure (in function signature: `({ prop1, prop2 })`)
2616
+ 2. Props/params destructure in body (`const { x } = propValue` where propValue is a prop)
2617
+ 3. `useRef` declarations
2618
+ 4. `useState` declarations
2619
+ 5. `useReducer` declarations
2620
+ 6. `useSelector` / `useDispatch` (Redux hooks)
2621
+ 7. Router hooks (`useNavigate`, `useLocation`, `useParams`, `useSearchParams`)
2622
+ 8. Context hooks (`useContext`, `useToast`, etc.)
2623
+ 9. Custom hooks (`use*` pattern)
2624
+ 10. Derived state / variables (computed from hooks above, e.g., `const isSearching = term.length > 0`)
2625
+ 11. `useMemo` declarations
2626
+ 12. `useCallback` declarations
2627
+ 13. Handler functions (`const handleX = () => {}`)
2628
+ 14. `useEffect` / `useLayoutEffect`
2629
+ 15. Return statement
2630
+
2631
+ **Why use it:** A consistent code structure makes components and hooks predictable and easier to navigate. Placing hooks before derived values ensures dependencies are defined before use. Effects come last because they typically depend on everything else.
2632
+
2633
+ ```typescript
2634
+ // ✅ Good — Component follows the correct order
2635
+ const UserDashboard = ({ title }) => {
2636
+ // 1. useRef
2637
+ const inputRef = useRef(null);
2638
+
2639
+ // 2. useState
2640
+ const [count, setCount] = useState(0);
2641
+ const [isLoading, setIsLoading] = useState(false);
2642
+
2643
+ // 3. Redux hooks
2644
+ const dispatch = useDispatch();
2645
+ const user = useSelector((state) => state.user);
2646
+
2647
+ // 4. Router hooks
2648
+ const navigate = useNavigate();
2649
+ const { id } = useParams();
2650
+
2651
+ // 5. Custom hooks
2652
+ const { data, loading } = useFetchData(id);
2653
+
2654
+ // 6. Derived state
2655
+ const isReady = !loading && data !== null;
2656
+ const displayName = user?.name ?? "Guest";
2657
+
2658
+ // 7. useMemo
2659
+ const filteredItems = useMemo(
2660
+ () => data?.filter((item) => item.active),
2661
+ [data],
2662
+ );
2663
+
2664
+ // 8. useCallback
2665
+ const handleSubmit = useCallback(
2666
+ () => {
2667
+ dispatch(submitAction());
2668
+ },
2669
+ [dispatch],
2670
+ );
2671
+
2672
+ // 9. Handler functions
2673
+ const resetHandler = () => {
2674
+ setCount(0);
2675
+ setIsLoading(false);
2676
+ };
2677
+
2678
+ // 10. useEffect
2679
+ useEffect(
2680
+ () => {
2681
+ inputRef.current?.focus();
2682
+ },
2683
+ [],
2684
+ );
2685
+
2686
+ // 11. Return
2687
+ return (
2688
+ <div>
2689
+ <h1>{title}</h1>
2690
+ <span>{displayName}</span>
2691
+ </div>
2692
+ );
2693
+ };
2694
+
2695
+ // ✅ Good — Custom hook follows the correct order
2696
+ const useCreateAccount = () => {
2697
+ // 1. useState
2698
+ const [loading, setLoading] = useState(false);
2699
+ const [created, setCreated] = useState(false);
2700
+
2701
+ // 2. Redux hooks
2702
+ const dispatch = useDispatch();
2703
+
2704
+ // 3. Context hooks
2705
+ const { toast } = useToast();
2706
+
2707
+ // 4. Handler functions
2708
+ const createAccountHandler = async (data: AccountData) => {
2709
+ setLoading(true);
2710
+ try {
2711
+ await api.createAccount(data);
2712
+ setCreated(true);
2713
+ } catch (error) {
2714
+ toast({ description: "Failed to create account" });
2715
+ } finally {
2716
+ setLoading(false);
2717
+ }
2718
+ };
2719
+
2720
+ // 5. useEffect
2721
+ useEffect(
2722
+ () => {
2723
+ if (created) {
2724
+ setTimeout(() => setCreated(false), 50);
2725
+ }
2726
+ },
2727
+ [created],
2728
+ );
2729
+
2730
+ // 6. Return
2731
+ return { createAccountHandler, created, loading };
2732
+ };
2733
+
2734
+ // ❌ Bad — useEffect before useState
2735
+ const BadComponent = ({ title }) => {
2736
+ useEffect(() => {
2737
+ console.log("mounted");
2738
+ }, []);
2739
+
2740
+ const [count, setCount] = useState(0);
2741
+
2742
+ return <div>{title}</div>;
2743
+ };
2744
+
2745
+ // ❌ Bad — context hook before useState in custom hook
2746
+ const useBadHook = () => {
2747
+ const { toast } = useToast(); // Should come after useState
2748
+ const [loading, setLoading] = useState(false);
2749
+ return { loading };
2750
+ };
2751
+
2752
+ // ❌ Bad — handler before hooks
2753
+ const AnotherBadComponent = ({ title }) => {
2754
+ const handleClick = () => {
2755
+ console.log("clicked");
2756
+ };
2757
+
2758
+ const dispatch = useDispatch();
2759
+ const [count, setCount] = useState(0);
2760
+
2761
+ return <div onClick={handleClick}>{title}</div>;
2762
+ };
2763
+
2764
+ // ❌ Bad — derived state after handler
2765
+ const YetAnotherBad = ({ title }) => {
2766
+ const [items, setItems] = useState([]);
2767
+
2768
+ const handleAdd = () => {
2769
+ setItems([...items, "new"]);
2770
+ };
2771
+
2772
+ const itemCount = items.length; // Should come before handleAdd
2773
+
2774
+ return <div>{itemCount}</div>;
2775
+ };
2776
+ ```
2777
+
2778
+ <br />
2779
+
2780
+ ## 📝 Variable Rules
2781
+
2782
+ ### `variable-naming-convention`
2783
+
2784
+ **What it does:** Enforces naming conventions for variables:
2785
+ - **camelCase** for regular variables and functions
2786
+ - **UPPER_CASE** for constants (primitive values)
2787
+ - **PascalCase** for React components and classes
2788
+ - **camelCase with `use` prefix** for hooks
2789
+
2790
+ **Why use it:** Consistent naming makes code predictable. You can tell what something is by how it's named.
2791
+
2792
+ ```javascript
2793
+ // ✅ Good — correct conventions
2794
+ const userName = "John"; // camelCase for variables
2795
+ const itemCount = 42; // camelCase for variables
2796
+ const MAX_RETRIES = 3; // UPPER_CASE for constants
2797
+ const API_BASE_URL = "/api"; // UPPER_CASE for constants
2798
+ const UserProfile = () => <div />; // PascalCase for components
2799
+ const useAuth = () => {}; // camelCase with use prefix for hooks
2800
+
2801
+ // ❌ Bad — wrong conventions
2802
+ const user_name = "John"; // snake_case
2803
+ const maxretries = 3; // should be UPPER_CASE
2804
+ const userProfile = () => <div />; // should be PascalCase
2805
+ const UseAuth = () => {}; // hooks should be camelCase
2806
+ ```
2807
+
2808
+ <br />
2809
+
2203
2810
  ---
2204
2811
 
2205
2812
  ## 🔧 Auto-fixing