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,380 @@
|
|
|
1
|
+
# Turbo Configuration
|
|
2
|
+
|
|
3
|
+
ESLint rules for Turborepo monorepos to catch common configuration issues.
|
|
4
|
+
|
|
5
|
+
[← Back to main README](../../../README.md)
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Turbo configuration is **disabled by default** and provides:
|
|
10
|
+
|
|
11
|
+
- Environment variable validation
|
|
12
|
+
- Turborepo configuration best practices
|
|
13
|
+
- Monorepo-specific linting
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import { eslintConfig } from "js-style-kit";
|
|
19
|
+
|
|
20
|
+
export default eslintConfig({
|
|
21
|
+
turbo: true, // Enable Turborepo rules
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Key Features
|
|
26
|
+
|
|
27
|
+
### Environment Variable Declaration
|
|
28
|
+
|
|
29
|
+
Ensures all environment variables used in your code are properly declared in Turborepo configuration:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
// turbo.json
|
|
33
|
+
{
|
|
34
|
+
"tasks": {
|
|
35
|
+
"build": {
|
|
36
|
+
"env": ["NODE_ENV", "API_URL"]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// ✅ Good - declared in turbo.json
|
|
44
|
+
const apiUrl = process.env.API_URL;
|
|
45
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
46
|
+
|
|
47
|
+
// ❌ Bad - not declared in turbo.json
|
|
48
|
+
const secretKey = process.env.SECRET_KEY; // Warning!
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Rule:**
|
|
52
|
+
|
|
53
|
+
- `turbo/no-undeclared-env-vars` - Ensures env vars are declared in `turbo.json`
|
|
54
|
+
|
|
55
|
+
**Why this matters:**
|
|
56
|
+
|
|
57
|
+
- Turborepo needs to know about env vars for proper caching
|
|
58
|
+
- Undeclared env vars can cause cache invalidation issues
|
|
59
|
+
- Makes dependencies explicit and trackable
|
|
60
|
+
|
|
61
|
+
## How It Works
|
|
62
|
+
|
|
63
|
+
The rule checks your `turbo.json` configuration and validates that any environment variables accessed in your code are listed in the appropriate task's `env` array.
|
|
64
|
+
|
|
65
|
+
### Project-Level Configuration
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
// turbo.json
|
|
69
|
+
{
|
|
70
|
+
"tasks": {
|
|
71
|
+
"build": {
|
|
72
|
+
"env": [
|
|
73
|
+
"NODE_ENV",
|
|
74
|
+
"API_URL",
|
|
75
|
+
"NEXT_PUBLIC_*" // Wildcard pattern
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"dev": {
|
|
79
|
+
"cache": false,
|
|
80
|
+
"env": ["NODE_ENV"]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Global Environment Variables
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
// turbo.json
|
|
90
|
+
{
|
|
91
|
+
"globalEnv": ["CI", "VERCEL"],
|
|
92
|
+
"tasks": {
|
|
93
|
+
"build": {
|
|
94
|
+
"env": ["API_URL"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Examples
|
|
101
|
+
|
|
102
|
+
### Valid Usage
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// turbo.json has: { "tasks": { "build": { "env": ["API_URL", "DB_HOST"] } } }
|
|
106
|
+
|
|
107
|
+
// ✅ Good - all declared
|
|
108
|
+
export const config = {
|
|
109
|
+
apiUrl: process.env.API_URL,
|
|
110
|
+
dbHost: process.env.DB_HOST,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// ✅ Good - using wildcard pattern
|
|
114
|
+
// turbo.json has: { "env": ["NEXT_PUBLIC_*"] }
|
|
115
|
+
const publicKey = process.env.NEXT_PUBLIC_API_KEY;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Invalid Usage
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
// turbo.json has: { "tasks": { "build": { "env": ["API_URL"] } } }
|
|
122
|
+
|
|
123
|
+
// ❌ Bad - SECRET_KEY not declared
|
|
124
|
+
const config = {
|
|
125
|
+
apiUrl: process.env.API_URL,
|
|
126
|
+
secret: process.env.SECRET_KEY, // Warning!
|
|
127
|
+
};
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Fixing the Issue
|
|
131
|
+
|
|
132
|
+
Add missing env vars to `turbo.json`:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"tasks": {
|
|
137
|
+
"build": {
|
|
138
|
+
"env": ["API_URL", "SECRET_KEY"]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Wildcard Patterns
|
|
145
|
+
|
|
146
|
+
Turborepo supports wildcard patterns for environment variables:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"tasks": {
|
|
151
|
+
"build": {
|
|
152
|
+
"env": [
|
|
153
|
+
"NEXT_PUBLIC_*", // Matches NEXT_PUBLIC_API_URL, etc.
|
|
154
|
+
"VITE_*", // Matches VITE_API_URL, etc.
|
|
155
|
+
"REACT_APP_*" // Matches REACT_APP_VERSION, etc.
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
// ✅ All valid with wildcard patterns above
|
|
164
|
+
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
|
165
|
+
const appVersion = process.env.REACT_APP_VERSION;
|
|
166
|
+
const viteMode = process.env.VITE_MODE;
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Common Patterns
|
|
170
|
+
|
|
171
|
+
### Next.js Monorepo
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
// turbo.json
|
|
175
|
+
{
|
|
176
|
+
"globalEnv": ["CI", "NODE_ENV"],
|
|
177
|
+
"tasks": {
|
|
178
|
+
"build": {
|
|
179
|
+
"env": ["NEXT_PUBLIC_*", "DATABASE_URL", "API_SECRET"],
|
|
180
|
+
"outputs": [".next/**", "!.next/cache/**"]
|
|
181
|
+
},
|
|
182
|
+
"dev": {
|
|
183
|
+
"cache": false
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Multi-Framework Monorepo
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
// turbo.json
|
|
193
|
+
{
|
|
194
|
+
"tasks": {
|
|
195
|
+
"build": {
|
|
196
|
+
"env": [
|
|
197
|
+
"NODE_ENV",
|
|
198
|
+
"NEXT_PUBLIC_*", // Next.js apps
|
|
199
|
+
"VITE_*", // Vite apps
|
|
200
|
+
"REACT_APP_*" // CRA apps
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Backend Services
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
// turbo.json
|
|
211
|
+
{
|
|
212
|
+
"tasks": {
|
|
213
|
+
"build": {
|
|
214
|
+
"env": ["DATABASE_URL", "REDIS_URL", "AWS_*", "STRIPE_SECRET_KEY"]
|
|
215
|
+
},
|
|
216
|
+
"test": {
|
|
217
|
+
"env": ["DATABASE_URL", "TEST_DATABASE_URL"]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Customization
|
|
224
|
+
|
|
225
|
+
### Disable for Specific Files
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
export default eslintConfig({
|
|
229
|
+
turbo: true,
|
|
230
|
+
overrides: [
|
|
231
|
+
{
|
|
232
|
+
files: ["scripts/**/*.ts"],
|
|
233
|
+
rules: {
|
|
234
|
+
// Scripts might use env vars not in turbo.json
|
|
235
|
+
"turbo/no-undeclared-env-vars": "off",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Error Instead of Warning
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
export default eslintConfig({
|
|
246
|
+
turbo: true,
|
|
247
|
+
rules: {
|
|
248
|
+
// Make undeclared env vars an error
|
|
249
|
+
"turbo/no-undeclared-env-vars": "error",
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Disable Completely
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
export default eslintConfig({
|
|
258
|
+
turbo: true,
|
|
259
|
+
rules: {
|
|
260
|
+
"turbo/no-undeclared-env-vars": "off",
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Monorepo Structure
|
|
266
|
+
|
|
267
|
+
Typical monorepo using Turborepo:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
my-monorepo/
|
|
271
|
+
├── turbo.json # Turbo configuration
|
|
272
|
+
├── package.json
|
|
273
|
+
├── packages/
|
|
274
|
+
│ ├── web/ # Next.js app
|
|
275
|
+
│ │ └── src/
|
|
276
|
+
│ ├── api/ # API service
|
|
277
|
+
│ │ └── src/
|
|
278
|
+
│ └── shared/ # Shared utilities
|
|
279
|
+
│ └── src/
|
|
280
|
+
└── apps/
|
|
281
|
+
└── mobile/ # React Native app
|
|
282
|
+
└── src/
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Each package can use environment variables, but they must be declared in the root `turbo.json` or task-specific configuration.
|
|
286
|
+
|
|
287
|
+
## Environment Variables Best Practices
|
|
288
|
+
|
|
289
|
+
### 1. Declare All Variables
|
|
290
|
+
|
|
291
|
+
Always declare environment variables in `turbo.json` for proper cache invalidation:
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"tasks": {
|
|
296
|
+
"build": {
|
|
297
|
+
"env": ["ALL", "VARS", "HERE"]
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 2. Use Wildcards for Prefixes
|
|
304
|
+
|
|
305
|
+
For public/framework-specific variables:
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"tasks": {
|
|
310
|
+
"build": {
|
|
311
|
+
"env": ["NEXT_PUBLIC_*", "VITE_*"]
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### 3. Separate by Task
|
|
318
|
+
|
|
319
|
+
Different tasks can have different env vars:
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"tasks": {
|
|
324
|
+
"build": {
|
|
325
|
+
"env": ["API_URL", "BUILD_ID"]
|
|
326
|
+
},
|
|
327
|
+
"test": {
|
|
328
|
+
"env": ["TEST_DATABASE_URL"]
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 4. Use Global Env for CI
|
|
335
|
+
|
|
336
|
+
Common variables used everywhere:
|
|
337
|
+
|
|
338
|
+
```json
|
|
339
|
+
{
|
|
340
|
+
"globalEnv": ["CI", "NODE_ENV", "VERCEL"],
|
|
341
|
+
"tasks": {
|
|
342
|
+
/* ... */
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Troubleshooting
|
|
348
|
+
|
|
349
|
+
### Rule not working
|
|
350
|
+
|
|
351
|
+
- Ensure `turbo.json` exists in your project root
|
|
352
|
+
- Check that the env var is being accessed as `process.env.VAR_NAME`
|
|
353
|
+
- Verify the task name matches your `turbo.json` tasks
|
|
354
|
+
|
|
355
|
+
### Too many warnings
|
|
356
|
+
|
|
357
|
+
- Use wildcard patterns for common prefixes
|
|
358
|
+
- Add frequently-used vars to `globalEnv`
|
|
359
|
+
- Consider disabling for certain files (scripts, config)
|
|
360
|
+
|
|
361
|
+
### False positives
|
|
362
|
+
|
|
363
|
+
- Dynamic env var access might not be detected correctly:
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
// Might not be caught
|
|
367
|
+
const key = process.env[`DYNAMIC_${name}`];
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Related Configurations
|
|
371
|
+
|
|
372
|
+
- [Base](../base/README.md) - Base ESLint rules
|
|
373
|
+
- [TypeScript](../typescript/README.md) - TypeScript configuration
|
|
374
|
+
|
|
375
|
+
## Learn More
|
|
376
|
+
|
|
377
|
+
- [eslint-plugin-turbo](https://github.com/vercel/turbo/tree/main/packages/eslint-plugin-turbo)
|
|
378
|
+
- [Turborepo Documentation](https://turbo.build/repo/docs)
|
|
379
|
+
- [Turborepo Environment Variables](https://turbo.build/repo/docs/core-concepts/caching#environment-variables)
|
|
380
|
+
- [Main README](../../../README.md)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import turbo from "eslint-plugin-turbo";
|
|
2
|
+
|
|
3
|
+
import type { EslintConfigObject, EslintRuleConfig } from "../types.js";
|
|
4
|
+
import type { TurboRules } from "./types.js";
|
|
5
|
+
|
|
6
|
+
import { configNames } from "../constants.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates an ESLint configuration for Turbo.
|
|
10
|
+
*
|
|
11
|
+
* @param customRules - Optional custom rules to merge with the default Turbo rules.
|
|
12
|
+
* @returns ESLint configuration object for Turbo.
|
|
13
|
+
*/
|
|
14
|
+
export const turboConfig = (
|
|
15
|
+
customRules?: Record<string, EslintRuleConfig>,
|
|
16
|
+
): EslintConfigObject => ({
|
|
17
|
+
name: configNames.turbo,
|
|
18
|
+
plugins: {
|
|
19
|
+
turbo,
|
|
20
|
+
},
|
|
21
|
+
rules:
|
|
22
|
+
customRules ??
|
|
23
|
+
({
|
|
24
|
+
"turbo/no-undeclared-env-vars": "warn",
|
|
25
|
+
} satisfies TurboRules),
|
|
26
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Linter } from "eslint";
|
|
2
|
+
|
|
3
|
+
import type { ConfigName } from "./constants.js";
|
|
4
|
+
|
|
5
|
+
export type EslintSeverity = 0 | 1 | 2 | "error" | "off" | "warn";
|
|
6
|
+
|
|
7
|
+
export type EslintRuleConfig<
|
|
8
|
+
TOptions = Record<string, unknown>,
|
|
9
|
+
TOptions2 = Record<string, unknown>,
|
|
10
|
+
> =
|
|
11
|
+
| [EslintSeverity, string | TOptions | undefined]
|
|
12
|
+
| [EslintSeverity, string | undefined, TOptions2 | undefined]
|
|
13
|
+
| [EslintSeverity, TOptions | undefined, TOptions2 | undefined]
|
|
14
|
+
| [EslintSeverity]
|
|
15
|
+
| EslintSeverity;
|
|
16
|
+
|
|
17
|
+
export interface EslintConfigObject<
|
|
18
|
+
Rules extends Linter.RulesRecord = Linter.RulesRecord,
|
|
19
|
+
> extends Linter.Config<Rules> {
|
|
20
|
+
name: ConfigName;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type FilenameCase =
|
|
24
|
+
| "camelCase"
|
|
25
|
+
| "kebabCase"
|
|
26
|
+
| "pascalCase"
|
|
27
|
+
| "snakeCase";
|
|
28
|
+
|
|
29
|
+
export type FunctionStyle = "arrow" | "declaration" | "expression";
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# TypeScript Configuration
|
|
2
|
+
|
|
3
|
+
Comprehensive TypeScript support with type-aware linting rules from `@typescript-eslint`. The configuration strikes a balance between strict type safety and practical development.
|
|
4
|
+
|
|
5
|
+
[← Back to main README](../../../README.md)
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
TypeScript support is **enabled by default** and provides:
|
|
10
|
+
|
|
11
|
+
- Type-aware linting rules
|
|
12
|
+
- Consistent TypeScript patterns
|
|
13
|
+
- Import/export type safety
|
|
14
|
+
- Modern TypeScript best practices
|
|
15
|
+
|
|
16
|
+
## Configuration
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import { eslintConfig } from "js-style-kit";
|
|
20
|
+
|
|
21
|
+
export default eslintConfig({
|
|
22
|
+
typescript: true, // Default - automatic project detection
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Options
|
|
27
|
+
|
|
28
|
+
#### Automatic Detection (Recommended)
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
typescript: true; // Automatically finds and uses your tsconfig.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Specify Path
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
typescript: "./tsconfig.eslint.json"; // Use specific tsconfig file
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### Disable TypeScript Rules
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
typescript: false; // Turn off TypeScript linting
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## How It Works
|
|
47
|
+
|
|
48
|
+
When TypeScript is enabled:
|
|
49
|
+
|
|
50
|
+
1. **Automatic project detection** - Finds your `tsconfig.json` automatically
|
|
51
|
+
2. **Type-aware linting** - Uses TypeScript's type information for advanced checks
|
|
52
|
+
3. **JavaScript compatibility** - Type-checking disabled for `.js` files
|
|
53
|
+
4. **Base rule overrides** - Replaces ESLint rules with TypeScript equivalents (e.g., `no-unused-vars` → `@typescript-eslint/no-unused-vars`)
|
|
54
|
+
|
|
55
|
+
## Key Rule Categories
|
|
56
|
+
|
|
57
|
+
### Type Safety
|
|
58
|
+
|
|
59
|
+
**Strict Type Checking**
|
|
60
|
+
|
|
61
|
+
- `no-explicit-any` - Warn when using `any` type
|
|
62
|
+
- `no-unsafe-*` - Prevent unsafe type operations
|
|
63
|
+
- `no-unnecessary-type-assertion` - Remove redundant type assertions
|
|
64
|
+
- `no-unnecessary-condition` - Detect always-true/false conditions
|
|
65
|
+
- `strict-boolean-expressions` - Require boolean types in conditions
|
|
66
|
+
|
|
67
|
+
**Type Inference**
|
|
68
|
+
|
|
69
|
+
- `no-inferrable-types` - Remove unnecessary type annotations
|
|
70
|
+
- `prefer-as-const` - Use `as const` for literal types
|
|
71
|
+
- `consistent-type-definitions` - Prefer `interface` or `type` consistently
|
|
72
|
+
|
|
73
|
+
### Modern TypeScript
|
|
74
|
+
|
|
75
|
+
**Import/Export**
|
|
76
|
+
|
|
77
|
+
- `consistent-type-imports` - Use `import type` for types (inline style)
|
|
78
|
+
- `consistent-type-exports` - Use `export type` for types
|
|
79
|
+
- `no-import-type-side-effects` - Prevent import type side effects
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// ✅ Good - inline type imports
|
|
83
|
+
import { type User, getUser } from "./api";
|
|
84
|
+
|
|
85
|
+
// ❌ Bad - separate type import
|
|
86
|
+
import type { User } from "./api";
|
|
87
|
+
import { getUser } from "./api";
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Null Safety**
|
|
91
|
+
|
|
92
|
+
- `prefer-nullish-coalescing` - Use `??` over `||` for null checks
|
|
93
|
+
- `prefer-optional-chain` - Use `?.` for safe property access
|
|
94
|
+
- `no-non-null-assertion` - Warn on `!` non-null assertions (subjective in favor of casting)
|
|
95
|
+
|
|
96
|
+
**Modern Syntax**
|
|
97
|
+
|
|
98
|
+
- `prefer-for-of` - Prefer `for...of` over traditional `for` loops
|
|
99
|
+
- `prefer-includes` - Use `.includes()` instead of `.indexOf()`
|
|
100
|
+
- `prefer-string-starts-ends-with` - Use `.startsWith()` and `.endsWith()`
|
|
101
|
+
|
|
102
|
+
### Code Quality
|
|
103
|
+
|
|
104
|
+
**Error Prevention**
|
|
105
|
+
|
|
106
|
+
- `no-floating-promises` - Require handling Promise returns
|
|
107
|
+
- `await-thenable` - Only await Promise-like values
|
|
108
|
+
- `no-misused-promises` - Prevent Promises in wrong contexts
|
|
109
|
+
- `only-throw-error` - Only throw Error objects
|
|
110
|
+
|
|
111
|
+
**Best Practices**
|
|
112
|
+
|
|
113
|
+
- `no-unused-vars` - Detect unused variables (with `_` prefix exception)
|
|
114
|
+
- `no-empty-function` - Warn on empty functions
|
|
115
|
+
- `no-useless-constructor` - Remove unnecessary constructors
|
|
116
|
+
- `array-type` - Consistent array type syntax (defaults to `string[]` instead of `Array<string>`)
|
|
117
|
+
|
|
118
|
+
### Documentation
|
|
119
|
+
|
|
120
|
+
**Comment Quality**
|
|
121
|
+
|
|
122
|
+
- `ban-ts-comment` - Require explanations for `@ts-ignore` & `@ts-expect-error` (min 10 chars)
|
|
123
|
+
- `ban-tslint-comment` - Prevent deprecated TSLint comments
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
// ❌ Bad - no explanation
|
|
127
|
+
// @ts-ignore
|
|
128
|
+
const x = dangerous();
|
|
129
|
+
|
|
130
|
+
// ✅ Good - explains why
|
|
131
|
+
// @ts-expect-error - legacy API doesn't have types
|
|
132
|
+
const x = dangerous();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## TypeScript + React
|
|
136
|
+
|
|
137
|
+
When both TypeScript and React are enabled, additional rules apply:
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
import { eslintConfig } from "js-style-kit";
|
|
141
|
+
|
|
142
|
+
export default eslintConfig({
|
|
143
|
+
typescript: "./tsconfig.json",
|
|
144
|
+
react: true,
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
React-specific TypeScript rules:
|
|
149
|
+
|
|
150
|
+
- Type-safe prop definitions
|
|
151
|
+
- React hooks type checking
|
|
152
|
+
- JSX type safety
|
|
153
|
+
|
|
154
|
+
[→ See React Configuration](../react/README.md)
|
|
155
|
+
|
|
156
|
+
## Unused Variables
|
|
157
|
+
|
|
158
|
+
The `no-unused-vars` rule allows `_` prefix for intentionally unused variables:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
// ✅ Good - underscore prefix indicates intentional
|
|
162
|
+
const [_loading, setLoading] = useState(false);
|
|
163
|
+
const handler = (_event: Event) => {
|
|
164
|
+
/* ... */
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// ❌ Bad - unused without underscore
|
|
168
|
+
const [loading, setLoading] = useState(false);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Customization
|
|
172
|
+
|
|
173
|
+
Override specific TypeScript rules:
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
import { eslintConfig } from "js-style-kit";
|
|
177
|
+
|
|
178
|
+
export default eslintConfig({
|
|
179
|
+
typescript: true,
|
|
180
|
+
rules: {
|
|
181
|
+
// Allow any type in certain cases
|
|
182
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
183
|
+
|
|
184
|
+
// Enforce non-null assertions when you know better than TS
|
|
185
|
+
"@typescript-eslint/no-non-null-assertion": "off",
|
|
186
|
+
|
|
187
|
+
// Stricter unused vars (no underscore escape hatch)
|
|
188
|
+
"@typescript-eslint/no-unused-vars": [
|
|
189
|
+
"warn",
|
|
190
|
+
{
|
|
191
|
+
argsIgnorePattern: "^_",
|
|
192
|
+
varsIgnorePattern: "^_",
|
|
193
|
+
ignoreRestSiblings: true,
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Troubleshooting
|
|
201
|
+
|
|
202
|
+
### ESLint can't find tsconfig.json
|
|
203
|
+
|
|
204
|
+
Use explicit path:
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
typescript: "./tsconfig.json";
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Type checking is slow
|
|
211
|
+
|
|
212
|
+
1. Use `tsconfig.eslint.json` with limited `include` paths
|
|
213
|
+
2. Exclude test files if not needed for linting
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
typescript: "./tsconfig.eslint.json";
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Related Configurations
|
|
220
|
+
|
|
221
|
+
- [Base ESLint](../base/README.md) - Core JavaScript rules
|
|
222
|
+
- [Import Plugin](../import/README.md) - Import/export validation
|
|
223
|
+
- [React](../react/README.md) - React + TypeScript support
|
|
224
|
+
|
|
225
|
+
## Learn More
|
|
226
|
+
|
|
227
|
+
- [@typescript-eslint Documentation](https://typescript-eslint.io/)
|
|
228
|
+
- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html)
|
|
229
|
+
- [Main README](../../../README.md)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { defineConfig } from "eslint/config";
|
|
2
|
+
import tseslint, { type Config } from "typescript-eslint";
|
|
3
|
+
|
|
4
|
+
import type { EslintRuleConfig } from "../types.js";
|
|
5
|
+
|
|
6
|
+
import { configNames } from "../constants.js";
|
|
7
|
+
import { tseslintRules } from "./rules.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a TypeScript ESLint configuration object.
|
|
11
|
+
*
|
|
12
|
+
* @param tsconfigPath - Path to the TypeScript configuration file
|
|
13
|
+
* @param customRules - Optional object containing custom rules to override or add to the TypeScript configuration.
|
|
14
|
+
* @returns TypeScript ESLint configuration object
|
|
15
|
+
*/
|
|
16
|
+
export const tseslintConfig = (
|
|
17
|
+
tsconfigPath?: string,
|
|
18
|
+
customRules?: Record<string, EslintRuleConfig>,
|
|
19
|
+
): Config => {
|
|
20
|
+
const userCwd = process.cwd();
|
|
21
|
+
|
|
22
|
+
return defineConfig(
|
|
23
|
+
{
|
|
24
|
+
files: ["**/*.{js,cjs,mjs,ts,jsx,tsx}"],
|
|
25
|
+
languageOptions: {
|
|
26
|
+
parser: tseslint.parser,
|
|
27
|
+
parserOptions: {
|
|
28
|
+
...(tsconfigPath ?
|
|
29
|
+
{ project: tsconfigPath, tsconfigRootDir: userCwd }
|
|
30
|
+
: { projectService: true, tsconfigRootDir: import.meta.dirname }),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
name: configNames.typescript,
|
|
34
|
+
plugins: {
|
|
35
|
+
"@typescript-eslint": tseslint.plugin,
|
|
36
|
+
},
|
|
37
|
+
rules: {
|
|
38
|
+
...tseslintRules,
|
|
39
|
+
...(customRules ?? {}),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
// disable type-aware linting on JS files
|
|
44
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
45
|
+
files: ["**/*.js"],
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
};
|