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.
- package/AGENTS.md +27 -29
- package/README.md +1427 -820
- package/index.d.ts +16 -0
- package/index.js +4720 -294
- 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
|
-
*
|
|
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 **
|
|
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
|
|
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
|
|
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 |
|
|
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 **
|
|
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 **
|
|
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
|
-
|
|
|
250
|
-
| `
|
|
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
|
-
|
|
|
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
|
-
##
|
|
522
|
+
## 📞 Call Expression Rules
|
|
488
523
|
|
|
489
|
-
### `
|
|
524
|
+
### `function-arguments-format`
|
|
490
525
|
|
|
491
|
-
**What it does:**
|
|
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:**
|
|
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 —
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
540
|
+
// ✅ Good — 2+ arguments get one per line
|
|
541
|
+
setValue(
|
|
542
|
+
"email",
|
|
543
|
+
"user@example.com",
|
|
544
|
+
);
|
|
535
545
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
546
|
+
createUser(
|
|
547
|
+
name,
|
|
548
|
+
email,
|
|
549
|
+
password,
|
|
550
|
+
);
|
|
539
551
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
552
|
+
// ✅ Good — multiline argument triggers expansion
|
|
553
|
+
processData(
|
|
554
|
+
{
|
|
555
|
+
id: 1,
|
|
556
|
+
name: "test",
|
|
557
|
+
},
|
|
558
|
+
);
|
|
543
559
|
|
|
544
|
-
//
|
|
545
|
-
|
|
560
|
+
// ✅ Good — callback with body triggers expansion
|
|
561
|
+
items.forEach(
|
|
562
|
+
(item) => {
|
|
563
|
+
process(item);
|
|
564
|
+
save(item);
|
|
565
|
+
},
|
|
566
|
+
);
|
|
546
567
|
|
|
547
|
-
// ❌ Bad —
|
|
548
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
569
|
-
|
|
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
|
-
//
|
|
589
|
-
|
|
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
|
-
### `
|
|
595
|
+
### `nested-call-closing-brackets`
|
|
595
596
|
|
|
596
|
-
**What it does:**
|
|
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:**
|
|
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 —
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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-
|
|
634
|
+
### `no-empty-lines-in-function-calls`
|
|
616
635
|
|
|
617
|
-
**What it does:** Removes empty lines within function
|
|
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
|
|
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
|
-
|
|
642
|
+
createUser(
|
|
624
643
|
name,
|
|
625
644
|
email,
|
|
645
|
+
password,
|
|
626
646
|
role,
|
|
627
|
-
)
|
|
647
|
+
);
|
|
628
648
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
649
|
+
fetchData(
|
|
650
|
+
url,
|
|
651
|
+
{
|
|
652
|
+
method: "POST",
|
|
653
|
+
body: data,
|
|
654
|
+
},
|
|
655
|
+
);
|
|
633
656
|
|
|
634
|
-
// ❌ Bad — empty line between
|
|
635
|
-
|
|
657
|
+
// ❌ Bad — empty line between arguments
|
|
658
|
+
createUser(
|
|
636
659
|
name,
|
|
637
660
|
|
|
638
661
|
email,
|
|
639
662
|
|
|
640
|
-
|
|
641
|
-
)
|
|
663
|
+
password,
|
|
664
|
+
);
|
|
642
665
|
|
|
643
666
|
// ❌ Bad — empty line after opening paren
|
|
644
|
-
|
|
667
|
+
fetchData(
|
|
645
668
|
|
|
646
|
-
|
|
647
|
-
|
|
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
|
-
### `
|
|
683
|
+
### `opening-brackets-same-line`
|
|
654
684
|
|
|
655
|
-
**What it does:**
|
|
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:**
|
|
687
|
+
**Why use it:** Opening brackets on new lines create unnecessary indentation and vertical space.
|
|
662
688
|
|
|
663
689
|
```javascript
|
|
664
|
-
// ✅ Good —
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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
|
-
//
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
```
|
|
696
|
+
// ✅ Good — multiline content is fine
|
|
697
|
+
fn({
|
|
698
|
+
key: value,
|
|
699
|
+
other: data,
|
|
700
|
+
});
|
|
678
701
|
|
|
679
|
-
|
|
702
|
+
items.map(({ id, name }) => (
|
|
703
|
+
<Item key={id} name={name} />
|
|
704
|
+
));
|
|
680
705
|
|
|
681
|
-
|
|
706
|
+
// ❌ Bad — opening bracket on new line
|
|
707
|
+
fn(
|
|
708
|
+
{ key: value }
|
|
709
|
+
);
|
|
682
710
|
|
|
683
|
-
|
|
711
|
+
process(
|
|
712
|
+
[1, 2, 3]
|
|
713
|
+
);
|
|
684
714
|
|
|
685
|
-
|
|
715
|
+
items.map(
|
|
716
|
+
({ id }) => id
|
|
717
|
+
);
|
|
718
|
+
```
|
|
686
719
|
|
|
687
|
-
|
|
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 —
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
//
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
736
|
+
// ✅ Good — complex 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
|
-
### `
|
|
755
|
+
### `single-argument-on-one-line`
|
|
707
756
|
|
|
708
|
-
**What it does:**
|
|
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:**
|
|
759
|
+
**Why use it:** Single-argument calls don't need multiline formatting. Expanding them wastes vertical space.
|
|
714
760
|
|
|
715
761
|
```javascript
|
|
716
|
-
// ✅ Good —
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
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
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
770
|
+
// ✅ Good — complex single argument can be multiline
|
|
771
|
+
processConfig({
|
|
772
|
+
key: value,
|
|
773
|
+
other: data,
|
|
774
|
+
});
|
|
733
775
|
|
|
734
|
-
// ❌ Bad —
|
|
735
|
-
|
|
736
|
-
|
|
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
|
-
|
|
792
|
+
## 💬 Comment Rules
|
|
742
793
|
|
|
743
|
-
|
|
794
|
+
### `comment-format`
|
|
744
795
|
|
|
745
|
-
**
|
|
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 —
|
|
749
|
-
|
|
750
|
-
|
|
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
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
) => {};
|
|
809
|
+
/*
|
|
810
|
+
* This is a multi-line
|
|
811
|
+
* block comment
|
|
812
|
+
*/
|
|
761
813
|
|
|
762
|
-
// ✅ Good —
|
|
763
|
-
|
|
814
|
+
// ✅ Good — file-top comments without gaps
|
|
815
|
+
// File: utils.js
|
|
816
|
+
// Author: John Doe
|
|
817
|
+
// License: MIT
|
|
764
818
|
|
|
765
|
-
// ❌ Bad —
|
|
766
|
-
|
|
767
|
-
email, password,
|
|
768
|
-
role) {}
|
|
819
|
+
// ❌ Bad — missing space after //
|
|
820
|
+
//This is a comment
|
|
769
821
|
|
|
770
|
-
// ❌ Bad —
|
|
771
|
-
|
|
772
|
-
|
|
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
|
-
##
|
|
831
|
+
## 🔀 Control Flow Rules
|
|
778
832
|
|
|
779
|
-
### `
|
|
833
|
+
### `block-statement-newlines`
|
|
780
834
|
|
|
781
|
-
**What it does:** Enforces
|
|
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:**
|
|
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 —
|
|
787
|
-
|
|
788
|
-
()
|
|
789
|
-
|
|
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
|
-
|
|
802
|
-
()
|
|
803
|
-
|
|
804
|
-
);
|
|
845
|
+
for (const item of items) {
|
|
846
|
+
process(item);
|
|
847
|
+
}
|
|
805
848
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
const subscription = subscribe();
|
|
849
|
+
while (running) {
|
|
850
|
+
tick();
|
|
851
|
+
}
|
|
810
852
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
[subscribe],
|
|
814
|
-
);
|
|
853
|
+
// ❌ Bad — everything on one line
|
|
854
|
+
if (condition) { doSomething(); }
|
|
815
855
|
|
|
816
|
-
// ❌ Bad —
|
|
817
|
-
|
|
856
|
+
// ❌ Bad — no space after brace
|
|
857
|
+
if (condition) {doSomething();}
|
|
818
858
|
|
|
819
|
-
// ❌ Bad —
|
|
820
|
-
|
|
859
|
+
// ❌ Bad — inconsistent formatting
|
|
860
|
+
for (const item of items) { process(item);
|
|
861
|
+
}
|
|
821
862
|
```
|
|
822
863
|
|
|
823
864
|
---
|
|
824
865
|
|
|
825
|
-
### `
|
|
866
|
+
### `if-statement-format`
|
|
826
867
|
|
|
827
|
-
**What it does:**
|
|
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:**
|
|
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 —
|
|
833
|
-
|
|
834
|
-
|
|
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
|
-
##
|
|
1048
|
+
## ⚡ Function Rules
|
|
1063
1049
|
|
|
1064
|
-
### `
|
|
1050
|
+
### `function-call-spacing`
|
|
1065
1051
|
|
|
1066
|
-
**What it does:**
|
|
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 —
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
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 —
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1073
|
+
### `function-naming-convention`
|
|
1098
1074
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
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
|
-
//
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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
|
-
### `
|
|
1108
|
+
### `function-object-destructure`
|
|
1120
1109
|
|
|
1121
|
-
**What it does:**
|
|
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:**
|
|
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
|
-
```
|
|
1130
|
-
// ✅ Good —
|
|
1131
|
-
|
|
1132
|
-
|
|
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
|
-
//
|
|
1136
|
-
export {
|
|
1137
|
-
Button,
|
|
1138
|
-
Input,
|
|
1139
|
-
Select,
|
|
1140
|
-
Checkbox,
|
|
1119
|
+
// Use age, email, isActive, name...
|
|
1141
1120
|
};
|
|
1142
1121
|
|
|
1143
|
-
|
|
1144
|
-
|
|
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
|
-
//
|
|
1153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
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
|
-
|
|
1170
|
-
|
|
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
|
-
### `
|
|
1159
|
+
### `function-params-per-line`
|
|
1176
1160
|
|
|
1177
|
-
**What it does:**
|
|
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:**
|
|
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 —
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1166
|
+
// ✅ Good — each param on own line
|
|
1167
|
+
function createUser(
|
|
1168
|
+
name,
|
|
1169
|
+
email,
|
|
1170
|
+
password,
|
|
1171
|
+
role,
|
|
1172
|
+
) {}
|
|
1190
1173
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
useMemo,
|
|
1197
|
-
} from "react";
|
|
1174
|
+
const handler = (
|
|
1175
|
+
event,
|
|
1176
|
+
context,
|
|
1177
|
+
callback,
|
|
1178
|
+
) => {};
|
|
1198
1179
|
|
|
1199
|
-
|
|
1200
|
-
|
|
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 —
|
|
1207
|
-
|
|
1183
|
+
// ❌ Bad — mixed formatting
|
|
1184
|
+
function createUser(name,
|
|
1185
|
+
email, password,
|
|
1186
|
+
role) {}
|
|
1208
1187
|
|
|
1209
|
-
// ❌ Bad —
|
|
1210
|
-
|
|
1211
|
-
|
|
1188
|
+
// ❌ Bad — some on same line, some not
|
|
1189
|
+
const handler = (event, context,
|
|
1190
|
+
callback) => {};
|
|
1191
|
+
```
|
|
1212
1192
|
|
|
1213
|
-
|
|
1214
|
-
import { Button }
|
|
1215
|
-
from "@/components";
|
|
1193
|
+
---
|
|
1216
1194
|
|
|
1217
|
-
|
|
1218
|
-
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
1219
|
-
```
|
|
1195
|
+
### `no-empty-lines-in-function-params`
|
|
1220
1196
|
|
|
1221
|
-
**
|
|
1197
|
+
**What it does:** Removes empty lines within function parameter lists — between parameters and after opening/before closing parentheses.
|
|
1222
1198
|
|
|
1223
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1233
|
+
## 🪝 Hook Rules
|
|
1234
1234
|
|
|
1235
|
-
|
|
1235
|
+
### `hook-callback-format`
|
|
1236
1236
|
|
|
1237
|
-
**
|
|
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 —
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1242
|
+
// ✅ Good — callback and deps clearly separated
|
|
1243
|
+
useEffect(
|
|
1244
|
+
() => {
|
|
1245
|
+
fetchData();
|
|
1246
|
+
},
|
|
1247
|
+
[userId],
|
|
1248
|
+
);
|
|
1244
1249
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1250
|
+
useCallback(
|
|
1251
|
+
() => {
|
|
1252
|
+
handleSubmit(data);
|
|
1253
|
+
},
|
|
1254
|
+
[data, handleSubmit],
|
|
1255
|
+
);
|
|
1247
1256
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1257
|
+
useMemo(
|
|
1258
|
+
() => expensiveCalculation(items),
|
|
1259
|
+
[items],
|
|
1260
|
+
);
|
|
1250
1261
|
|
|
1251
|
-
//
|
|
1252
|
-
|
|
1262
|
+
// ✅ Good — cleanup 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
|
-
### `
|
|
1281
|
+
### `hook-deps-per-line`
|
|
1258
1282
|
|
|
1259
|
-
**What it does:**
|
|
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:**
|
|
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 —
|
|
1268
|
-
|
|
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
|
-
//
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1292
|
+
// ✅ Good — 3+ deps get one per line
|
|
1293
|
+
useEffect(
|
|
1294
|
+
() => {},
|
|
1295
|
+
[
|
|
1296
|
+
userId,
|
|
1297
|
+
token,
|
|
1298
|
+
refreshToken,
|
|
1299
|
+
],
|
|
1300
|
+
);
|
|
1279
1301
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1302
|
+
useCallback(
|
|
1303
|
+
() => handleSubmit(data),
|
|
1304
|
+
[
|
|
1305
|
+
data,
|
|
1306
|
+
handleSubmit,
|
|
1307
|
+
validateForm,
|
|
1308
|
+
showError,
|
|
1309
|
+
],
|
|
1310
|
+
);
|
|
1288
1311
|
|
|
1289
|
-
|
|
1290
|
-
|
|
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
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
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
|
-
| `
|
|
1325
|
+
| `maxDeps` | `integer` | `2` | Maximum dependencies to keep on single line |
|
|
1309
1326
|
|
|
1310
1327
|
```javascript
|
|
1311
|
-
|
|
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
|
-
|
|
1334
|
+
## 📥 Import/Export Rules
|
|
1317
1335
|
|
|
1318
|
-
|
|
1336
|
+
### `absolute-imports-only`
|
|
1319
1337
|
|
|
1320
|
-
**
|
|
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 —
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
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
|
-
//
|
|
1330
|
-
import
|
|
1353
|
+
// ✅ Good — assets allow deep imports by default
|
|
1354
|
+
import logo from "@/assets/images/logo.png";
|
|
1331
1355
|
|
|
1332
|
-
// ❌ Bad —
|
|
1333
|
-
|
|
1334
|
-
|
|
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
|
-
|
|
1339
|
-
|
|
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
|
|
1342
|
-
`
|
|
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
|
-
| `
|
|
1349
|
-
| `
|
|
1350
|
-
| `
|
|
1351
|
-
| `
|
|
1352
|
-
| `
|
|
1353
|
-
| `
|
|
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
|
|
1357
|
-
"code-style/
|
|
1358
|
-
|
|
1359
|
-
|
|
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
|
-
|
|
1365
|
-
|
|
1366
|
-
## ⚛️ JSX Rules
|
|
1389
|
+
---
|
|
1367
1390
|
|
|
1368
|
-
### `
|
|
1391
|
+
### `export-format`
|
|
1369
1392
|
|
|
1370
|
-
**What it does:**
|
|
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:**
|
|
1399
|
+
**Why use it:** Consistent export formatting improves readability. Short exports stay compact, long exports become scannable.
|
|
1373
1400
|
|
|
1374
1401
|
```javascript
|
|
1375
|
-
// ✅ Good —
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
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
|
-
//
|
|
1407
|
+
// ✅ Good — 4+ 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
|
-
|
|
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
|