eslint-plugin-code-style 2.0.0 → 2.0.2
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/README.md +5 -3
- package/dist/index.js +244 -244
- package/package.json +1 -5
- package/AGENTS.md +0 -1387
- package/CHANGELOG.md +0 -2087
- package/CLAUDE.md +0 -12
- package/docs/rules/README.md +0 -31
- package/docs/rules/arrays.md +0 -107
- package/docs/rules/arrow-functions.md +0 -115
- package/docs/rules/call-expressions.md +0 -275
- package/docs/rules/classes.md +0 -88
- package/docs/rules/comments.md +0 -42
- package/docs/rules/components.md +0 -330
- package/docs/rules/control-flow.md +0 -448
- package/docs/rules/functions.md +0 -232
- package/docs/rules/hooks.md +0 -147
- package/docs/rules/imports-exports.md +0 -383
- package/docs/rules/jsx.md +0 -518
- package/docs/rules/objects.md +0 -224
- package/docs/rules/react.md +0 -175
- package/docs/rules/spacing.md +0 -61
- package/docs/rules/strings.md +0 -92
- package/docs/rules/typescript.md +0 -482
- package/docs/rules/variables.md +0 -32
package/docs/rules/components.md
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
# Component Rules
|
|
2
|
-
|
|
3
|
-
### `component-props-destructure`
|
|
4
|
-
|
|
5
|
-
**What it does:** Enforces that React component props must be destructured in the function parameter, not received as a single `props` object.
|
|
6
|
-
|
|
7
|
-
**Why use it:** Destructured props make it immediately clear what props a component uses. It improves readability and helps catch unused props.
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
// Good — props are destructured
|
|
11
|
-
export const Button = ({ label, onClick, variant = "primary" }) => (
|
|
12
|
-
<button onClick={onClick} type="button">
|
|
13
|
-
{label}
|
|
14
|
-
</button>
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
export const Card = ({
|
|
18
|
-
children,
|
|
19
|
-
className = "",
|
|
20
|
-
title,
|
|
21
|
-
} : {
|
|
22
|
-
children: ReactNode,
|
|
23
|
-
className?: string,
|
|
24
|
-
title: string,
|
|
25
|
-
}) => (
|
|
26
|
-
<div className={className}>
|
|
27
|
-
<h2>{title}</h2>
|
|
28
|
-
{children}
|
|
29
|
-
</div>
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
// Bad — props received as single object
|
|
33
|
-
export const Button = (props) => (
|
|
34
|
-
<button onClick={props.onClick} type="button">
|
|
35
|
-
{props.label}
|
|
36
|
-
</button>
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
export const Card = (props: CardPropsInterface) => (
|
|
40
|
-
<div className={props.className}>
|
|
41
|
-
<h2>{props.title}</h2>
|
|
42
|
-
{props.children}
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
### `component-props-inline-type`
|
|
50
|
-
|
|
51
|
-
**What it does:** Enforces that React component props must use inline type annotation instead of referencing an interface or type alias. Also enforces:
|
|
52
|
-
- Exactly one space before and after colon: `} : {`
|
|
53
|
-
- Props in type must match exactly with destructured props (no missing or extra)
|
|
54
|
-
- Each prop type on its own line when there are multiple props
|
|
55
|
-
- First prop type must be on new line after `{` when multiple props
|
|
56
|
-
- No empty lines after opening brace or before closing brace
|
|
57
|
-
- No space before `?` in optional properties (`prop?: type` not `prop ?: type`)
|
|
58
|
-
- Trailing commas (not semicolons) for each prop type
|
|
59
|
-
- No empty lines between prop types
|
|
60
|
-
|
|
61
|
-
**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.
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
// Good — inline type annotation with matching props
|
|
65
|
-
export const Button = ({ label } : { label: string }) => (
|
|
66
|
-
<button type="button">{label}</button>
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
export const Card = ({
|
|
70
|
-
className = "",
|
|
71
|
-
description,
|
|
72
|
-
title,
|
|
73
|
-
} : {
|
|
74
|
-
className?: string,
|
|
75
|
-
description?: string,
|
|
76
|
-
title: string,
|
|
77
|
-
}) => (
|
|
78
|
-
<div className={className}>
|
|
79
|
-
<h1>{title}</h1>
|
|
80
|
-
{description && <p>{description}</p>}
|
|
81
|
-
</div>
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
// Bad — interface reference instead of inline type
|
|
85
|
-
interface ButtonPropsInterface {
|
|
86
|
-
label: string,
|
|
87
|
-
}
|
|
88
|
-
export const Button = ({ label }: ButtonPropsInterface) => (
|
|
89
|
-
<button type="button">{label}</button>
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
// Bad — missing space before and after colon
|
|
93
|
-
export const Button = ({ label }:{ label: string }) => (
|
|
94
|
-
<button type="button">{label}</button>
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
// Bad — props don't match (extra 'flag' in type, missing in destructured)
|
|
98
|
-
export const Card = ({
|
|
99
|
-
title,
|
|
100
|
-
} : {
|
|
101
|
-
flag: boolean,
|
|
102
|
-
title: string,
|
|
103
|
-
}) => (
|
|
104
|
-
<div>{title}</div>
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
// Bad — semicolons instead of commas
|
|
108
|
-
export const Card = ({ title } : { title: string; }) => (
|
|
109
|
-
<div>{title}</div>
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
// Bad — first prop on same line as opening brace
|
|
113
|
-
export const Card = ({
|
|
114
|
-
title,
|
|
115
|
-
} : { title: string,
|
|
116
|
-
className?: string,
|
|
117
|
-
}) => (
|
|
118
|
-
<div>{title}</div>
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// Bad — space before ? in optional property
|
|
122
|
-
export const Card = ({ title } : { title ?: string }) => (
|
|
123
|
-
<div>{title}</div>
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// Bad — props on same line when multiple
|
|
127
|
-
export const Card = ({ a, b } : { a: string, b: string }) => (
|
|
128
|
-
<div>{a}{b}</div>
|
|
129
|
-
);
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
### `folder-based-naming-convention`
|
|
135
|
-
|
|
136
|
-
**What it does:** Enforces naming conventions based on folder location, with chained folder names for nested files. Also enforces camelCase suffix for data/constants/strings/services/reducers folders (e.g., `authData`, `apiConstants`, `loginStrings`, `userServices`):
|
|
137
|
-
|
|
138
|
-
| Folder | Suffix | Example |
|
|
139
|
-
|--------|--------|---------|
|
|
140
|
-
| `views/` | View | `DashboardView` |
|
|
141
|
-
| `layouts/` | Layout | `MainLayout` |
|
|
142
|
-
| `pages/` | Page | `HomePage` |
|
|
143
|
-
| `providers/` | Provider | `AuthProvider` |
|
|
144
|
-
| `reducers/` | Reducer | `UserReducer` |
|
|
145
|
-
| `contexts/` | Context | `AuthContext` |
|
|
146
|
-
| `theme/` / `themes/` | Theme | `DarkTheme` |
|
|
147
|
-
| `data/` | Data (camelCase) | `authData` |
|
|
148
|
-
| `constants/` | Constants (camelCase) | `apiConstants` |
|
|
149
|
-
| `strings/` | Strings (camelCase) | `loginStrings` |
|
|
150
|
-
| `services/` | Services (camelCase) | `userServices` |
|
|
151
|
-
| `atoms/` | *(none)* | `Button` |
|
|
152
|
-
| `components/` | *(none)* | `Card` |
|
|
153
|
-
|
|
154
|
-
Nested files chain folder names (e.g., `layouts/auth/login.tsx` -> `LoginAuthLayout`, `atoms/input/password.tsx` -> `PasswordInput`).
|
|
155
|
-
|
|
156
|
-
**Why use it:** Consistent naming based on folder structure makes purpose immediately clear. The chained naming encodes the full path context into the name. The camelCase suffix for data/constants/strings/services/reducers folders distinguishes these utility modules from PascalCase component-like entities.
|
|
157
|
-
|
|
158
|
-
```tsx
|
|
159
|
-
// Good — suffix folders
|
|
160
|
-
// in views/dashboard.tsx
|
|
161
|
-
export const DashboardView = () => <div>Dashboard</div>;
|
|
162
|
-
|
|
163
|
-
// in layouts/auth/login.tsx (chained: Login + Auth + Layout)
|
|
164
|
-
export const LoginAuthLayout = () => <div>Login</div>;
|
|
165
|
-
|
|
166
|
-
// in providers/auth.tsx
|
|
167
|
-
export const AuthProvider = ({ children }) => <AuthContext.Provider>{children}</AuthContext.Provider>;
|
|
168
|
-
|
|
169
|
-
// in contexts/auth.ts
|
|
170
|
-
export const AuthContext = createContext(null);
|
|
171
|
-
|
|
172
|
-
// in reducers/user.ts
|
|
173
|
-
export const UserReducer = (state, action) => { ... };
|
|
174
|
-
|
|
175
|
-
// in themes/dark.ts
|
|
176
|
-
export const DarkTheme = { primary: "#000" };
|
|
177
|
-
|
|
178
|
-
// Good — camelCase suffix folders
|
|
179
|
-
// in data/auth.ts
|
|
180
|
-
export const authData = { ... };
|
|
181
|
-
|
|
182
|
-
// in constants/api.ts
|
|
183
|
-
export const apiConstants = { ... };
|
|
184
|
-
|
|
185
|
-
// in strings/login.ts
|
|
186
|
-
export const loginStrings = { ... };
|
|
187
|
-
|
|
188
|
-
// in services/user.ts
|
|
189
|
-
export const userServices = { ... };
|
|
190
|
-
|
|
191
|
-
// Good — no-suffix folders (chaining only)
|
|
192
|
-
// in atoms/input/password.tsx (chained: Password + Input)
|
|
193
|
-
export const PasswordInput = () => <input type="password" />;
|
|
194
|
-
|
|
195
|
-
// in atoms/button.tsx
|
|
196
|
-
export const Button = () => <button>Click</button>;
|
|
197
|
-
|
|
198
|
-
// in components/card.tsx
|
|
199
|
-
export const Card = () => <div>Card</div>;
|
|
200
|
-
|
|
201
|
-
// Bad
|
|
202
|
-
// in layouts/auth/login.tsx (should be "LoginAuthLayout")
|
|
203
|
-
export const Login = () => <div>Login</div>;
|
|
204
|
-
|
|
205
|
-
// in reducers/user.ts (should be "UserReducer")
|
|
206
|
-
export const User = (state, action) => { ... };
|
|
207
|
-
|
|
208
|
-
// in atoms/input/password.tsx (should be "PasswordInput")
|
|
209
|
-
export const Password = () => <input type="password" />;
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
> **Note:** Module barrel files (e.g., `views/index.ts`) are skipped. Interfaces, enums, and types have their own naming rules (`interface-format`, `enum-format`, `type-format`). Auto-fix renames the identifier and all its references.
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
### `folder-structure-consistency`
|
|
217
|
-
|
|
218
|
-
**What it does:** Enforces that module folders have a consistent internal structure — either all flat files or all wrapped in subfolders. Wrapped mode is only justified when at least one subfolder contains 2+ files. Applies to the same folders as `module-index-exports`: atoms, components, hooks, utils, enums, types, interfaces, reducers, layouts, views, pages, and more.
|
|
219
|
-
|
|
220
|
-
**Why use it:** Mixing flat files and wrapped folders in the same directory creates inconsistency. This rule ensures a uniform structure — if one item needs a folder (because it has multiple files), all items should be wrapped; if none do, keep everything flat.
|
|
221
|
-
|
|
222
|
-
**Configurable options:**
|
|
223
|
-
| Option | Default | Description |
|
|
224
|
-
|--------|---------|-------------|
|
|
225
|
-
| `moduleFolders` | Same as `module-index-exports` (atoms, components, hooks, utils, enums, types, views, layouts, pages, etc.) | Replace the entire folder list |
|
|
226
|
-
| `extraModuleFolders` | `[]` | Add extra folders on top of the defaults |
|
|
227
|
-
|
|
228
|
-
```
|
|
229
|
-
// Good — flat mode (all direct files)
|
|
230
|
-
atoms/input.tsx
|
|
231
|
-
atoms/calendar.tsx
|
|
232
|
-
|
|
233
|
-
// Good — wrapped mode (justified — input has multiple files)
|
|
234
|
-
atoms/input/index.tsx
|
|
235
|
-
atoms/input/helpers.ts
|
|
236
|
-
atoms/calendar/index.tsx
|
|
237
|
-
|
|
238
|
-
// Bad — mixed (some flat, some wrapped with justification)
|
|
239
|
-
atoms/input.tsx -> "all items should be wrapped in folders"
|
|
240
|
-
atoms/calendar/index.tsx
|
|
241
|
-
atoms/calendar/helpers.ts
|
|
242
|
-
|
|
243
|
-
// Bad — wrapped but unnecessary (each folder has only 1 file)
|
|
244
|
-
atoms/input/index.tsx -> "use direct files instead"
|
|
245
|
-
atoms/calendar/index.tsx
|
|
246
|
-
|
|
247
|
-
// Bad — mixed without justification
|
|
248
|
-
atoms/input.tsx
|
|
249
|
-
atoms/calendar/index.tsx -> "use direct files instead"
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
> **Note:** This rule applies equally to all module folders — component folders (atoms, components, views), data folders (enums, types, interfaces), and utility folders (hooks, utils, helpers). The `folder-based-naming-convention` naming rule is separate and enforces export naming based on folder location.
|
|
253
|
-
|
|
254
|
-
---
|
|
255
|
-
|
|
256
|
-
### `no-redundant-folder-suffix`
|
|
257
|
-
|
|
258
|
-
**What it does:** Flags files and folders whose name redundantly includes the parent (or ancestor) folder name as a suffix. Since the folder already provides context, the name doesn't need to repeat it.
|
|
259
|
-
|
|
260
|
-
**Why use it:** This complements `folder-based-naming-convention` — the *file name* should stay clean while the *exported component name* carries the suffix. For example, `layouts/main.tsx` exporting `MainLayout` is better than `layouts/main-layout.tsx` exporting `MainLayout`.
|
|
261
|
-
|
|
262
|
-
```
|
|
263
|
-
// Good — names don't repeat the folder name
|
|
264
|
-
layouts/main.tsx -> export const MainLayout = ...
|
|
265
|
-
atoms/button.tsx -> file "button" has no redundant suffix
|
|
266
|
-
views/dashboard.tsx -> file "dashboard" has no redundant suffix
|
|
267
|
-
views/access-control/... -> folder "access-control" has no redundant suffix
|
|
268
|
-
atoms/input/index.tsx -> uses "index" inside folder (correct)
|
|
269
|
-
|
|
270
|
-
// Bad — file name matches parent folder name (use index instead)
|
|
271
|
-
atoms/input/input.tsx -> use "input/index.tsx" instead
|
|
272
|
-
components/card/card.tsx -> use "card/index.tsx" instead
|
|
273
|
-
|
|
274
|
-
// Bad — file names redundantly include the folder suffix
|
|
275
|
-
layouts/main-layout.tsx -> redundant "-layout" (already in layouts/)
|
|
276
|
-
atoms/button-atom.tsx -> redundant "-atom" (already in atoms/)
|
|
277
|
-
views/dashboard-view.tsx -> redundant "-view" (already in views/)
|
|
278
|
-
|
|
279
|
-
// Bad — folder names redundantly include the ancestor suffix
|
|
280
|
-
views/access-control-view/ -> redundant "-view" (already in views/)
|
|
281
|
-
|
|
282
|
-
// Nested names are also checked against ancestor folders
|
|
283
|
-
atoms/forms/input-atom.tsx -> redundant "-atom" from ancestor "atoms/"
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
> **Note:** Index files (`index.ts`, `index.js`, etc.) are skipped for file name checks. Folder names are singularized automatically (e.g., `layouts` -> `layout`, `categories` -> `category`, `classes` -> `class`).
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
### `svg-icon-naming-convention`
|
|
291
|
-
|
|
292
|
-
**What it does:** Enforces naming conventions for SVG icon components:
|
|
293
|
-
- Components that return only an SVG element must have a name ending with "Icon"
|
|
294
|
-
- Components with "Icon" suffix must return an SVG element
|
|
295
|
-
|
|
296
|
-
**Why use it:** Consistent naming makes it immediately clear which components render icons, improving code readability and making icon components easier to find in large codebases.
|
|
297
|
-
|
|
298
|
-
```tsx
|
|
299
|
-
// Good — returns SVG and ends with "Icon"
|
|
300
|
-
export const SuccessIcon = ({ className = "" }: { className?: string }) => (
|
|
301
|
-
<svg className={className}>
|
|
302
|
-
<path d="M9 12l2 2 4-4" />
|
|
303
|
-
</svg>
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
// Good — returns non-SVG and doesn't end with "Icon"
|
|
307
|
-
export const Button = ({ children }: { children: React.ReactNode }) => (
|
|
308
|
-
<button>{children}</button>
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
// Bad — returns SVG but doesn't end with "Icon"
|
|
312
|
-
export const Success = ({ className = "" }: { className?: string }) => (
|
|
313
|
-
<svg className={className}>
|
|
314
|
-
<path d="M9 12l2 2 4-4" />
|
|
315
|
-
</svg>
|
|
316
|
-
);
|
|
317
|
-
// Error: Component "Success" returns an SVG element and should end with "Icon" suffix
|
|
318
|
-
|
|
319
|
-
// Bad — ends with "Icon" but doesn't return SVG
|
|
320
|
-
export const ButtonIcon = ({ children }: { children: React.ReactNode }) => (
|
|
321
|
-
<button>{children}</button>
|
|
322
|
-
);
|
|
323
|
-
// Error: Component "ButtonIcon" has "Icon" suffix but doesn't return an SVG element
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
<br />
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
[<- Back to Rules Index](./README.md) | [<- Back to Main README](../../README.md)
|