eslint-plugin-code-style 1.1.10 → 1.2.6
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 +581 -38
- package/index.d.ts +16 -0
- package/index.js +8491 -3271
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
[](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/blob/main/LICENSE)
|
|
8
8
|
|
|
9
9
|
[](https://eslint.org/)
|
|
10
|
-
[](https://nodejs.org/)
|
|
11
11
|
[](https://react.dev/)
|
|
12
|
-
[](https://www.typescriptlang.org/)
|
|
13
|
+
[](https://tailwindcss.com/)
|
|
13
14
|
|
|
14
15
|
[](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/stargazers)
|
|
15
16
|
[](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/issues)
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
|
|
19
20
|
**A powerful ESLint plugin for enforcing consistent code formatting and style rules in React/JSX projects.**
|
|
20
21
|
|
|
21
|
-
*
|
|
22
|
+
*60 auto-fixable rules to keep your codebase clean and consistent*
|
|
22
23
|
|
|
23
24
|
</div>
|
|
24
25
|
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
|
|
27
28
|
## 🎯 Why This Plugin?
|
|
28
29
|
|
|
29
|
-
This plugin provides **
|
|
30
|
+
This plugin provides **60 custom auto-fixable rules** for code formatting. Built for **ESLint v9 flat configs**.
|
|
30
31
|
|
|
31
32
|
> **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
33
|
|
|
@@ -35,7 +36,7 @@ This plugin provides **51 custom auto-fixable rules** for code formatting. Built
|
|
|
35
36
|
- **Works alongside existing tools** — Complements ESLint's built-in rules and packages like eslint-plugin-react, eslint-plugin-import, etc
|
|
36
37
|
- **Self-sufficient rules** — Each rule handles complete formatting independently
|
|
37
38
|
- **Consistency at scale** — Reduces code-style differences between team members by enforcing uniform formatting across your projects
|
|
38
|
-
- **Fully automated** — All
|
|
39
|
+
- **Fully automated** — All 60 rules support auto-fix, eliminating manual style reviews
|
|
39
40
|
|
|
40
41
|
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
42
|
|
|
@@ -59,17 +60,19 @@ We provide **ready-to-use ESLint flat configuration files** that combine `eslint
|
|
|
59
60
|
|
|
60
61
|
### 💡 Why Use These Configs?
|
|
61
62
|
|
|
62
|
-
- **Complete Coverage** — Combines ESLint built-in rules, third-party plugins, and all
|
|
63
|
+
- **Complete Coverage** — Combines ESLint built-in rules, third-party plugins, and all 60 code-style rules
|
|
63
64
|
- **Ready-to-Use** — Copy the config file and start linting immediately
|
|
64
65
|
- **Battle-Tested** — These configurations have been refined through real-world usage
|
|
65
66
|
- **Fully Documented** — Each config includes detailed instructions and explanations
|
|
66
67
|
|
|
67
68
|
### 📋 Available Configurations
|
|
68
69
|
|
|
69
|
-
| Configuration | Description |
|
|
70
|
-
|
|
70
|
+
| Configuration | Description | Status |
|
|
71
|
+
|---------------|-------------|--------|
|
|
71
72
|
| **React** | React.js projects (JavaScript, JSX) | [View Config](./recommended-configs/react/) |
|
|
72
73
|
| **React + TS + Tailwind** | React + TypeScript + Tailwind CSS | [View Config](./recommended-configs/react-ts-tw/) |
|
|
74
|
+
| **React + TypeScript** | React + TypeScript projects | Coming Soon |
|
|
75
|
+
| **React + Tailwind** | React + Tailwind CSS projects | Coming Soon |
|
|
73
76
|
|
|
74
77
|
### ⚡ Quick Start with Recommended Config
|
|
75
78
|
|
|
@@ -94,7 +97,7 @@ We provide **ready-to-use ESLint flat configuration files** that combine `eslint
|
|
|
94
97
|
<td width="50%">
|
|
95
98
|
|
|
96
99
|
### 🔧 Auto-Fixable Rules
|
|
97
|
-
All **
|
|
100
|
+
All **60 rules** support automatic fixing with `eslint --fix`. No manual code changes needed.
|
|
98
101
|
|
|
99
102
|
</td>
|
|
100
103
|
<td width="50%">
|
|
@@ -140,7 +143,7 @@ yarn add eslint-plugin-code-style -D
|
|
|
140
143
|
| Dependency | Version |
|
|
141
144
|
|------------|---------|
|
|
142
145
|
| **ESLint** | `>= 9.0.0` |
|
|
143
|
-
| **Node.js** | `>=
|
|
146
|
+
| **Node.js** | `>= 20.0.0` |
|
|
144
147
|
|
|
145
148
|
<br />
|
|
146
149
|
|
|
@@ -178,28 +181,35 @@ eslint src/ --fix
|
|
|
178
181
|
|
|
179
182
|
```javascript
|
|
180
183
|
rules: {
|
|
184
|
+
"code-style/absolute-imports-only": "error",
|
|
181
185
|
"code-style/array-items-per-line": "error",
|
|
182
186
|
"code-style/array-objects-on-new-lines": "error",
|
|
183
187
|
"code-style/arrow-function-block-body": "error",
|
|
184
188
|
"code-style/arrow-function-simple-jsx": "error",
|
|
185
189
|
"code-style/arrow-function-simplify": "error",
|
|
186
|
-
"code-style/curried-arrow-same-line": "error",
|
|
187
190
|
"code-style/assignment-value-same-line": "error",
|
|
188
191
|
"code-style/block-statement-newlines": "error",
|
|
192
|
+
"code-style/classname-dynamic-at-end": "error",
|
|
193
|
+
"code-style/classname-multiline": "error",
|
|
194
|
+
"code-style/classname-no-extra-spaces": "error",
|
|
189
195
|
"code-style/comment-format": "error",
|
|
196
|
+
"code-style/component-props-destructure": "error",
|
|
197
|
+
"code-style/component-props-inline-type": "error",
|
|
198
|
+
"code-style/curried-arrow-same-line": "error",
|
|
199
|
+
"code-style/enum-format": "error",
|
|
200
|
+
"code-style/export-format": "error",
|
|
201
|
+
"code-style/function-arguments-format": "error",
|
|
190
202
|
"code-style/function-call-spacing": "error",
|
|
191
203
|
"code-style/function-naming-convention": "error",
|
|
204
|
+
"code-style/function-object-destructure": "error",
|
|
192
205
|
"code-style/function-params-per-line": "error",
|
|
193
206
|
"code-style/hook-callback-format": "error",
|
|
194
207
|
"code-style/hook-deps-per-line": "error",
|
|
195
208
|
"code-style/if-statement-format": "error",
|
|
196
|
-
"code-style/multiline-if-conditions": "error",
|
|
197
|
-
"code-style/absolute-imports-only": "error",
|
|
198
|
-
"code-style/export-format": "error",
|
|
199
209
|
"code-style/import-format": "error",
|
|
200
210
|
"code-style/import-source-spacing": "error",
|
|
201
211
|
"code-style/index-export-style": "error",
|
|
202
|
-
"code-style/
|
|
212
|
+
"code-style/interface-format": "error",
|
|
203
213
|
"code-style/jsx-children-on-new-line": "error",
|
|
204
214
|
"code-style/jsx-closing-bracket-spacing": "error",
|
|
205
215
|
"code-style/jsx-element-child-new-line": "error",
|
|
@@ -210,7 +220,8 @@ rules: {
|
|
|
210
220
|
"code-style/jsx-string-value-trim": "error",
|
|
211
221
|
"code-style/jsx-ternary-format": "error",
|
|
212
222
|
"code-style/member-expression-bracket-spacing": "error",
|
|
213
|
-
"code-style/
|
|
223
|
+
"code-style/module-index-exports": "error",
|
|
224
|
+
"code-style/multiline-if-conditions": "error",
|
|
214
225
|
"code-style/nested-call-closing-brackets": "error",
|
|
215
226
|
"code-style/no-empty-lines-in-function-calls": "error",
|
|
216
227
|
"code-style/no-empty-lines-in-function-params": "error",
|
|
@@ -221,14 +232,14 @@ rules: {
|
|
|
221
232
|
"code-style/object-property-value-brace": "error",
|
|
222
233
|
"code-style/object-property-value-format": "error",
|
|
223
234
|
"code-style/opening-brackets-same-line": "error",
|
|
235
|
+
"code-style/react-code-order": "error",
|
|
224
236
|
"code-style/simple-call-single-line": "error",
|
|
225
237
|
"code-style/single-argument-on-one-line": "error",
|
|
226
238
|
"code-style/string-property-spacing": "error",
|
|
227
|
-
"code-style/
|
|
228
|
-
"code-style/enum-format": "error",
|
|
229
|
-
"code-style/interface-format": "error",
|
|
239
|
+
"code-style/type-annotation-spacing": "error",
|
|
230
240
|
"code-style/type-format": "error",
|
|
231
241
|
"code-style/typescript-definition-location": "error",
|
|
242
|
+
"code-style/variable-naming-convention": "error",
|
|
232
243
|
}
|
|
233
244
|
```
|
|
234
245
|
|
|
@@ -238,7 +249,7 @@ rules: {
|
|
|
238
249
|
|
|
239
250
|
## 📖 Rules Summary
|
|
240
251
|
|
|
241
|
-
> All **
|
|
252
|
+
> All **60 rules** are auto-fixable. See detailed examples for each rule in the [Rules Reference](#-rules-reference) section below.
|
|
242
253
|
>
|
|
243
254
|
> Rules marked with ⚙️ support customization options (e.g., extending default folder lists).
|
|
244
255
|
|
|
@@ -257,10 +268,13 @@ rules: {
|
|
|
257
268
|
| `nested-call-closing-brackets` | Chain closing brackets on same line: `}));` not scattered across lines |
|
|
258
269
|
| `no-empty-lines-in-function-calls` | No empty lines between arguments or after `(`/before `)` |
|
|
259
270
|
| `opening-brackets-same-line` | Opening `{`, `[`, or `(` on same line as function call, not on new line |
|
|
260
|
-
| `simple-call-single-line` | Collapse simple
|
|
271
|
+
| `simple-call-single-line` | Collapse simple arrow function calls to single line (including callbacks with params and optional chaining) |
|
|
261
272
|
| `single-argument-on-one-line` | Single simple argument stays on one line: `fn(x)` not expanded |
|
|
262
273
|
| **Comment Rules** | |
|
|
263
274
|
| `comment-format` | Space after `//`, space inside `/* */`, convert single-line blocks to `//`, no blank lines between file-top comments |
|
|
275
|
+
| **Component Rules** | |
|
|
276
|
+
| `component-props-destructure` | Component props must be destructured `({ prop })` not received as `(props)` |
|
|
277
|
+
| `component-props-inline-type` | Inline type annotation `} : {` with matching props, proper spacing, commas, no interface reference |
|
|
264
278
|
| **Control Flow Rules** | |
|
|
265
279
|
| `block-statement-newlines` | Newline after `{` and before `}` in if/for/while/function blocks |
|
|
266
280
|
| `if-statement-format` | `{` on same line as `if`/`else if`, `else` on same line as `}`, proper spacing |
|
|
@@ -269,6 +283,7 @@ rules: {
|
|
|
269
283
|
| **Function Rules** | |
|
|
270
284
|
| `function-call-spacing` | No space between function name and `(`: `fn()` not `fn ()` |
|
|
271
285
|
| `function-naming-convention` | Functions use camelCase, start with verb (get/set/handle/is/has), handlers end with Handler |
|
|
286
|
+
| `function-object-destructure` | Non-component functions: use typed params (not destructured), destructure in body; report dot notation access |
|
|
272
287
|
| `function-params-per-line` | When multiline, each param on own line with consistent indentation |
|
|
273
288
|
| `no-empty-lines-in-function-params` | No empty lines between parameters or after `(`/before `)` |
|
|
274
289
|
| **Hook Rules** | |
|
|
@@ -282,6 +297,9 @@ rules: {
|
|
|
282
297
|
| `index-export-style` | Index files: no blank lines, enforce shorthand or import-export style; Regular files: require blank lines between exports (default: shorthand) ⚙️ |
|
|
283
298
|
| `module-index-exports` | Index files must export all folder contents (files and subfolders) ⚙️ |
|
|
284
299
|
| **JSX Rules** | |
|
|
300
|
+
| `classname-dynamic-at-end` | Dynamic expressions (`${className}`) must be at the end of class strings (JSX and variables) |
|
|
301
|
+
| `classname-multiline` | Long className strings broken into multiple lines, one class per line; uses `"..."` in JSX (no expressions) or `` `...` `` (with expressions/variables) ⚙️ |
|
|
302
|
+
| `classname-no-extra-spaces` | No extra/leading/trailing spaces in class strings (JSX, variables, and objects) |
|
|
285
303
|
| `jsx-children-on-new-line` | Multiple JSX children: each on own line with proper indentation |
|
|
286
304
|
| `jsx-closing-bracket-spacing` | No space before `>` or `/>` in JSX tags |
|
|
287
305
|
| `jsx-element-child-new-line` | Nested JSX elements on new lines; text/expression children can stay inline |
|
|
@@ -304,10 +322,13 @@ rules: {
|
|
|
304
322
|
| **TypeScript Rules** | |
|
|
305
323
|
| `enum-format` | Enforce enum naming (PascalCase + Enum suffix), UPPER_CASE members, no empty lines, and trailing commas |
|
|
306
324
|
| `interface-format` | Enforce interface naming (PascalCase + Interface suffix), camelCase properties, no empty lines, and trailing commas |
|
|
325
|
+
| `type-annotation-spacing` | Enforce consistent spacing in type annotations: no space before colon/generic/array brackets, one space after colon |
|
|
307
326
|
| `type-format` | Enforce type naming (PascalCase + Type suffix), camelCase properties, no empty lines, and trailing commas |
|
|
308
327
|
| `typescript-definition-location` | Enforce TypeScript definitions (interfaces, types, enums) to be in designated folders ⚙️ |
|
|
328
|
+
| **React Rules** | |
|
|
329
|
+
| `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 |
|
|
309
330
|
| **Variable Rules** | |
|
|
310
|
-
| `variable-naming-convention` | camelCase for variables
|
|
331
|
+
| `variable-naming-convention` | camelCase for all variables and constants, PascalCase for components, `use` prefix for hooks |
|
|
311
332
|
|
|
312
333
|
<br />
|
|
313
334
|
|
|
@@ -707,17 +728,18 @@ items.map(
|
|
|
707
728
|
|
|
708
729
|
### `simple-call-single-line`
|
|
709
730
|
|
|
710
|
-
**What it does:** Collapses simple function calls with an arrow function
|
|
731
|
+
**What it does:** Collapses simple function calls with an arrow function onto one line when the result fits within 120 characters. Handles:
|
|
732
|
+
- Zero-param callbacks: `lazy(() => import("./Page"))`
|
|
733
|
+
- Callbacks with params and simple expression bodies: `.find((f) => f.code === x)`
|
|
734
|
+
- Optional chaining: `.find(...)?.symbol`
|
|
711
735
|
|
|
712
|
-
**Why use it:** Common patterns like `lazy(() => import(...))` don't need multiline formatting. Single line is cleaner.
|
|
736
|
+
**Why use it:** Common patterns like `lazy(() => import(...))` and `.find((item) => item.id === id)` don't need multiline formatting. Single line is cleaner.
|
|
713
737
|
|
|
714
738
|
```javascript
|
|
715
739
|
// ✅ Good — simple patterns on one line
|
|
716
740
|
const Page = lazy(() => import("./Page"));
|
|
717
|
-
const Modal = lazy(() => import("./Modal"));
|
|
718
741
|
setTimeout(() => callback(), 100);
|
|
719
|
-
|
|
720
|
-
items.filter(() => isValid(item));
|
|
742
|
+
const symbol = items.find(({ code }) => code === currency)?.symbol;
|
|
721
743
|
|
|
722
744
|
// ✅ Good — complex callbacks stay multiline
|
|
723
745
|
const Page = lazy(() => {
|
|
@@ -730,10 +752,11 @@ const Page = lazy(
|
|
|
730
752
|
() => import("./Page"),
|
|
731
753
|
);
|
|
732
754
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
)
|
|
755
|
+
const symbol = items.find(({ code }) =>
|
|
756
|
+
code === currency)?.symbol;
|
|
757
|
+
|
|
758
|
+
const symbol = items.find(({ code }) => code === currency)?.
|
|
759
|
+
symbol;
|
|
737
760
|
```
|
|
738
761
|
|
|
739
762
|
---
|
|
@@ -1091,6 +1114,57 @@ function get_user_data() {}
|
|
|
1091
1114
|
|
|
1092
1115
|
---
|
|
1093
1116
|
|
|
1117
|
+
### `function-object-destructure`
|
|
1118
|
+
|
|
1119
|
+
**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).
|
|
1120
|
+
|
|
1121
|
+
**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.
|
|
1122
|
+
|
|
1123
|
+
```typescript
|
|
1124
|
+
// ✅ Good — typed param with destructuring in body
|
|
1125
|
+
const createUserHandler = async (data: CreateUserParamsInterface) => {
|
|
1126
|
+
const { age, email, isActive, name } = data;
|
|
1127
|
+
|
|
1128
|
+
// Use age, email, isActive, name...
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
const updateUserHandler = (params: UpdateParamsInterface) => {
|
|
1132
|
+
const { id, updates } = params;
|
|
1133
|
+
|
|
1134
|
+
// Use id, updates...
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
// ✅ Good — React components CAN destructure in signature
|
|
1138
|
+
const UserCard = ({
|
|
1139
|
+
name,
|
|
1140
|
+
email,
|
|
1141
|
+
} : {
|
|
1142
|
+
name: string,
|
|
1143
|
+
email: string,
|
|
1144
|
+
}) => (
|
|
1145
|
+
<div>{name} - {email}</div>
|
|
1146
|
+
);
|
|
1147
|
+
|
|
1148
|
+
// ❌ Bad — non-component function destructures in signature
|
|
1149
|
+
const createUserHandler = async ({
|
|
1150
|
+
age,
|
|
1151
|
+
email,
|
|
1152
|
+
isActive,
|
|
1153
|
+
name,
|
|
1154
|
+
}: CreateUserParamsInterface) => {
|
|
1155
|
+
// ...
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1158
|
+
// ❌ Bad — accessing param via dot notation (should destructure)
|
|
1159
|
+
const processDataHandler = (data: DataInterface) => {
|
|
1160
|
+
console.log(data.id); // Bad: use destructuring
|
|
1161
|
+
console.log(data.name); // Bad: use destructuring
|
|
1162
|
+
return data.value * 2; // Bad: use destructuring
|
|
1163
|
+
};
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
---
|
|
1167
|
+
|
|
1094
1168
|
### `function-params-per-line`
|
|
1095
1169
|
|
|
1096
1170
|
**What it does:** When function parameters span multiple lines, ensures each parameter is on its own line with consistent indentation.
|
|
@@ -1299,7 +1373,7 @@ import { fetchUsers } from "@/apis/users/fetchUsers";
|
|
|
1299
1373
|
```
|
|
1300
1374
|
|
|
1301
1375
|
**Default Allowed Folders:**
|
|
1302
|
-
`actions`, `apis`, `assets`, `atoms`, `components`, `constants`, `contexts`, `data`, `enums`, `hooks`, `interfaces`, `layouts`, `middlewares`, `providers`, `reducers`, `redux`, `requests`, `routes`, `schemas`, `services`, `store`, `styles`, `theme`, `thunks`, `types`, `utils`, `views`
|
|
1376
|
+
`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`
|
|
1303
1377
|
|
|
1304
1378
|
**Customization Options:**
|
|
1305
1379
|
|
|
@@ -1543,7 +1617,7 @@ import { Button } from "@/components/Button/Button"; // Avoid this!
|
|
|
1543
1617
|
```
|
|
1544
1618
|
|
|
1545
1619
|
**Default Module Folders:**
|
|
1546
|
-
`actions`, `apis`, `assets`, `atoms`, `components`, `constants`, `contexts`, `data`, `enums`, `hooks`, `interfaces`, `layouts`, `middlewares`, `providers`, `reducers`, `redux`, `requests`, `routes`, `schemas`, `services`, `store`, `styles`, `theme`, `thunks`, `types`, `utils`, `views`
|
|
1620
|
+
`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`
|
|
1547
1621
|
|
|
1548
1622
|
**Default Ignore Patterns:**
|
|
1549
1623
|
`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`
|
|
@@ -1572,6 +1646,132 @@ import { Button } from "@/components/Button/Button"; // Avoid this!
|
|
|
1572
1646
|
|
|
1573
1647
|
## ⚛️ JSX Rules
|
|
1574
1648
|
|
|
1649
|
+
### `classname-dynamic-at-end`
|
|
1650
|
+
|
|
1651
|
+
**What it does:** Enforces that dynamic expressions in className template literals are placed at the end, after all static class names. Also applies to variables with names containing "class" or "Class".
|
|
1652
|
+
|
|
1653
|
+
**Why use it:** When using Tailwind CSS with `tailwindcss/classnames-order`, static classes are automatically sorted. However, dynamic expressions like `${className}` or `${styles.button}` can break the visual order if placed in the middle. This rule ensures dynamic parts come last for consistent, readable class strings.
|
|
1654
|
+
|
|
1655
|
+
```javascript
|
|
1656
|
+
// ✅ Good — dynamic expressions at the end (JSX)
|
|
1657
|
+
<div className={`flex items-center gap-4 ${className}`} />
|
|
1658
|
+
|
|
1659
|
+
// ✅ Good — dynamic expressions at the end (variable)
|
|
1660
|
+
const buttonClasses = `flex items-center ${className} ${styles.button}`;
|
|
1661
|
+
|
|
1662
|
+
// ❌ Bad — dynamic expression at the beginning
|
|
1663
|
+
<div className={`${className} flex items-center gap-4`} />
|
|
1664
|
+
|
|
1665
|
+
// ❌ Bad — dynamic expression in the middle (variable)
|
|
1666
|
+
const buttonClasses = `flex ${className} items-center gap-4`;
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
---
|
|
1670
|
+
|
|
1671
|
+
### `classname-multiline`
|
|
1672
|
+
|
|
1673
|
+
**What it does:** Enforces that long className strings are broken into multiple lines, with each class on its own line. Triggers when either the class count exceeds `maxClassCount` (default: 3) or the string length exceeds `maxLength` (default: 80). Also enforces:
|
|
1674
|
+
- JSX `className` with no dynamic expressions uses `"..."` string literal format
|
|
1675
|
+
- JSX `className` with dynamic expressions uses `` {`...`} `` template literal format
|
|
1676
|
+
- Variables/object properties use `` `...` `` template literal for multiline (JS requires it)
|
|
1677
|
+
- No empty lines between classes or before/after the quotes
|
|
1678
|
+
- Under-threshold multiline classes are collapsed back to a single line
|
|
1679
|
+
|
|
1680
|
+
Applies to JSX `className` attributes, variables with class-related names, and object properties within class-related objects.
|
|
1681
|
+
|
|
1682
|
+
**Why use it:** Long single-line class strings are hard to read and review. Breaking them into one class per line makes diffs cleaner and classes easier to scan. Using string literals when no expressions are needed keeps the code simpler.
|
|
1683
|
+
|
|
1684
|
+
```javascript
|
|
1685
|
+
// ✅ Good — JSX with no expressions uses "..." format
|
|
1686
|
+
<div
|
|
1687
|
+
className="
|
|
1688
|
+
flex
|
|
1689
|
+
items-center
|
|
1690
|
+
justify-center
|
|
1691
|
+
rounded-lg
|
|
1692
|
+
p-4
|
|
1693
|
+
"
|
|
1694
|
+
/>
|
|
1695
|
+
|
|
1696
|
+
// ✅ Good — JSX with expressions uses {`...`} format
|
|
1697
|
+
<div
|
|
1698
|
+
className={`
|
|
1699
|
+
flex
|
|
1700
|
+
items-center
|
|
1701
|
+
justify-center
|
|
1702
|
+
${className}
|
|
1703
|
+
`}
|
|
1704
|
+
/>
|
|
1705
|
+
|
|
1706
|
+
// ✅ Good — variable multiline uses template literal
|
|
1707
|
+
const buttonClasses = `
|
|
1708
|
+
flex
|
|
1709
|
+
items-center
|
|
1710
|
+
justify-center
|
|
1711
|
+
${className}
|
|
1712
|
+
`;
|
|
1713
|
+
|
|
1714
|
+
// ✅ Good — object property multiline uses template literal
|
|
1715
|
+
const variantClasses = {
|
|
1716
|
+
danger: `
|
|
1717
|
+
flex
|
|
1718
|
+
items-center
|
|
1719
|
+
justify-center
|
|
1720
|
+
bg-red-500
|
|
1721
|
+
`,
|
|
1722
|
+
};
|
|
1723
|
+
|
|
1724
|
+
// ✅ Good — short class strings stay on one line
|
|
1725
|
+
<div className="flex items-center" />
|
|
1726
|
+
|
|
1727
|
+
// ❌ Bad — too many classes on one line
|
|
1728
|
+
<div className="flex items-center justify-center rounded-lg p-4 font-bold" />
|
|
1729
|
+
|
|
1730
|
+
// ❌ Bad — using template literal in JSX when no expressions
|
|
1731
|
+
<div className={`
|
|
1732
|
+
flex
|
|
1733
|
+
items-center
|
|
1734
|
+
justify-center
|
|
1735
|
+
rounded-lg
|
|
1736
|
+
`} />
|
|
1737
|
+
|
|
1738
|
+
// ❌ Bad — empty lines between classes
|
|
1739
|
+
<div className="
|
|
1740
|
+
flex
|
|
1741
|
+
|
|
1742
|
+
items-center
|
|
1743
|
+
justify-center
|
|
1744
|
+
" />
|
|
1745
|
+
```
|
|
1746
|
+
|
|
1747
|
+
---
|
|
1748
|
+
|
|
1749
|
+
### `classname-no-extra-spaces`
|
|
1750
|
+
|
|
1751
|
+
**What it does:** Removes multiple consecutive spaces and leading/trailing spaces inside className values. Applies to:
|
|
1752
|
+
- JSX `className` attributes (string literals and template literals)
|
|
1753
|
+
- Variables with names containing "class" (e.g., `buttonClasses`, `variantClasses`)
|
|
1754
|
+
- Object properties within class-related objects
|
|
1755
|
+
|
|
1756
|
+
**Why use it:** Extra spaces between class names are usually unintentional and can cause issues. This rule normalizes spacing and removes unnecessary whitespace.
|
|
1757
|
+
|
|
1758
|
+
```javascript
|
|
1759
|
+
// ✅ Good — single space between classes
|
|
1760
|
+
<div className="flex items-center gap-4 rounded-lg" />
|
|
1761
|
+
const buttonClasses = `flex items-center ${className}`;
|
|
1762
|
+
const variantClasses = { primary: "bg-blue-500 text-white" };
|
|
1763
|
+
|
|
1764
|
+
// ❌ Bad — multiple consecutive spaces
|
|
1765
|
+
<div className="flex items-center gap-4" />
|
|
1766
|
+
const buttonClasses = `flex items-center`;
|
|
1767
|
+
const variantClasses = { primary: "bg-blue-500 text-white" };
|
|
1768
|
+
|
|
1769
|
+
// ❌ Bad — leading/trailing spaces in template literal
|
|
1770
|
+
const buttonClasses = ` flex items-center ${className} `;
|
|
1771
|
+
```
|
|
1772
|
+
|
|
1773
|
+
---
|
|
1774
|
+
|
|
1575
1775
|
### `jsx-children-on-new-line`
|
|
1576
1776
|
|
|
1577
1777
|
**What it does:** When a JSX element has multiple children, ensures each child is on its own line with proper indentation.
|
|
@@ -2195,6 +2395,139 @@ const item = data[ index ];
|
|
|
2195
2395
|
|
|
2196
2396
|
<br />
|
|
2197
2397
|
|
|
2398
|
+
## 🧩 Component Rules
|
|
2399
|
+
|
|
2400
|
+
### `component-props-destructure`
|
|
2401
|
+
|
|
2402
|
+
**What it does:** Enforces that React component props must be destructured in the function parameter, not received as a single `props` object.
|
|
2403
|
+
|
|
2404
|
+
**Why use it:** Destructured props make it immediately clear what props a component uses. It improves readability and helps catch unused props.
|
|
2405
|
+
|
|
2406
|
+
```typescript
|
|
2407
|
+
// ✅ Good — props are destructured
|
|
2408
|
+
export const Button = ({ label, onClick, variant = "primary" }) => (
|
|
2409
|
+
<button onClick={onClick} type="button">
|
|
2410
|
+
{label}
|
|
2411
|
+
</button>
|
|
2412
|
+
);
|
|
2413
|
+
|
|
2414
|
+
export const Card = ({
|
|
2415
|
+
children,
|
|
2416
|
+
className = "",
|
|
2417
|
+
title,
|
|
2418
|
+
} : {
|
|
2419
|
+
children: ReactNode,
|
|
2420
|
+
className?: string,
|
|
2421
|
+
title: string,
|
|
2422
|
+
}) => (
|
|
2423
|
+
<div className={className}>
|
|
2424
|
+
<h2>{title}</h2>
|
|
2425
|
+
{children}
|
|
2426
|
+
</div>
|
|
2427
|
+
);
|
|
2428
|
+
|
|
2429
|
+
// ❌ Bad — props received as single object
|
|
2430
|
+
export const Button = (props) => (
|
|
2431
|
+
<button onClick={props.onClick} type="button">
|
|
2432
|
+
{props.label}
|
|
2433
|
+
</button>
|
|
2434
|
+
);
|
|
2435
|
+
|
|
2436
|
+
export const Card = (props: CardPropsInterface) => (
|
|
2437
|
+
<div className={props.className}>
|
|
2438
|
+
<h2>{props.title}</h2>
|
|
2439
|
+
{props.children}
|
|
2440
|
+
</div>
|
|
2441
|
+
);
|
|
2442
|
+
```
|
|
2443
|
+
|
|
2444
|
+
---
|
|
2445
|
+
|
|
2446
|
+
### `component-props-inline-type`
|
|
2447
|
+
|
|
2448
|
+
**What it does:** Enforces that React component props must use inline type annotation instead of referencing an interface or type alias. Also enforces:
|
|
2449
|
+
- Exactly one space before and after colon: `} : {`
|
|
2450
|
+
- Props in type must match exactly with destructured props (no missing or extra)
|
|
2451
|
+
- Each prop type on its own line when there are multiple props
|
|
2452
|
+
- First prop type must be on new line after `{` when multiple props
|
|
2453
|
+
- No empty lines after opening brace or before closing brace
|
|
2454
|
+
- No space before `?` in optional properties (`prop?: type` not `prop ?: type`)
|
|
2455
|
+
- Trailing commas (not semicolons) for each prop type
|
|
2456
|
+
- No empty lines between prop types
|
|
2457
|
+
|
|
2458
|
+
**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.
|
|
2459
|
+
|
|
2460
|
+
```typescript
|
|
2461
|
+
// ✅ Good — inline type annotation with matching props
|
|
2462
|
+
export const Button = ({ label } : { label: string }) => (
|
|
2463
|
+
<button type="button">{label}</button>
|
|
2464
|
+
);
|
|
2465
|
+
|
|
2466
|
+
export const Card = ({
|
|
2467
|
+
className = "",
|
|
2468
|
+
description,
|
|
2469
|
+
title,
|
|
2470
|
+
} : {
|
|
2471
|
+
className?: string,
|
|
2472
|
+
description?: string,
|
|
2473
|
+
title: string,
|
|
2474
|
+
}) => (
|
|
2475
|
+
<div className={className}>
|
|
2476
|
+
<h1>{title}</h1>
|
|
2477
|
+
{description && <p>{description}</p>}
|
|
2478
|
+
</div>
|
|
2479
|
+
);
|
|
2480
|
+
|
|
2481
|
+
// ❌ Bad — interface reference instead of inline type
|
|
2482
|
+
interface ButtonPropsInterface {
|
|
2483
|
+
label: string,
|
|
2484
|
+
}
|
|
2485
|
+
export const Button = ({ label }: ButtonPropsInterface) => (
|
|
2486
|
+
<button type="button">{label}</button>
|
|
2487
|
+
);
|
|
2488
|
+
|
|
2489
|
+
// ❌ Bad — missing space before and after colon
|
|
2490
|
+
export const Button = ({ label }:{ label: string }) => (
|
|
2491
|
+
<button type="button">{label}</button>
|
|
2492
|
+
);
|
|
2493
|
+
|
|
2494
|
+
// ❌ Bad — props don't match (extra 'flag' in type, missing in destructured)
|
|
2495
|
+
export const Card = ({
|
|
2496
|
+
title,
|
|
2497
|
+
} : {
|
|
2498
|
+
flag: boolean,
|
|
2499
|
+
title: string,
|
|
2500
|
+
}) => (
|
|
2501
|
+
<div>{title}</div>
|
|
2502
|
+
);
|
|
2503
|
+
|
|
2504
|
+
// ❌ Bad — semicolons instead of commas
|
|
2505
|
+
export const Card = ({ title } : { title: string; }) => (
|
|
2506
|
+
<div>{title}</div>
|
|
2507
|
+
);
|
|
2508
|
+
|
|
2509
|
+
// ❌ Bad — first prop on same line as opening brace
|
|
2510
|
+
export const Card = ({
|
|
2511
|
+
title,
|
|
2512
|
+
} : { title: string,
|
|
2513
|
+
className?: string,
|
|
2514
|
+
}) => (
|
|
2515
|
+
<div>{title}</div>
|
|
2516
|
+
);
|
|
2517
|
+
|
|
2518
|
+
// ❌ Bad — space before ? in optional property
|
|
2519
|
+
export const Card = ({ title } : { title ?: string }) => (
|
|
2520
|
+
<div>{title}</div>
|
|
2521
|
+
);
|
|
2522
|
+
|
|
2523
|
+
// ❌ Bad — props on same line when multiple
|
|
2524
|
+
export const Card = ({ a, b } : { a: string, b: string }) => (
|
|
2525
|
+
<div>{a}{b}</div>
|
|
2526
|
+
);
|
|
2527
|
+
```
|
|
2528
|
+
|
|
2529
|
+
<br />
|
|
2530
|
+
|
|
2198
2531
|
## 🔷 TypeScript Rules
|
|
2199
2532
|
|
|
2200
2533
|
### `enum-format`
|
|
@@ -2321,6 +2654,45 @@ export type ConfigType = {
|
|
|
2321
2654
|
|
|
2322
2655
|
---
|
|
2323
2656
|
|
|
2657
|
+
### `type-annotation-spacing`
|
|
2658
|
+
|
|
2659
|
+
**What it does:** Enforces consistent spacing in TypeScript type annotations:
|
|
2660
|
+
- No space before the colon in type annotations: `name: string` not `name : string`
|
|
2661
|
+
- One space after the colon: `name: string` not `name:string`
|
|
2662
|
+
- No space before generic type parameters: `Array<T>` not `Array <T>`
|
|
2663
|
+
- No space before array brackets: `string[]` not `string []`
|
|
2664
|
+
|
|
2665
|
+
**Why use it:** Consistent type annotation spacing follows TypeScript conventions and improves code readability.
|
|
2666
|
+
|
|
2667
|
+
```typescript
|
|
2668
|
+
// ✅ Good — proper spacing
|
|
2669
|
+
const name: string = "John";
|
|
2670
|
+
const items: string[] = [];
|
|
2671
|
+
const data: Array<number> = [];
|
|
2672
|
+
const handler = (value: string): boolean => true;
|
|
2673
|
+
|
|
2674
|
+
function getData<T>(id: string): Promise<T> {
|
|
2675
|
+
return fetch(id);
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
// ❌ Bad — space before colon
|
|
2679
|
+
const name : string = "John";
|
|
2680
|
+
const handler = (value : string) : boolean => true;
|
|
2681
|
+
|
|
2682
|
+
// ❌ Bad — no space after colon
|
|
2683
|
+
const name:string = "John";
|
|
2684
|
+
const handler = (value:string):boolean => true;
|
|
2685
|
+
|
|
2686
|
+
// ❌ Bad — space before generic
|
|
2687
|
+
const data: Array <number> = [];
|
|
2688
|
+
function getData <T>(id: string): Promise <T> {}
|
|
2689
|
+
|
|
2690
|
+
// ❌ Bad — space before array brackets
|
|
2691
|
+
const items: string [] = [];
|
|
2692
|
+
```
|
|
2693
|
+
|
|
2694
|
+
---
|
|
2695
|
+
|
|
2324
2696
|
### `typescript-definition-location`
|
|
2325
2697
|
|
|
2326
2698
|
**What it does:** Enforces that TypeScript definitions are placed in their designated folders:
|
|
@@ -2368,13 +2740,184 @@ export enum StatusEnum { // Should be in enums folder, not types
|
|
|
2368
2740
|
|
|
2369
2741
|
<br />
|
|
2370
2742
|
|
|
2743
|
+
## ⚛️ React Rules
|
|
2744
|
+
|
|
2745
|
+
### `react-code-order`
|
|
2746
|
+
|
|
2747
|
+
**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.
|
|
2748
|
+
|
|
2749
|
+
**Order (top to bottom):**
|
|
2750
|
+
1. Props/params destructure (in function signature: `({ prop1, prop2 })`)
|
|
2751
|
+
2. Props/params destructure in body (`const { x } = propValue` where propValue is a prop)
|
|
2752
|
+
3. `useRef` declarations
|
|
2753
|
+
4. `useState` declarations
|
|
2754
|
+
5. `useReducer` declarations
|
|
2755
|
+
6. `useSelector` / `useDispatch` (Redux hooks)
|
|
2756
|
+
7. Router hooks (`useNavigate`, `useLocation`, `useParams`, `useSearchParams`)
|
|
2757
|
+
8. Context hooks (`useContext`, `useToast`, etc.)
|
|
2758
|
+
9. Custom hooks (`use*` pattern)
|
|
2759
|
+
10. Derived state / variables (computed from hooks above, e.g., `const isSearching = term.length > 0`)
|
|
2760
|
+
11. `useMemo` declarations
|
|
2761
|
+
12. `useCallback` declarations
|
|
2762
|
+
13. Handler functions (`const handleX = () => {}`)
|
|
2763
|
+
14. `useEffect` / `useLayoutEffect`
|
|
2764
|
+
15. Return statement
|
|
2765
|
+
|
|
2766
|
+
**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.
|
|
2767
|
+
|
|
2768
|
+
```typescript
|
|
2769
|
+
// ✅ Good — Component follows the correct order
|
|
2770
|
+
const UserDashboard = ({ title }) => {
|
|
2771
|
+
// 1. useRef
|
|
2772
|
+
const inputRef = useRef(null);
|
|
2773
|
+
|
|
2774
|
+
// 2. useState
|
|
2775
|
+
const [count, setCount] = useState(0);
|
|
2776
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
2777
|
+
|
|
2778
|
+
// 3. Redux hooks
|
|
2779
|
+
const dispatch = useDispatch();
|
|
2780
|
+
const user = useSelector((state) => state.user);
|
|
2781
|
+
|
|
2782
|
+
// 4. Router hooks
|
|
2783
|
+
const navigate = useNavigate();
|
|
2784
|
+
const { id } = useParams();
|
|
2785
|
+
|
|
2786
|
+
// 5. Custom hooks
|
|
2787
|
+
const { data, loading } = useFetchData(id);
|
|
2788
|
+
|
|
2789
|
+
// 6. Derived state
|
|
2790
|
+
const isReady = !loading && data !== null;
|
|
2791
|
+
const displayName = user?.name ?? "Guest";
|
|
2792
|
+
|
|
2793
|
+
// 7. useMemo
|
|
2794
|
+
const filteredItems = useMemo(
|
|
2795
|
+
() => data?.filter((item) => item.active),
|
|
2796
|
+
[data],
|
|
2797
|
+
);
|
|
2798
|
+
|
|
2799
|
+
// 8. useCallback
|
|
2800
|
+
const handleSubmit = useCallback(
|
|
2801
|
+
() => {
|
|
2802
|
+
dispatch(submitAction());
|
|
2803
|
+
},
|
|
2804
|
+
[dispatch],
|
|
2805
|
+
);
|
|
2806
|
+
|
|
2807
|
+
// 9. Handler functions
|
|
2808
|
+
const resetHandler = () => {
|
|
2809
|
+
setCount(0);
|
|
2810
|
+
setIsLoading(false);
|
|
2811
|
+
};
|
|
2812
|
+
|
|
2813
|
+
// 10. useEffect
|
|
2814
|
+
useEffect(
|
|
2815
|
+
() => {
|
|
2816
|
+
inputRef.current?.focus();
|
|
2817
|
+
},
|
|
2818
|
+
[],
|
|
2819
|
+
);
|
|
2820
|
+
|
|
2821
|
+
// 11. Return
|
|
2822
|
+
return (
|
|
2823
|
+
<div>
|
|
2824
|
+
<h1>{title}</h1>
|
|
2825
|
+
<span>{displayName}</span>
|
|
2826
|
+
</div>
|
|
2827
|
+
);
|
|
2828
|
+
};
|
|
2829
|
+
|
|
2830
|
+
// ✅ Good — Custom hook follows the correct order
|
|
2831
|
+
const useCreateAccount = () => {
|
|
2832
|
+
// 1. useState
|
|
2833
|
+
const [loading, setLoading] = useState(false);
|
|
2834
|
+
const [created, setCreated] = useState(false);
|
|
2835
|
+
|
|
2836
|
+
// 2. Redux hooks
|
|
2837
|
+
const dispatch = useDispatch();
|
|
2838
|
+
|
|
2839
|
+
// 3. Context hooks
|
|
2840
|
+
const { toast } = useToast();
|
|
2841
|
+
|
|
2842
|
+
// 4. Handler functions
|
|
2843
|
+
const createAccountHandler = async (data: AccountData) => {
|
|
2844
|
+
setLoading(true);
|
|
2845
|
+
try {
|
|
2846
|
+
await api.createAccount(data);
|
|
2847
|
+
setCreated(true);
|
|
2848
|
+
} catch (error) {
|
|
2849
|
+
toast({ description: "Failed to create account" });
|
|
2850
|
+
} finally {
|
|
2851
|
+
setLoading(false);
|
|
2852
|
+
}
|
|
2853
|
+
};
|
|
2854
|
+
|
|
2855
|
+
// 5. useEffect
|
|
2856
|
+
useEffect(
|
|
2857
|
+
() => {
|
|
2858
|
+
if (created) {
|
|
2859
|
+
setTimeout(() => setCreated(false), 50);
|
|
2860
|
+
}
|
|
2861
|
+
},
|
|
2862
|
+
[created],
|
|
2863
|
+
);
|
|
2864
|
+
|
|
2865
|
+
// 6. Return
|
|
2866
|
+
return { createAccountHandler, created, loading };
|
|
2867
|
+
};
|
|
2868
|
+
|
|
2869
|
+
// ❌ Bad — useEffect before useState
|
|
2870
|
+
const BadComponent = ({ title }) => {
|
|
2871
|
+
useEffect(() => {
|
|
2872
|
+
console.log("mounted");
|
|
2873
|
+
}, []);
|
|
2874
|
+
|
|
2875
|
+
const [count, setCount] = useState(0);
|
|
2876
|
+
|
|
2877
|
+
return <div>{title}</div>;
|
|
2878
|
+
};
|
|
2879
|
+
|
|
2880
|
+
// ❌ Bad — context hook before useState in custom hook
|
|
2881
|
+
const useBadHook = () => {
|
|
2882
|
+
const { toast } = useToast(); // Should come after useState
|
|
2883
|
+
const [loading, setLoading] = useState(false);
|
|
2884
|
+
return { loading };
|
|
2885
|
+
};
|
|
2886
|
+
|
|
2887
|
+
// ❌ Bad — handler before hooks
|
|
2888
|
+
const AnotherBadComponent = ({ title }) => {
|
|
2889
|
+
const handleClick = () => {
|
|
2890
|
+
console.log("clicked");
|
|
2891
|
+
};
|
|
2892
|
+
|
|
2893
|
+
const dispatch = useDispatch();
|
|
2894
|
+
const [count, setCount] = useState(0);
|
|
2895
|
+
|
|
2896
|
+
return <div onClick={handleClick}>{title}</div>;
|
|
2897
|
+
};
|
|
2898
|
+
|
|
2899
|
+
// ❌ Bad — derived state after handler
|
|
2900
|
+
const YetAnotherBad = ({ title }) => {
|
|
2901
|
+
const [items, setItems] = useState([]);
|
|
2902
|
+
|
|
2903
|
+
const handleAdd = () => {
|
|
2904
|
+
setItems([...items, "new"]);
|
|
2905
|
+
};
|
|
2906
|
+
|
|
2907
|
+
const itemCount = items.length; // Should come before handleAdd
|
|
2908
|
+
|
|
2909
|
+
return <div>{itemCount}</div>;
|
|
2910
|
+
};
|
|
2911
|
+
```
|
|
2912
|
+
|
|
2913
|
+
<br />
|
|
2914
|
+
|
|
2371
2915
|
## 📝 Variable Rules
|
|
2372
2916
|
|
|
2373
2917
|
### `variable-naming-convention`
|
|
2374
2918
|
|
|
2375
2919
|
**What it does:** Enforces naming conventions for variables:
|
|
2376
|
-
- **camelCase** for
|
|
2377
|
-
- **UPPER_CASE** for constants (primitive values)
|
|
2920
|
+
- **camelCase** for all variables and constants
|
|
2378
2921
|
- **PascalCase** for React components and classes
|
|
2379
2922
|
- **camelCase with `use` prefix** for hooks
|
|
2380
2923
|
|
|
@@ -2384,14 +2927,14 @@ export enum StatusEnum { // Should be in enums folder, not types
|
|
|
2384
2927
|
// ✅ Good — correct conventions
|
|
2385
2928
|
const userName = "John"; // camelCase for variables
|
|
2386
2929
|
const itemCount = 42; // camelCase for variables
|
|
2387
|
-
const
|
|
2388
|
-
const
|
|
2930
|
+
const maxRetries = 3; // camelCase for constants
|
|
2931
|
+
const apiBaseUrl = "/api"; // camelCase for constants
|
|
2389
2932
|
const UserProfile = () => <div />; // PascalCase for components
|
|
2390
2933
|
const useAuth = () => {}; // camelCase with use prefix for hooks
|
|
2391
2934
|
|
|
2392
2935
|
// ❌ Bad — wrong conventions
|
|
2393
2936
|
const user_name = "John"; // snake_case
|
|
2394
|
-
const
|
|
2937
|
+
const MAX_RETRIES = 3; // should be camelCase
|
|
2395
2938
|
const userProfile = () => <div />; // should be PascalCase
|
|
2396
2939
|
const UseAuth = () => {}; // hooks should be camelCase
|
|
2397
2940
|
```
|