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.
- package/AGENTS.md +27 -29
- package/README.md +419 -10
- package/index.d.ts +8 -0
- package/index.js +4407 -426
- 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
|
|
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
|
|
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 +
|
|
20
|
-
| React +
|
|
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
|
+
├── ... (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 (
|
|
201
|
-
|
|
202
|
-
- **Array
|
|
203
|
-
- **Arrow
|
|
204
|
-
- **
|
|
205
|
-
- **
|
|
206
|
-
- **
|
|
207
|
-
- **
|
|
208
|
-
- **
|
|
209
|
-
- **
|
|
210
|
-
- **Import/Export
|
|
211
|
-
- **JSX
|
|
212
|
-
- **
|
|
213
|
-
- **
|
|
214
|
-
- **
|
|
215
|
-
- **
|
|
216
|
-
- **
|
|
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
|
|
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
|
-
*
|
|
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 **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
|
|
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",
|
|
@@ -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 **
|
|
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;
|