eslint-plugin-code-style 1.1.10 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/AGENTS.md +27 -29
  2. package/README.md +419 -10
  3. package/index.d.ts +8 -0
  4. package/index.js +4407 -426
  5. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -4,20 +4,21 @@ Instructions for AI coding agents working with this codebase.
4
4
 
5
5
  ## Project Overview
6
6
 
7
- **eslint-plugin-code-style** is an ESLint plugin providing 47 custom auto-fixable formatting rules for React/JSX projects. It's designed for ESLint v9+ flat config system.
7
+ **eslint-plugin-code-style** is an ESLint plugin providing 56 custom auto-fixable formatting rules for React/JSX projects. It's designed for ESLint v9+ flat config system.
8
8
 
9
- - **Main entry:** `index.js` - Contains all 47 rules in a single file
9
+ - **Main entry:** `index.js` - Contains all 56 rules in a single file
10
10
  - **Type definitions:** `index.d.ts` - TypeScript declarations for IDE support
11
11
  - **Recommended configs:** `recommended-configs/` - Ready-to-use ESLint configurations
12
12
  - **Test apps:** `_tests_/` - Sample apps for testing rules
13
13
 
14
14
  ### Available Configurations
15
15
 
16
- | Config | Folder | Status |
17
- |--------|--------|--------|
18
- | React (JS) | `react/` | Available |
19
- | React + TypeScript | `react-ts/` | Coming Soon |
20
- | React + TS + Tailwind | `react-ts-tw/` | Coming Soon |
16
+ | Config | Recommended Folder | Test Folder | Status |
17
+ |--------|-------------------|-------------|--------|
18
+ | React (JS) | `recommended-configs/react/` | `_tests_/react/` | Available |
19
+ | React + TS + Tailwind | `recommended-configs/react-ts-tw/` | `_tests_/react-ts-tw/` | Available |
20
+ | React + TypeScript | - | - | Coming Soon |
21
+ | React + Tailwind | - | - | Coming Soon |
21
22
 
22
23
  ## Build & Test Commands
23
24
 
@@ -43,7 +44,7 @@ index.js
43
44
  ├── imports (fs, path, url)
44
45
  ├── Rule 1 definition (const ruleName = { create(), meta: {} })
45
46
  ├── Rule 2 definition
46
- ├── ... (47 rules total)
47
+ ├── ... (56 rules total)
47
48
  └── export default { meta: {}, rules: {} }
48
49
  ```
49
50
 
@@ -197,26 +198,23 @@ if (node.parent?.type === "CallExpression") return;
197
198
 
198
199
  ## Rule Categories
199
200
 
200
- Rules are organized in these categories (see default export):
201
-
202
- - **Array rules:** `array-*`
203
- - **Arrow function rules:** `arrow-function-*`, `curried-arrow-*`
204
- - **Assignment rules:** `assignment-*`
205
- - **Block statement rules:** `block-statement-*`
206
- - **Comment rules:** `comment-*`
207
- - **Function rules:** `function-*`
208
- - **Hook rules:** `hook-*`
209
- - **If statement rules:** `if-statement-*`, `multiline-if-*`
210
- - **Import/Export rules:** `absolute-imports-*`, `export-*`, `import-*`, `index-export-*`, `module-index-*`
211
- - **JSX rules:** `jsx-*`
212
- - **Member expression rules:** `member-expression-*`
213
- - **Nested call rules:** `nested-call-*`
214
- - **No empty lines rules:** `no-empty-lines-*`
215
- - **Object property rules:** `object-property-*`
216
- - **Opening brackets rules:** `opening-brackets-*`
217
- - **Simple call rules:** `simple-call-*`, `single-argument-*`
218
- - **String property rules:** `string-property-*`
219
- - **Variable rules:** `variable-*`
201
+ Rules are organized in these categories (alphabetically sorted in index.js and README.md):
202
+
203
+ - **Array Rules:** `array-items-per-line`, `array-objects-on-new-lines`
204
+ - **Arrow Function Rules:** `arrow-function-block-body`, `arrow-function-simple-jsx`, `arrow-function-simplify`, `curried-arrow-same-line`
205
+ - **Call Expression Rules:** `function-arguments-format`, `nested-call-closing-brackets`, `no-empty-lines-in-function-calls`, `opening-brackets-same-line`, `simple-call-single-line`, `single-argument-on-one-line`
206
+ - **Comment Rules:** `comment-format`
207
+ - **Component Rules:** `component-props-destructure`, `component-props-inline-type`
208
+ - **Control Flow Rules:** `block-statement-newlines`, `if-statement-format`, `multiline-if-conditions`, `no-empty-lines-in-switch-cases`
209
+ - **Function Rules:** `function-call-spacing`, `function-naming-convention`, `function-object-destructure`, `function-params-per-line`, `no-empty-lines-in-function-params`
210
+ - **Hook Rules:** `hook-callback-format`, `hook-deps-per-line`
211
+ - **Import/Export Rules:** `absolute-imports-only`, `export-format`, `import-format`, `import-source-spacing`, `index-export-style`, `module-index-exports`
212
+ - **JSX Rules:** `jsx-children-on-new-line`, `jsx-closing-bracket-spacing`, `jsx-element-child-new-line`, `jsx-logical-expression-simplify`, `jsx-parentheses-position`, `jsx-prop-naming-convention`, `jsx-simple-element-one-line`, `jsx-string-value-trim`, `jsx-ternary-format`, `no-empty-lines-in-jsx`
213
+ - **Object Rules:** `no-empty-lines-in-objects`, `object-property-per-line`, `object-property-value-brace`, `object-property-value-format`, `string-property-spacing`
214
+ - **React Rules:** `react-code-order`
215
+ - **Spacing Rules:** `assignment-value-same-line`, `member-expression-bracket-spacing`
216
+ - **TypeScript Rules:** `enum-format`, `interface-format`, `type-annotation-spacing`, `type-format`, `typescript-definition-location`
217
+ - **Variable Rules:** `variable-naming-convention`
220
218
 
221
219
  ## Naming Conventions
222
220
 
@@ -232,7 +230,7 @@ Rules are organized in these categories (see default export):
232
230
 
233
231
  ## Documentation Files
234
232
 
235
- - `README.md` - Main documentation with all 47 rules
233
+ - `README.md` - Main documentation with all 56 rules
236
234
  - `recommended-configs/<config-name>/README.md` - Config-specific documentation (references main README for rule details)
237
235
  - `index.d.ts` - TypeScript types for IDE autocomplete
238
236
 
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
- *51 auto-fixable rules to keep your codebase clean and consistent*
21
+ *56 auto-fixable rules to keep your codebase clean and consistent*
22
22
 
23
23
  </div>
24
24
 
@@ -26,7 +26,7 @@
26
26
 
27
27
  ## 🎯 Why This Plugin?
28
28
 
29
- This plugin provides **51 custom auto-fixable rules** for code formatting. Built for **ESLint v9 flat configs**.
29
+ This plugin provides **56 custom auto-fixable rules** for code formatting. Built for **ESLint v9 flat configs**.
30
30
 
31
31
  > **Note:** ESLint [deprecated 79 formatting rules](https://eslint.org/blog/2023/10/deprecating-formatting-rules/) in v8.53.0. Our recommended configs use `@stylistic/eslint-plugin` as the replacement for these deprecated rules.
32
32
 
@@ -35,7 +35,7 @@ This plugin provides **51 custom auto-fixable rules** for code formatting. Built
35
35
  - **Works alongside existing tools** — Complements ESLint's built-in rules and packages like eslint-plugin-react, eslint-plugin-import, etc
36
36
  - **Self-sufficient rules** — Each rule handles complete formatting independently
37
37
  - **Consistency at scale** — Reduces code-style differences between team members by enforcing uniform formatting across your projects
38
- - **Fully automated** — All 47 rules support auto-fix, eliminating manual style reviews
38
+ - **Fully automated** — All 56 rules support auto-fix, eliminating manual style reviews
39
39
 
40
40
  When combined with ESLint's native rules and other popular plugins, this package helps create a complete code style solution that keeps your codebase clean and consistent.
41
41
 
@@ -59,17 +59,19 @@ We provide **ready-to-use ESLint flat configuration files** that combine `eslint
59
59
 
60
60
  ### 💡 Why Use These Configs?
61
61
 
62
- - **Complete Coverage** — Combines ESLint built-in rules, third-party plugins, and all 51 code-style rules
62
+ - **Complete Coverage** — Combines ESLint built-in rules, third-party plugins, and all 56 code-style rules
63
63
  - **Ready-to-Use** — Copy the config file and start linting immediately
64
64
  - **Battle-Tested** — These configurations have been refined through real-world usage
65
65
  - **Fully Documented** — Each config includes detailed instructions and explanations
66
66
 
67
67
  ### 📋 Available Configurations
68
68
 
69
- | Configuration | Description | Link |
70
- |---------------|-------------|------|
69
+ | Configuration | Description | Status |
70
+ |---------------|-------------|--------|
71
71
  | **React** | React.js projects (JavaScript, JSX) | [View Config](./recommended-configs/react/) |
72
72
  | **React + TS + Tailwind** | React + TypeScript + Tailwind CSS | [View Config](./recommended-configs/react-ts-tw/) |
73
+ | **React + TypeScript** | React + TypeScript projects | Coming Soon |
74
+ | **React + Tailwind** | React + Tailwind CSS projects | Coming Soon |
73
75
 
74
76
  ### ⚡ Quick Start with Recommended Config
75
77
 
@@ -94,7 +96,7 @@ We provide **ready-to-use ESLint flat configuration files** that combine `eslint
94
96
  <td width="50%">
95
97
 
96
98
  ### 🔧 Auto-Fixable Rules
97
- All **51 rules** support automatic fixing with `eslint --fix`. No manual code changes needed.
99
+ All **56 rules** support automatic fixing with `eslint --fix`. No manual code changes needed.
98
100
 
99
101
  </td>
100
102
  <td width="50%">
@@ -187,8 +189,12 @@ rules: {
187
189
  "code-style/assignment-value-same-line": "error",
188
190
  "code-style/block-statement-newlines": "error",
189
191
  "code-style/comment-format": "error",
192
+ "code-style/component-props-destructure": "error",
193
+ "code-style/react-code-order": "error",
194
+ "code-style/component-props-inline-type": "error",
190
195
  "code-style/function-call-spacing": "error",
191
196
  "code-style/function-naming-convention": "error",
197
+ "code-style/function-object-destructure": "error",
192
198
  "code-style/function-params-per-line": "error",
193
199
  "code-style/hook-callback-format": "error",
194
200
  "code-style/hook-deps-per-line": "error",
@@ -227,6 +233,7 @@ rules: {
227
233
  "code-style/variable-naming-convention": "error",
228
234
  "code-style/enum-format": "error",
229
235
  "code-style/interface-format": "error",
236
+ "code-style/type-annotation-spacing": "error",
230
237
  "code-style/type-format": "error",
231
238
  "code-style/typescript-definition-location": "error",
232
239
  }
@@ -238,7 +245,7 @@ rules: {
238
245
 
239
246
  ## 📖 Rules Summary
240
247
 
241
- > All **51 rules** are auto-fixable. See detailed examples for each rule in the [Rules Reference](#-rules-reference) section below.
248
+ > All **56 rules** are auto-fixable. See detailed examples for each rule in the [Rules Reference](#-rules-reference) section below.
242
249
  >
243
250
  > Rules marked with ⚙️ support customization options (e.g., extending default folder lists).
244
251
 
@@ -261,6 +268,9 @@ rules: {
261
268
  | `single-argument-on-one-line` | Single simple argument stays on one line: `fn(x)` not expanded |
262
269
  | **Comment Rules** | |
263
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 |
264
274
  | **Control Flow Rules** | |
265
275
  | `block-statement-newlines` | Newline after `{` and before `}` in if/for/while/function blocks |
266
276
  | `if-statement-format` | `{` on same line as `if`/`else if`, `else` on same line as `}`, proper spacing |
@@ -269,6 +279,7 @@ rules: {
269
279
  | **Function Rules** | |
270
280
  | `function-call-spacing` | No space between function name and `(`: `fn()` not `fn ()` |
271
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 |
272
283
  | `function-params-per-line` | When multiline, each param on own line with consistent indentation |
273
284
  | `no-empty-lines-in-function-params` | No empty lines between parameters or after `(`/before `)` |
274
285
  | **Hook Rules** | |
@@ -304,8 +315,11 @@ rules: {
304
315
  | **TypeScript Rules** | |
305
316
  | `enum-format` | Enforce enum naming (PascalCase + Enum suffix), UPPER_CASE members, no empty lines, and trailing commas |
306
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 |
307
319
  | `type-format` | Enforce type naming (PascalCase + Type suffix), camelCase properties, no empty lines, and trailing commas |
308
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 |
309
323
  | **Variable Rules** | |
310
324
  | `variable-naming-convention` | camelCase for variables, UPPER_CASE for constants, PascalCase for components, `use` prefix for hooks |
311
325
 
@@ -1091,6 +1105,57 @@ function get_user_data() {}
1091
1105
 
1092
1106
  ---
1093
1107
 
1108
+ ### `function-object-destructure`
1109
+
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).
1111
+
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.
1113
+
1114
+ ```typescript
1115
+ // ✅ Good — typed param with destructuring in body
1116
+ const createUserHandler = async (data: CreateUserParamsInterface) => {
1117
+ const { age, email, isActive, name } = data;
1118
+
1119
+ // Use age, email, isActive, name...
1120
+ };
1121
+
1122
+ const updateUserHandler = (params: UpdateParamsInterface) => {
1123
+ const { id, updates } = params;
1124
+
1125
+ // Use id, updates...
1126
+ };
1127
+
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
+ );
1138
+
1139
+ // ❌ Bad — non-component function destructures in signature
1140
+ const createUserHandler = async ({
1141
+ age,
1142
+ email,
1143
+ isActive,
1144
+ name,
1145
+ }: CreateUserParamsInterface) => {
1146
+ // ...
1147
+ };
1148
+
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
+ };
1155
+ ```
1156
+
1157
+ ---
1158
+
1094
1159
  ### `function-params-per-line`
1095
1160
 
1096
1161
  **What it does:** When function parameters span multiple lines, ensures each parameter is on its own line with consistent indentation.
@@ -1299,7 +1364,7 @@ import { fetchUsers } from "@/apis/users/fetchUsers";
1299
1364
  ```
1300
1365
 
1301
1366
  **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`
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`
1303
1368
 
1304
1369
  **Customization Options:**
1305
1370
 
@@ -1543,7 +1608,7 @@ import { Button } from "@/components/Button/Button"; // Avoid this!
1543
1608
  ```
1544
1609
 
1545
1610
  **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`
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`
1547
1612
 
1548
1613
  **Default Ignore Patterns:**
1549
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`
@@ -2195,6 +2260,139 @@ const item = data[ index ];
2195
2260
 
2196
2261
  <br />
2197
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
+
2198
2396
  ## 🔷 TypeScript Rules
2199
2397
 
2200
2398
  ### `enum-format`
@@ -2321,6 +2519,45 @@ export type ConfigType = {
2321
2519
 
2322
2520
  ---
2323
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
+
2324
2561
  ### `typescript-definition-location`
2325
2562
 
2326
2563
  **What it does:** Enforces that TypeScript definitions are placed in their designated folders:
@@ -2368,6 +2605,178 @@ export enum StatusEnum { // Should be in enums folder, not types
2368
2605
 
2369
2606
  <br />
2370
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
+
2371
2780
  ## 📝 Variable Rules
2372
2781
 
2373
2782
  ### `variable-naming-convention`
package/index.d.ts CHANGED
@@ -13,11 +13,15 @@ export type RuleNames =
13
13
  | "code-style/assignment-value-same-line"
14
14
  | "code-style/block-statement-newlines"
15
15
  | "code-style/comment-format"
16
+ | "code-style/component-props-destructure"
17
+ | "code-style/react-code-order"
18
+ | "code-style/component-props-inline-type"
16
19
  | "code-style/curried-arrow-same-line"
17
20
  | "code-style/export-format"
18
21
  | "code-style/function-arguments-format"
19
22
  | "code-style/function-call-spacing"
20
23
  | "code-style/function-naming-convention"
24
+ | "code-style/function-object-destructure"
21
25
  | "code-style/function-params-per-line"
22
26
  | "code-style/hook-callback-format"
23
27
  | "code-style/hook-deps-per-line"
@@ -86,11 +90,15 @@ interface PluginRules {
86
90
  "assignment-value-same-line": Rule.RuleModule;
87
91
  "block-statement-newlines": Rule.RuleModule;
88
92
  "comment-format": Rule.RuleModule;
93
+ "component-props-destructure": Rule.RuleModule;
94
+ "react-code-order": Rule.RuleModule;
95
+ "component-props-inline-type": Rule.RuleModule;
89
96
  "curried-arrow-same-line": Rule.RuleModule;
90
97
  "export-format": Rule.RuleModule;
91
98
  "function-arguments-format": Rule.RuleModule;
92
99
  "function-call-spacing": Rule.RuleModule;
93
100
  "function-naming-convention": Rule.RuleModule;
101
+ "function-object-destructure": Rule.RuleModule;
94
102
  "function-params-per-line": Rule.RuleModule;
95
103
  "hook-callback-format": Rule.RuleModule;
96
104
  "hook-deps-per-line": Rule.RuleModule;