js-style-kit 0.6.1 → 0.7.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/README.md +5 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +48 -7
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
- package/src/eslint/base/README.md +186 -0
- package/src/eslint/base/config.ts +37 -0
- package/src/eslint/base/rules.ts +444 -0
- package/src/eslint/base/types.ts +20 -0
- package/src/eslint/constants.ts +52 -0
- package/src/eslint/convex/README.md +30 -0
- package/src/eslint/convex/config.ts +34 -0
- package/src/eslint/convex/rules.ts +8 -0
- package/src/eslint/convex/types.ts +8 -0
- package/src/eslint/ignores.ts +31 -0
- package/src/eslint/import/README.md +397 -0
- package/src/eslint/import/config.ts +48 -0
- package/src/eslint/import/rules.ts +81 -0
- package/src/eslint/index.ts +259 -0
- package/src/eslint/jsdoc/README.md +399 -0
- package/src/eslint/jsdoc/config.ts +29 -0
- package/src/eslint/jsdoc/rules.ts +81 -0
- package/src/eslint/jsdoc/types.ts +56 -0
- package/src/eslint/nextjs/config.ts +25 -0
- package/src/eslint/nextjs/rules.ts +25 -0
- package/src/eslint/nextjs/types.ts +27 -0
- package/src/eslint/perfectionist/README.md +454 -0
- package/src/eslint/perfectionist/config.ts +25 -0
- package/src/eslint/perfectionist/rules.ts +39 -0
- package/src/eslint/prefer-arrow-function/config.ts +33 -0
- package/src/eslint/prefer-arrow-function/types.ts +13 -0
- package/src/eslint/process-custom-rules.ts +72 -0
- package/src/eslint/query/README.md +254 -0
- package/src/eslint/query/config.ts +27 -0
- package/src/eslint/query/rules.ts +11 -0
- package/src/eslint/query/types.ts +11 -0
- package/src/eslint/react/README.md +416 -0
- package/src/eslint/react/config.ts +65 -0
- package/src/eslint/react/rules.ts +188 -0
- package/src/eslint/react/types.ts +26 -0
- package/src/eslint/react-refresh/config.ts +28 -0
- package/src/eslint/react-refresh/rules.ts +48 -0
- package/src/eslint/storybook/README.md +424 -0
- package/src/eslint/storybook/config.ts +57 -0
- package/src/eslint/testing/README.md +436 -0
- package/src/eslint/testing/config.ts +90 -0
- package/src/eslint/testing/jest-rules.ts +47 -0
- package/src/eslint/testing/vitest-rules.ts +42 -0
- package/src/eslint/turbo/README.md +380 -0
- package/src/eslint/turbo/config.ts +26 -0
- package/src/eslint/turbo/types.ts +7 -0
- package/src/eslint/types.ts +29 -0
- package/src/eslint/typescript/README.md +229 -0
- package/src/eslint/typescript/config.ts +48 -0
- package/src/eslint/typescript/rules.ts +137 -0
- package/src/eslint/typescript/types.ts +35 -0
- package/src/eslint/unicorn/README.md +497 -0
- package/src/eslint/unicorn/config.ts +36 -0
- package/src/eslint/unicorn/rules.ts +86 -0
- package/src/index.ts +3 -0
- package/src/modules.d.ts +5 -0
- package/src/prettier/README.md +413 -0
- package/src/prettier/index.ts +110 -0
- package/src/utils/is-type.ts +60 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# React Configuration
|
|
2
|
+
|
|
3
|
+
Comprehensive React support with ESLint rules for React, hooks, compiler, and framework-specific configurations for Next.js, Vite, Remix, and React Router.
|
|
4
|
+
|
|
5
|
+
[← Back to main README](../../../README.md)
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
React support is **disabled by default** and provides:
|
|
10
|
+
|
|
11
|
+
- React and JSX best practices
|
|
12
|
+
- React Hooks validation
|
|
13
|
+
- React Compiler support (enabled by default)
|
|
14
|
+
- Framework-specific configurations
|
|
15
|
+
- TypeScript integration
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import { eslintConfig } from "js-style-kit";
|
|
21
|
+
|
|
22
|
+
export default eslintConfig({
|
|
23
|
+
react: true, // Enable with defaults
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Configuration Options
|
|
28
|
+
|
|
29
|
+
### Basic Enable
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
react: true; // React + hooks + compiler rules
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Framework-Specific
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
react: {
|
|
39
|
+
framework: "next", // "next" | "vite" | "remix" | "react-router" | "none"
|
|
40
|
+
reactCompiler: true, // React 19 compiler (default: true)
|
|
41
|
+
reactRefresh: false, // Fast Refresh validation (framework-dependent)
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Framework Configurations
|
|
46
|
+
|
|
47
|
+
### Next.js
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
export default eslintConfig({
|
|
51
|
+
react: { framework: "next" },
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Includes:**
|
|
56
|
+
|
|
57
|
+
- ✅ Next.js-specific ESLint rules
|
|
58
|
+
- ✅ Ignores `.next` directory
|
|
59
|
+
- ✅ React Refresh disabled (Next.js handles it)
|
|
60
|
+
|
|
61
|
+
### Vite
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
export default eslintConfig({
|
|
65
|
+
react: { framework: "vite" },
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Includes:**
|
|
70
|
+
|
|
71
|
+
- ✅ React Refresh validation enabled
|
|
72
|
+
- ✅ No Next.js-specific rules
|
|
73
|
+
|
|
74
|
+
### Remix
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
export default eslintConfig({
|
|
78
|
+
react: { framework: "remix" },
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Includes:**
|
|
83
|
+
|
|
84
|
+
- ✅ React Refresh disabled (Remix handles it)
|
|
85
|
+
- ✅ No Next.js-specific rules
|
|
86
|
+
|
|
87
|
+
### React Router
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
export default eslintConfig({
|
|
91
|
+
react: { framework: "react-router" },
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Includes:**
|
|
96
|
+
|
|
97
|
+
- ✅ React Refresh disabled (Router handles it)
|
|
98
|
+
- ✅ No Next.js-specific rules
|
|
99
|
+
|
|
100
|
+
### Override Defaults
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
react: {
|
|
104
|
+
framework: "next",
|
|
105
|
+
reactRefresh: true, // Force enable even with Next.js
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## React Compiler
|
|
110
|
+
|
|
111
|
+
[React Compiler](https://react.dev/learn/react-compiler) optimizes React components automatically. `eslint-plugin-react-hooks` provides several rules to ensure your code is compatible with the compiler. They are new, and they may conflict with some rules from `eslint-plugin-react`. If it does, open an issue to let me know!
|
|
112
|
+
|
|
113
|
+
**Enabled by default** when React is enabled.
|
|
114
|
+
|
|
115
|
+
### What It Checks
|
|
116
|
+
|
|
117
|
+
- Proper Hook usage
|
|
118
|
+
- Component purity requirements
|
|
119
|
+
- Dependencies tracking
|
|
120
|
+
|
|
121
|
+
### Disable Compiler Rules
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
export default eslintConfig({
|
|
125
|
+
react: {
|
|
126
|
+
framework: "none", // could be anything
|
|
127
|
+
reactCompiler: false,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## React Fast Refresh
|
|
133
|
+
|
|
134
|
+
[React Fast Refresh](https://www.npmjs.com/package/react-refresh) provides instant component updates without losing state. The validation ensures your components work with Fast Refresh.
|
|
135
|
+
|
|
136
|
+
### When to Enable
|
|
137
|
+
|
|
138
|
+
- ✅ **Enable** for Vite SPAs
|
|
139
|
+
- ❌ **Disable** for Next.js (handles its own)
|
|
140
|
+
- ❌ **Disable** for Remix (handles its own)
|
|
141
|
+
- ❌ **Disable** for React Router (handles its own)
|
|
142
|
+
|
|
143
|
+
### Manual Control
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
react: {
|
|
147
|
+
framework: "vite",
|
|
148
|
+
reactRefresh: true, // Explicitly enable
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### What It Validates
|
|
153
|
+
|
|
154
|
+
- ✅ Components properly exported
|
|
155
|
+
- ✅ No anonymous components
|
|
156
|
+
- ✅ No nested component definitions
|
|
157
|
+
- ✅ Export patterns compatible with Fast Refresh
|
|
158
|
+
- ✅ Allows constant exports with components (Vite-compatible)
|
|
159
|
+
|
|
160
|
+
## Function Styles for Components
|
|
161
|
+
|
|
162
|
+
Control how React components are written:
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
export default eslintConfig({
|
|
166
|
+
react: true,
|
|
167
|
+
functionStyle: "arrow", // Default
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Arrow Functions (default)
|
|
172
|
+
|
|
173
|
+
```jsx
|
|
174
|
+
// ✅ Good
|
|
175
|
+
const MyComponent = ({ name }) => {
|
|
176
|
+
return <div>Hello {name}</div>;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export default () => <div>App</div>;
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Function Declarations
|
|
183
|
+
|
|
184
|
+
```jsx
|
|
185
|
+
// ✅ Good
|
|
186
|
+
function MyComponent({ name }) {
|
|
187
|
+
return <div>Hello {name}</div>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export default function App() {
|
|
191
|
+
return <div>App</div>;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Function Expressions
|
|
196
|
+
|
|
197
|
+
```jsx
|
|
198
|
+
// ✅ Good
|
|
199
|
+
const MyComponent = function ({ name }) {
|
|
200
|
+
return <div>Hello {name}</div>;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export default function () {
|
|
204
|
+
return <div>App</div>;
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Disabled (Any Style)
|
|
209
|
+
|
|
210
|
+
```jsx
|
|
211
|
+
functionStyle: "off"; // Use any style you prefer
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Key Rules
|
|
215
|
+
|
|
216
|
+
### React Hooks
|
|
217
|
+
|
|
218
|
+
- **`react-hooks/rules-of-hooks`** - Enforces Rules of Hooks
|
|
219
|
+
- **`react-hooks/exhaustive-deps`** - Validates dependency arrays
|
|
220
|
+
|
|
221
|
+
```jsx
|
|
222
|
+
// ✅ Good - hooks at top level
|
|
223
|
+
const MyComponent = () => {
|
|
224
|
+
const [count, setCount] = useState(0);
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
/* ... */
|
|
227
|
+
}, [count]); // All deps listed
|
|
228
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// ❌ Bad - hooks in conditional
|
|
232
|
+
const MyComponent = () => {
|
|
233
|
+
if (condition) {
|
|
234
|
+
const [count, setCount] = useState(0); // ❌ Conditional hook
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### JSX Best Practices
|
|
240
|
+
|
|
241
|
+
- **`react/jsx-boolean-value`** - Enforce boolean prop syntax
|
|
242
|
+
- **`react/jsx-curly-brace-presence`** - Avoid unnecessary JSX braces
|
|
243
|
+
- **`react/jsx-fragments`** - Prefer `<>` over `<React.Fragment>`
|
|
244
|
+
- **`react/jsx-key`** - Require `key` prop in lists
|
|
245
|
+
- **`react/jsx-no-target-blank`** - Prevent unsafe `target="_blank"`
|
|
246
|
+
- **`react/self-closing-comp`** - Require self-closing tags when appropriate
|
|
247
|
+
|
|
248
|
+
```jsx
|
|
249
|
+
// ✅ Good
|
|
250
|
+
<>
|
|
251
|
+
<MyComponent enabled />
|
|
252
|
+
{items.map(item => <Item key={item.id} {...item} />)}
|
|
253
|
+
<Link href="/docs" target="_blank" rel="noopener noreferrer">Docs</Link>
|
|
254
|
+
<EmptyComponent />
|
|
255
|
+
</>
|
|
256
|
+
|
|
257
|
+
// ❌ Bad
|
|
258
|
+
<React.Fragment>
|
|
259
|
+
<MyComponent enabled={true} />
|
|
260
|
+
{items.map(item => <Item {...item} />)} {/* Missing key */}
|
|
261
|
+
<Link href="/docs" target="_blank">Docs</Link> {/* Unsafe */}
|
|
262
|
+
<EmptyComponent></EmptyComponent>
|
|
263
|
+
</React.Fragment>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Component Best Practices
|
|
267
|
+
|
|
268
|
+
- **`react/no-array-index-key`** - Avoid array index as key
|
|
269
|
+
- **`react/no-children-prop`** - Use children as JSX, not prop
|
|
270
|
+
- **`react/no-danger`** - Warn on `dangerouslySetInnerHTML`
|
|
271
|
+
- **`react/no-unstable-nested-components`** - No component definitions in render
|
|
272
|
+
- **`react/void-dom-elements-no-children`** - No children on void elements
|
|
273
|
+
|
|
274
|
+
```jsx
|
|
275
|
+
// ✅ Good
|
|
276
|
+
const Parent = () => {
|
|
277
|
+
return items.map((item) => <Item key={item.id}>{item.name}</Item>);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// ❌ Bad
|
|
281
|
+
const Parent = () => {
|
|
282
|
+
// ❌ Component defined inside render
|
|
283
|
+
const Child = () => <div>Bad</div>;
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<>
|
|
287
|
+
{items.map((item, i) => (
|
|
288
|
+
<Item key={i}>{item.name}</Item>
|
|
289
|
+
))}{" "}
|
|
290
|
+
{/* ❌ Index as key */}
|
|
291
|
+
<img>child content</img> {/* ❌ Void element with children */}
|
|
292
|
+
</>
|
|
293
|
+
);
|
|
294
|
+
};
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### State Management
|
|
298
|
+
|
|
299
|
+
- **`react/no-direct-mutation-state`** - No direct state mutation
|
|
300
|
+
- **`react/no-unused-state`** - Detect unused state
|
|
301
|
+
- **`react/prefer-stateless-function`** - Prefer functions over classes when possible
|
|
302
|
+
|
|
303
|
+
### TypeScript Integration
|
|
304
|
+
|
|
305
|
+
When both React and TypeScript are enabled:
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
export default eslintConfig({
|
|
309
|
+
react: true,
|
|
310
|
+
typescript: true, // Enabled by default
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Additional rules:**
|
|
315
|
+
|
|
316
|
+
- `react/prop-types` disabled (TypeScript handles it)
|
|
317
|
+
- Type-safe prop definitions
|
|
318
|
+
- JSX type checking
|
|
319
|
+
|
|
320
|
+
[→ See TypeScript Configuration](../typescript/README.md)
|
|
321
|
+
|
|
322
|
+
## Next.js-Specific Rules
|
|
323
|
+
|
|
324
|
+
When `framework: "next"` is set, additional Next.js rules are included:
|
|
325
|
+
|
|
326
|
+
- Google Font optimization
|
|
327
|
+
- No `<img>` element (use `next/image`)
|
|
328
|
+
- No `<head>` element (use `next/head`)
|
|
329
|
+
- No HTML link for pages
|
|
330
|
+
- Script optimization
|
|
331
|
+
- And more...
|
|
332
|
+
|
|
333
|
+
[→ Learn more about Next.js plugin](../nextjs/README.md)
|
|
334
|
+
|
|
335
|
+
## Common Patterns
|
|
336
|
+
|
|
337
|
+
### Next.js + TypeScript
|
|
338
|
+
|
|
339
|
+
```js
|
|
340
|
+
export default eslintConfig({
|
|
341
|
+
typescript: "./tsconfig.json",
|
|
342
|
+
react: {
|
|
343
|
+
framework: "next",
|
|
344
|
+
reactCompiler: true,
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Vite + TypeScript
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
export default eslintConfig({
|
|
353
|
+
typescript: true,
|
|
354
|
+
react: {
|
|
355
|
+
framework: "vite",
|
|
356
|
+
reactRefresh: true,
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Library Development
|
|
362
|
+
|
|
363
|
+
```js
|
|
364
|
+
export default eslintConfig({
|
|
365
|
+
typescript: true,
|
|
366
|
+
react: true,
|
|
367
|
+
jsdoc: { requireJsdoc: true },
|
|
368
|
+
functionStyle: "arrow",
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Customization
|
|
373
|
+
|
|
374
|
+
Override specific React rules:
|
|
375
|
+
|
|
376
|
+
```js
|
|
377
|
+
export default eslintConfig({
|
|
378
|
+
react: true,
|
|
379
|
+
rules: {
|
|
380
|
+
// Allow array index as key in some cases
|
|
381
|
+
"react/no-array-index-key": "off",
|
|
382
|
+
|
|
383
|
+
// Disable compiler rules
|
|
384
|
+
"react-hooks/react-compiler": "off",
|
|
385
|
+
|
|
386
|
+
// Stricter hook dependencies
|
|
387
|
+
"react-hooks/exhaustive-deps": "error",
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Troubleshooting
|
|
393
|
+
|
|
394
|
+
### React version detection fails
|
|
395
|
+
|
|
396
|
+
Ensure React is installed in your project. The config auto-detects the version.
|
|
397
|
+
|
|
398
|
+
### Fast Refresh not working
|
|
399
|
+
|
|
400
|
+
1. Check that `reactRefresh: true` is set for Vite
|
|
401
|
+
2. Ensure components follow Fast Refresh patterns
|
|
402
|
+
3. Review warnings from `eslint-plugin-react-refresh`
|
|
403
|
+
|
|
404
|
+
## Related Configurations
|
|
405
|
+
|
|
406
|
+
- [TypeScript](../typescript/README.md) - TypeScript configuration
|
|
407
|
+
- [Next.js Plugin](../nextjs/README.md) - Next.js-specific rules
|
|
408
|
+
- [Testing](../testing/README.md) - Test React components
|
|
409
|
+
|
|
410
|
+
## Learn More
|
|
411
|
+
|
|
412
|
+
- [React ESLint Plugin](https://github.com/jsx-eslint/eslint-plugin-react)
|
|
413
|
+
- [React Hooks ESLint Plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks)
|
|
414
|
+
- [React Refresh Plugin](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
|
|
415
|
+
- [React Compiler](https://react.dev/learn/react-compiler)
|
|
416
|
+
- [Main README](../../../README.md)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import react from "eslint-plugin-react";
|
|
2
|
+
import pluginReactHooks from "eslint-plugin-react-hooks";
|
|
3
|
+
import globals from "globals";
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
EslintConfigObject,
|
|
7
|
+
EslintRuleConfig,
|
|
8
|
+
FunctionStyle,
|
|
9
|
+
} from "../types.js";
|
|
10
|
+
|
|
11
|
+
import { configNames } from "../constants.js";
|
|
12
|
+
import { reactRules } from "./rules.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generates ESLint configuration for React.
|
|
16
|
+
*
|
|
17
|
+
* @param options - Configuration options
|
|
18
|
+
* @param options.functionStyle - Controls how functions (components) should be written. Can be:
|
|
19
|
+
* - "off": Disables function style enforcement
|
|
20
|
+
* - "arrow": Enforces arrow function expressions
|
|
21
|
+
* - "declaration": Enforces function declarations
|
|
22
|
+
* - "expression": Enforces function expressions
|
|
23
|
+
* @param options.reactCompiler - Whether to use the React compiler rules from `eslint-plugin-react-hooks`
|
|
24
|
+
* @param options.typescript - Whether TypeScript is being used in the project. When true, some rules are adjusted to be more TypeScript-friendly
|
|
25
|
+
* @param options.customRules - Optional object containing custom rules to override or add to the React configuration
|
|
26
|
+
* @returns An ESLint configuration object for React.
|
|
27
|
+
*/
|
|
28
|
+
export const reactEslintConfig = ({
|
|
29
|
+
customRules,
|
|
30
|
+
functionStyle,
|
|
31
|
+
reactCompiler,
|
|
32
|
+
typescript,
|
|
33
|
+
}: {
|
|
34
|
+
customRules?: Record<string, EslintRuleConfig>;
|
|
35
|
+
functionStyle: "off" | FunctionStyle;
|
|
36
|
+
reactCompiler: boolean;
|
|
37
|
+
typescript: boolean;
|
|
38
|
+
}): EslintConfigObject => {
|
|
39
|
+
return {
|
|
40
|
+
languageOptions: {
|
|
41
|
+
globals: {
|
|
42
|
+
...globals.browser,
|
|
43
|
+
},
|
|
44
|
+
parserOptions: {
|
|
45
|
+
ecmaFeatures: {
|
|
46
|
+
jsx: true,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
name: configNames.react,
|
|
51
|
+
plugins: {
|
|
52
|
+
react,
|
|
53
|
+
"react-hooks": pluginReactHooks,
|
|
54
|
+
},
|
|
55
|
+
rules: {
|
|
56
|
+
...reactRules({ functionStyle, reactCompiler, typescript }),
|
|
57
|
+
...(customRules ?? {}),
|
|
58
|
+
},
|
|
59
|
+
settings: {
|
|
60
|
+
react: {
|
|
61
|
+
version: "detect",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type { FunctionStyle } from "../types.js";
|
|
2
|
+
import type { ReactFunctionDefinitions, ReactRules } from "./types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates ESLint rules configuration for React and React Hooks.
|
|
6
|
+
* Includes settings for function component style enforcement and TypeScript-specific rules.
|
|
7
|
+
*
|
|
8
|
+
* @param options - Configuration options
|
|
9
|
+
* @param options.functionStyle - The preferred style for React function components: 'arrow' for arrow functions, 'declaration' for function declarations, 'expression' for function expressions, or 'off' to disable style enforcement
|
|
10
|
+
* @param options.reactCompiler - Whether to use the React compiler rules from `eslint-plugin-react-hooks`
|
|
11
|
+
* @param options.typescript - Whether TypeScript-specific React rules should be enabled
|
|
12
|
+
* @returns Configuration object containing ESLint rules for React and React Hooks
|
|
13
|
+
*/
|
|
14
|
+
export const reactRules = ({
|
|
15
|
+
functionStyle,
|
|
16
|
+
reactCompiler,
|
|
17
|
+
typescript,
|
|
18
|
+
}: {
|
|
19
|
+
functionStyle: "off" | FunctionStyle;
|
|
20
|
+
reactCompiler: boolean;
|
|
21
|
+
typescript: boolean;
|
|
22
|
+
}): ReactRules => {
|
|
23
|
+
const functionStyleMap: Record<FunctionStyle, ReactFunctionDefinitions> = {
|
|
24
|
+
arrow: "arrow-function",
|
|
25
|
+
declaration: "function-declaration",
|
|
26
|
+
expression: "function-expression",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// TODO: split out new rules into a `react-compiler` option
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
/**
|
|
33
|
+
* Disabled in favor of TypeScript for type checking
|
|
34
|
+
*/
|
|
35
|
+
...(typescript ? {} : { "react/prop-types": "warn" }),
|
|
36
|
+
/**
|
|
37
|
+
* Core React Hooks rules
|
|
38
|
+
*/
|
|
39
|
+
"react-hooks/exhaustive-deps": "warn",
|
|
40
|
+
"react-hooks/rules-of-hooks": "warn",
|
|
41
|
+
/**
|
|
42
|
+
* React compiler rules
|
|
43
|
+
*/
|
|
44
|
+
...(reactCompiler ?
|
|
45
|
+
{
|
|
46
|
+
/**
|
|
47
|
+
* Does not seem to be working, and overlaps with static-components
|
|
48
|
+
*/
|
|
49
|
+
// "react-hooks/component-hook-factories": "warn",
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* seems unecessary unless you're rolling your own React setup, users can always enable
|
|
53
|
+
*/
|
|
54
|
+
// "react-hooks/config": "warn",
|
|
55
|
+
|
|
56
|
+
"react-hooks/error-boundaries": "warn",
|
|
57
|
+
"react-hooks/globals": "warn",
|
|
58
|
+
"react-hooks/immutability": "warn",
|
|
59
|
+
"react-hooks/incompatible-library": "warn",
|
|
60
|
+
"react-hooks/preserve-manual-memoization": "warn",
|
|
61
|
+
"react-hooks/purity": "warn",
|
|
62
|
+
"react-hooks/refs": "warn",
|
|
63
|
+
"react-hooks/set-state-in-effect": "warn",
|
|
64
|
+
"react-hooks/set-state-in-render": "warn",
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* overlaps with react/no-unstable-nested-components
|
|
68
|
+
*/
|
|
69
|
+
// "react-hooks/static-components": "warn",
|
|
70
|
+
|
|
71
|
+
"react-hooks/unsupported-syntax": "warn",
|
|
72
|
+
"react-hooks/use-memo": "warn",
|
|
73
|
+
}
|
|
74
|
+
: {}),
|
|
75
|
+
/**
|
|
76
|
+
* Require an explicit type when using button elements.
|
|
77
|
+
*
|
|
78
|
+
* 🚫 Not fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/button-has-type.md
|
|
79
|
+
*/
|
|
80
|
+
"react/button-has-type": "warn",
|
|
81
|
+
"react/destructuring-assignment": ["warn", "always"],
|
|
82
|
+
"react/display-name": "warn",
|
|
83
|
+
/**
|
|
84
|
+
* Require consistent function type for function components.
|
|
85
|
+
*
|
|
86
|
+
* 🔧 Fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/HEAD/docs/rules/function-component-definition.md
|
|
87
|
+
*/
|
|
88
|
+
"react/function-component-definition":
|
|
89
|
+
functionStyle === "off" ? "off" : (
|
|
90
|
+
[
|
|
91
|
+
"warn",
|
|
92
|
+
{
|
|
93
|
+
namedComponents: functionStyleMap[functionStyle],
|
|
94
|
+
unnamedComponents:
|
|
95
|
+
functionStyle === "arrow" ? "arrow-function" : (
|
|
96
|
+
"function-expression"
|
|
97
|
+
),
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
),
|
|
101
|
+
/**
|
|
102
|
+
* Require consistent boolean attributes notation in JSX.
|
|
103
|
+
*
|
|
104
|
+
* 🔧 Fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
|
|
105
|
+
*/
|
|
106
|
+
"react/jsx-boolean-value": "warn",
|
|
107
|
+
/**
|
|
108
|
+
* Disallow unnecessary curly braces in JSX props and children.
|
|
109
|
+
*
|
|
110
|
+
* 🔧 Fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-curly-brace-presence.md
|
|
111
|
+
*/
|
|
112
|
+
"react/jsx-curly-brace-presence": "warn",
|
|
113
|
+
/**
|
|
114
|
+
* Require using shorthand form for React fragments, unless required.
|
|
115
|
+
*
|
|
116
|
+
* 🔧 Fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-fragments.md
|
|
117
|
+
*/
|
|
118
|
+
"react/jsx-fragments": "warn",
|
|
119
|
+
"react/jsx-key": "warn",
|
|
120
|
+
"react/jsx-no-comment-textnodes": "warn",
|
|
121
|
+
"react/jsx-no-duplicate-props": "warn",
|
|
122
|
+
/**
|
|
123
|
+
* Prevent problematic leaked values from being rendered.
|
|
124
|
+
*
|
|
125
|
+
* 🔧 Fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-leaked-render.md
|
|
126
|
+
*/
|
|
127
|
+
"react/jsx-no-leaked-render": "warn",
|
|
128
|
+
/**
|
|
129
|
+
* Prevents usage of unsafe `target='_blank'`.
|
|
130
|
+
*
|
|
131
|
+
* This rule is a part of `react/recommended`, but we've modified it to
|
|
132
|
+
* allow referrer.
|
|
133
|
+
*
|
|
134
|
+
* 🔧 Fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md
|
|
135
|
+
*/
|
|
136
|
+
"react/jsx-no-target-blank": [
|
|
137
|
+
"warn",
|
|
138
|
+
{
|
|
139
|
+
allowReferrer: true,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
"react/jsx-no-undef": "warn",
|
|
143
|
+
/**
|
|
144
|
+
* Disallow empty React fragments.
|
|
145
|
+
*
|
|
146
|
+
* 🔧 Fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-useless-fragment.md
|
|
147
|
+
*/
|
|
148
|
+
"react/jsx-no-useless-fragment": ["warn", { allowExpressions: true }],
|
|
149
|
+
/**
|
|
150
|
+
* Require the use of PascalCase for user-defined JSX components.
|
|
151
|
+
*
|
|
152
|
+
* 🚫 Not fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md
|
|
153
|
+
*/
|
|
154
|
+
"react/jsx-pascal-case": "warn",
|
|
155
|
+
"react/jsx-uses-react": "warn",
|
|
156
|
+
"react/jsx-uses-vars": "warn",
|
|
157
|
+
/**
|
|
158
|
+
* Disallow usage of Array index in keys.
|
|
159
|
+
*
|
|
160
|
+
* � Not fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md
|
|
161
|
+
*/
|
|
162
|
+
"react/no-array-index-key": "warn",
|
|
163
|
+
"react/no-children-prop": "warn",
|
|
164
|
+
"react/no-danger-with-children": "warn",
|
|
165
|
+
"react/no-deprecated": "warn",
|
|
166
|
+
"react/no-direct-mutation-state": "warn",
|
|
167
|
+
"react/no-find-dom-node": "warn",
|
|
168
|
+
"react/no-is-mounted": "warn",
|
|
169
|
+
"react/no-render-return-value": "warn",
|
|
170
|
+
"react/no-string-refs": "warn",
|
|
171
|
+
"react/no-unescaped-entities": "warn",
|
|
172
|
+
"react/no-unknown-property": "warn",
|
|
173
|
+
"react/no-unsafe": "warn",
|
|
174
|
+
/**
|
|
175
|
+
* Disallow creating unstable components inside components.
|
|
176
|
+
*
|
|
177
|
+
* 🚫 Not fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unstable-nested-components.md
|
|
178
|
+
*/
|
|
179
|
+
"react/no-unstable-nested-components": "warn",
|
|
180
|
+
"react/require-render-return": "warn",
|
|
181
|
+
/**
|
|
182
|
+
* Disallow closing tags for components without children.
|
|
183
|
+
*
|
|
184
|
+
* 🔧 Fixable - https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
|
|
185
|
+
*/
|
|
186
|
+
"react/self-closing-comp": "warn",
|
|
187
|
+
};
|
|
188
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EslintRuleConfig } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export type ReactFunctionDefinitions =
|
|
4
|
+
| "arrow-function"
|
|
5
|
+
| "function-declaration"
|
|
6
|
+
| "function-expression";
|
|
7
|
+
|
|
8
|
+
export type ReactRules = Record<
|
|
9
|
+
`${"react" | "react-hooks"}/${string}`,
|
|
10
|
+
EslintRuleConfig
|
|
11
|
+
> & {
|
|
12
|
+
"react/destructuring-assignment"?: EslintRuleConfig<
|
|
13
|
+
"always" | "never",
|
|
14
|
+
{
|
|
15
|
+
destructureInSignature?: "always" | "ignore";
|
|
16
|
+
ignoreClassFields?: boolean;
|
|
17
|
+
}
|
|
18
|
+
>;
|
|
19
|
+
"react/function-component-definition"?: EslintRuleConfig<{
|
|
20
|
+
namedComponents?: ReactFunctionDefinitions | ReactFunctionDefinitions[];
|
|
21
|
+
unnamedComponents?:
|
|
22
|
+
| "arrow-function"
|
|
23
|
+
| "function-expression"
|
|
24
|
+
| ("arrow-function" | "function-expression")[];
|
|
25
|
+
}>;
|
|
26
|
+
};
|