@foundbyte/eslint-plugin 1.1.4-alpha.7 → 1.1.5-alpha.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 +148 -48
- package/index.d.ts +9 -4
- package/index.js +50 -34
- package/lib/rules/index.d.ts +8 -8
- package/lib/rules/no-initial-as-assertion.d.ts +9 -9
- package/lib/rules/require-json-parse-type.d.ts +16 -0
- package/lib/rules/require-json-parse-type.js +120 -0
- package/package.json +5 -5
- package/types.d.ts +9 -1
package/README.md
CHANGED
|
@@ -23,45 +23,60 @@ module.exports = {
|
|
|
23
23
|
rules: {
|
|
24
24
|
// 启用所有推荐的规则
|
|
25
25
|
'@foundbyte/arrow-function-padding': 'error',
|
|
26
|
-
'@foundbyte/no-initial-as-assertion': [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
'@foundbyte/no-initial-as-assertion': [
|
|
27
|
+
'error',
|
|
28
|
+
{
|
|
29
|
+
excludeVariableAssertions: true,
|
|
30
|
+
excludeTypePatterns: ['^const$', '^[A-Z]$', '^unknown$'],
|
|
31
|
+
excludeMethodReturns: ['parse', 'querySelector'],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
31
34
|
'@foundbyte/hooks-arrow-function-only': 'error',
|
|
32
|
-
'@foundbyte/type-comment-requirement': [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
'@foundbyte/type-comment-requirement': [
|
|
36
|
+
'error',
|
|
37
|
+
{
|
|
38
|
+
minCommentsPerGroup: 1,
|
|
39
|
+
groupSize: 8,
|
|
40
|
+
includeTypes: true,
|
|
41
|
+
includeInterfaces: true,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
38
44
|
'@foundbyte/hooks-export-limit': ['error', { maxExports: 20 }],
|
|
39
45
|
'@foundbyte/vue-script-lines-limit': ['error', { maxLines: 300 }],
|
|
40
|
-
'@foundbyte/tag-attribute-restriction': [
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
tag: 'image',
|
|
44
|
-
attributes: [
|
|
45
|
-
{ name: 'mode', disallowedValues: [''] },
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
]
|
|
49
|
-
}],
|
|
50
|
-
'@foundbyte/no-restricted-components': ['error', [
|
|
46
|
+
'@foundbyte/tag-attribute-restriction': [
|
|
47
|
+
'error',
|
|
51
48
|
{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
restrictions: [
|
|
50
|
+
{
|
|
51
|
+
tag: 'image',
|
|
52
|
+
attributes: [{ name: 'mode', disallowedValues: [''] }],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
'@foundbyte/no-restricted-components': [
|
|
58
|
+
'error',
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
pattern: '/^a-(textarea|table)$/',
|
|
62
|
+
message: '禁止使用的Arco Design组件,请使用Keyblade组件替代',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
],
|
|
56
66
|
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 5 }],
|
|
57
|
-
'@foundbyte/no-disabled-comments': [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
'@foundbyte/no-disabled-comments': [
|
|
68
|
+
'error',
|
|
69
|
+
{
|
|
70
|
+
patterns: [
|
|
71
|
+
'/^\\s*eslint-disable-next-line\\s*$/',
|
|
72
|
+
'/^\\s*eslint-disable\\s*$/',
|
|
73
|
+
'/no-explicit-any/',
|
|
74
|
+
'/vue-script-lines-limit/',
|
|
75
|
+
'/hooks-export-limit/',
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
'@foundbyte/require-json-parse-type': 'error',
|
|
65
80
|
},
|
|
66
81
|
}
|
|
67
82
|
```
|
|
@@ -71,11 +86,13 @@ module.exports = {
|
|
|
71
86
|
### no-excessive-any
|
|
72
87
|
|
|
73
88
|
限制 `any` 类型的使用次数,包括:
|
|
89
|
+
|
|
74
90
|
- 显式 `any` 类型
|
|
75
91
|
- 隐式 `any` 类型(没有类型注解且无法推导类型的变量)
|
|
76
92
|
- 泛型类型中的 `any`(如 `Ref<any>`, `Promise<any>`, `any[]` 等)
|
|
77
93
|
|
|
78
94
|
**配置示例:**
|
|
95
|
+
|
|
79
96
|
```javascript
|
|
80
97
|
// 基本用法 - 限制 any 类型最多使用 5 次
|
|
81
98
|
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 5 }]
|
|
@@ -88,6 +105,7 @@ module.exports = {
|
|
|
88
105
|
```
|
|
89
106
|
|
|
90
107
|
**检测范围:**
|
|
108
|
+
|
|
91
109
|
- 显式 `any` 类型:`const a: any = 1`
|
|
92
110
|
- 隐式 `any` 类型:
|
|
93
111
|
- 没有初始值的变量声明:`const a`
|
|
@@ -99,6 +117,7 @@ module.exports = {
|
|
|
99
117
|
|
|
100
118
|
**智能类型推导:**
|
|
101
119
|
以下情况不计入 any 使用:
|
|
120
|
+
|
|
102
121
|
- 有初始值的变量:`const a = 1`, `const b = ref(60)`
|
|
103
122
|
- 对象字面量:`const obj = { name: 'test' }`
|
|
104
123
|
- 数组字面量:`const arr = [1, 2, 3]`
|
|
@@ -108,6 +127,7 @@ module.exports = {
|
|
|
108
127
|
- 异步函数调用:`const res = await ssoMobileAuth(...)`
|
|
109
128
|
|
|
110
129
|
**示例:**
|
|
130
|
+
|
|
111
131
|
```typescript
|
|
112
132
|
// ✅ 正确 - 当 maxAnyCount 为 3 时
|
|
113
133
|
const a: any = 1
|
|
@@ -155,6 +175,7 @@ interface MyInterface {
|
|
|
155
175
|
- 设置为正整数表示允许的最大次数
|
|
156
176
|
|
|
157
177
|
**注意:**
|
|
178
|
+
|
|
158
179
|
- 该规则统计文件中所有 `any` 类型的使用,包括显式和隐式
|
|
159
180
|
- 规则会智能识别可以类型推导的情况,避免过度检测
|
|
160
181
|
- 建议在 TypeScript 项目中设置合理的限制,鼓励使用具体类型
|
|
@@ -165,6 +186,7 @@ interface MyInterface {
|
|
|
165
186
|
禁止在初始变量定义时使用 `as` 类型断言,强制使用显式类型注解。
|
|
166
187
|
|
|
167
188
|
**配置示例:**
|
|
189
|
+
|
|
168
190
|
```javascript
|
|
169
191
|
// 基本用法
|
|
170
192
|
'@foundbyte/no-initial-as-assertion': 'error'
|
|
@@ -177,6 +199,7 @@ interface MyInterface {
|
|
|
177
199
|
```
|
|
178
200
|
|
|
179
201
|
**示例:**
|
|
202
|
+
|
|
180
203
|
```typescript
|
|
181
204
|
// ✅ 正确 - 使用显式类型注解
|
|
182
205
|
const a: Ref<string> = ref()
|
|
@@ -191,13 +214,16 @@ const c = reactive({}) as Reactive<object>
|
|
|
191
214
|
const d = ref({} as { a: string }) // 嵌套的类型断言也会被检测到
|
|
192
215
|
|
|
193
216
|
// ✅ 当 excludeVariableAssertions 为 true 时 - 变量断言被排除
|
|
194
|
-
interface ID {
|
|
217
|
+
interface ID {
|
|
218
|
+
a?: string
|
|
219
|
+
b?: string
|
|
220
|
+
}
|
|
195
221
|
const d: ID = {}
|
|
196
222
|
const e = d as { a?: string } // 不会报错
|
|
197
223
|
|
|
198
224
|
// ✅ 当 excludeMethodReturns 包含 'map' 时 - 数组方法返回类型断言被排除
|
|
199
225
|
const arr = [1, 2, 3]
|
|
200
|
-
const mapped = arr.map(x => x * 2) as number[] // 不会报错
|
|
226
|
+
const mapped = arr.map((x) => x * 2) as number[] // 不会报错
|
|
201
227
|
|
|
202
228
|
// ✅ 当 excludeMethodReturns 包含 'parse' 时 - JSON.parse 返回类型断言被排除
|
|
203
229
|
const jsonString = '{"name": "test"}'
|
|
@@ -221,6 +247,7 @@ const parsed = JSON.parse(jsonString) as { name: string } // 不会报错
|
|
|
221
247
|
- 示例:`['^const$', '^Readonly<.*>$']` 可以排除 `as const` 和 `as Readonly<...>`
|
|
222
248
|
|
|
223
249
|
**注意:**
|
|
250
|
+
|
|
224
251
|
- 该规则检查变量声明时的所有类型断言,包括嵌套在函数调用参数中的类型断言
|
|
225
252
|
- 推荐使用显式类型注解,因为类型断言会绕过 TypeScript 的类型检查
|
|
226
253
|
- 对于函数调用返回值的类型断言,可以通过配置选项排除特定情况
|
|
@@ -230,11 +257,13 @@ const parsed = JSON.parse(jsonString) as { name: string } // 不会报错
|
|
|
230
257
|
要求函数体之间必须空行。
|
|
231
258
|
|
|
232
259
|
**配置示例:**
|
|
260
|
+
|
|
233
261
|
```javascript
|
|
234
262
|
'@foundbyte/arrow-function-padding': 'error' // 总是要求空行
|
|
235
263
|
```
|
|
236
264
|
|
|
237
265
|
**示例:**
|
|
266
|
+
|
|
238
267
|
```javascript
|
|
239
268
|
// ✅ 正确
|
|
240
269
|
const one = () => {}
|
|
@@ -251,12 +280,13 @@ const two = () => {}
|
|
|
251
280
|
要求 React/Vue Hooks 必须使用箭头函数。
|
|
252
281
|
|
|
253
282
|
**示例:**
|
|
283
|
+
|
|
254
284
|
```javascript
|
|
255
285
|
// ✅ 正确
|
|
256
286
|
const useMyHook = () => {
|
|
257
287
|
const [state, setState] = useState()
|
|
258
288
|
return state
|
|
259
|
-
}
|
|
289
|
+
}
|
|
260
290
|
|
|
261
291
|
// ❌ 错误
|
|
262
292
|
function useMyHook() {
|
|
@@ -270,17 +300,19 @@ function useMyHook() {
|
|
|
270
300
|
限制 Hooks 文件内部成员的导出数量。
|
|
271
301
|
|
|
272
302
|
**配置示例:**
|
|
303
|
+
|
|
273
304
|
```javascript
|
|
274
305
|
'@foundbyte/hooks-export-limit': ['error', { maxExports: 2 }] // 最大允许的导出数量
|
|
275
306
|
```
|
|
276
307
|
|
|
277
308
|
**示例:**
|
|
309
|
+
|
|
278
310
|
```javascript
|
|
279
311
|
// ✅ 正确 - 只有一个导出
|
|
280
312
|
export const useMyHook = () => {
|
|
281
313
|
const a = ''
|
|
282
314
|
return {
|
|
283
|
-
a
|
|
315
|
+
a,
|
|
284
316
|
}
|
|
285
317
|
}
|
|
286
318
|
|
|
@@ -290,7 +322,7 @@ export const useMyHook = () => {
|
|
|
290
322
|
const b = ''
|
|
291
323
|
return {
|
|
292
324
|
a,
|
|
293
|
-
b
|
|
325
|
+
b,
|
|
294
326
|
}
|
|
295
327
|
}
|
|
296
328
|
```
|
|
@@ -300,8 +332,9 @@ export const useMyHook = () => {
|
|
|
300
332
|
要求复杂类型定义必须有类型注释。
|
|
301
333
|
|
|
302
334
|
**配置示例:**
|
|
335
|
+
|
|
303
336
|
```javascript
|
|
304
|
-
'@foundbyte/type-comment-requirement': ['error', {
|
|
337
|
+
'@foundbyte/type-comment-requirement': ['error', {
|
|
305
338
|
/** 至少1个注释 */
|
|
306
339
|
minCommentsPerGroup: 1,
|
|
307
340
|
/** 每3个类型 */
|
|
@@ -310,6 +343,7 @@ export const useMyHook = () => {
|
|
|
310
343
|
```
|
|
311
344
|
|
|
312
345
|
**示例:**
|
|
346
|
+
|
|
313
347
|
```ts
|
|
314
348
|
// ✅ 正确 - 有类型注释
|
|
315
349
|
interface user {
|
|
@@ -317,11 +351,15 @@ interface user {
|
|
|
317
351
|
name: 'John'
|
|
318
352
|
/** 年龄 */
|
|
319
353
|
age: 30
|
|
320
|
-
ddress: { city: 'NY'
|
|
354
|
+
ddress: { city: 'NY'; zip: '10001' }
|
|
321
355
|
}
|
|
322
356
|
|
|
323
357
|
// ❌ 错误 - 复杂对象没有类型注释
|
|
324
|
-
interface user {
|
|
358
|
+
interface user {
|
|
359
|
+
name: 'John'
|
|
360
|
+
age: 30
|
|
361
|
+
address: { city: 'NY'; zip: '10001' }
|
|
362
|
+
}
|
|
325
363
|
```
|
|
326
364
|
|
|
327
365
|
### vue-script-lines-limit
|
|
@@ -329,11 +367,13 @@ interface user { name: 'John', age: 30, address: { city: 'NY', zip: '10001' } }
|
|
|
329
367
|
限制 Vue 单文件组件中 `<script>` 标签的行数。
|
|
330
368
|
|
|
331
369
|
**配置示例:**
|
|
370
|
+
|
|
332
371
|
```javascript
|
|
333
372
|
'@foundbyte/vue-script-lines-limit': ['error', { maxLines: 200 }] // 最大允许的行数
|
|
334
373
|
```
|
|
335
374
|
|
|
336
375
|
**示例:**
|
|
376
|
+
|
|
337
377
|
```vue
|
|
338
378
|
<!-- ✅ 正确 - script 标签行数在限制内 -->
|
|
339
379
|
<script>
|
|
@@ -344,8 +384,8 @@ export default {
|
|
|
344
384
|
methods: {
|
|
345
385
|
increment() {
|
|
346
386
|
this.count++
|
|
347
|
-
}
|
|
348
|
-
}
|
|
387
|
+
},
|
|
388
|
+
},
|
|
349
389
|
}
|
|
350
390
|
</script>
|
|
351
391
|
|
|
@@ -360,6 +400,7 @@ export default {
|
|
|
360
400
|
禁止使用特定的 ESLint 禁用注释,如 `eslint-disable`、`eslint-disable-next-line` 等。支持 JavaScript/TypeScript 注释和 Vue 模板中的 HTML 注释。
|
|
361
401
|
|
|
362
402
|
**配置示例:**
|
|
403
|
+
|
|
363
404
|
```javascript
|
|
364
405
|
'@foundbyte/no-disabled-comments': ['error', {
|
|
365
406
|
patterns: [
|
|
@@ -373,6 +414,7 @@ export default {
|
|
|
373
414
|
```
|
|
374
415
|
|
|
375
416
|
**支持的注释类型:**
|
|
417
|
+
|
|
376
418
|
1. **JavaScript/TypeScript 注释**:
|
|
377
419
|
- 行注释:`// eslint-disable-next-line`
|
|
378
420
|
- 块注释:`/* eslint-disable */`
|
|
@@ -382,11 +424,13 @@ export default {
|
|
|
382
424
|
- `<!-- eslint-disable-next-line -->`
|
|
383
425
|
|
|
384
426
|
**检测的注释内容:**
|
|
427
|
+
|
|
385
428
|
- 注释内容经过 `trim()` 处理,去除前后空白字符
|
|
386
429
|
- 支持正则表达式模糊匹配
|
|
387
430
|
- 支持多个模式同时匹配
|
|
388
431
|
|
|
389
432
|
**示例:**
|
|
433
|
+
|
|
390
434
|
```javascript
|
|
391
435
|
// ✅ 正确 - 不匹配任何禁用模式
|
|
392
436
|
// 普通注释
|
|
@@ -434,11 +478,13 @@ const x: any = 1;
|
|
|
434
478
|
- 当为 true 时,匹配区分大小写
|
|
435
479
|
|
|
436
480
|
**错误消息:**
|
|
481
|
+
|
|
437
482
|
- `禁止使用注释: "eslint-disable-next-line",如必须排除,请在 .eslintignore 中排除,并写清楚排除原因`
|
|
438
483
|
- `禁止使用注释: "eslint-disable",如必须排除,请在 .eslintignore 中排除,并写清楚排除原因`
|
|
439
484
|
- `禁止使用注释: "eslint-disable-next-line @typescript-eslint/no-explicit-any",如必须排除,请在 .eslintignore 中排除,并写清楚排除原因`
|
|
440
485
|
|
|
441
486
|
**注意:**
|
|
487
|
+
|
|
442
488
|
- 该规则会检查所有 JavaScript/TypeScript 注释和 Vue 模板中的 HTML 注释
|
|
443
489
|
- 对于 Vue 文件,规则会同时检查 `<script>` 中的 JavaScript 注释和 `<template>` 中的 HTML 注释
|
|
444
490
|
- 建议只禁用特定的、有问题的规则,而不是使用无参数的 `eslint-disable` 禁用所有规则
|
|
@@ -449,6 +495,7 @@ const x: any = 1;
|
|
|
449
495
|
通过正则表达式配置禁止使用某些组件,支持自定义错误消息。
|
|
450
496
|
|
|
451
497
|
**配置示例:**
|
|
498
|
+
|
|
452
499
|
```javascript
|
|
453
500
|
'@foundbyte/no-restricted-components': ['error', [
|
|
454
501
|
{
|
|
@@ -463,6 +510,7 @@ const x: any = 1;
|
|
|
463
510
|
```
|
|
464
511
|
|
|
465
512
|
**支持的语法:**
|
|
513
|
+
|
|
466
514
|
- 正则表达式字符串:以 `/` 开头和结尾,如 `/^a-(textarea|table)$/`
|
|
467
515
|
- 普通字符串:自动转义并创建正则表达式,如 `'a-textarea'`
|
|
468
516
|
- 支持自闭合标签:`<a-textarea />`
|
|
@@ -471,14 +519,18 @@ const x: any = 1;
|
|
|
471
519
|
- 只对 .vue 文件生效,不会影响其他文件类型
|
|
472
520
|
|
|
473
521
|
**示例:**
|
|
522
|
+
|
|
474
523
|
```vue
|
|
475
524
|
<!-- ✅ 正确 - 不匹配任何禁止模式的组件 -->
|
|
476
525
|
<div>正常内容</div>
|
|
526
|
+
|
|
477
527
|
<span>正常内容</span>
|
|
528
|
+
|
|
478
529
|
<custom-component />
|
|
479
530
|
|
|
480
531
|
<!-- ❌ 错误 - 匹配 a-textarea 模式 -->
|
|
481
532
|
<a-textarea />
|
|
533
|
+
|
|
482
534
|
<a-textarea></a-textarea>
|
|
483
535
|
|
|
484
536
|
<!-- ❌ 错误 - 匹配 a-input 模式 -->
|
|
@@ -489,9 +541,11 @@ const x: any = 1;
|
|
|
489
541
|
```
|
|
490
542
|
|
|
491
543
|
**错误消息:**
|
|
544
|
+
|
|
492
545
|
- 显示配置中指定的自定义消息,如:`禁止使用 a-textarea 和 a-input 组件,请使用自定义组件替代`
|
|
493
546
|
|
|
494
547
|
**注意:**
|
|
548
|
+
|
|
495
549
|
- 该规则只检查 .vue 文件中的模板部分,不会检查 JavaScript/TypeScript 代码中的字符串
|
|
496
550
|
- 支持同时配置多个禁止模式,每个模式独立检查
|
|
497
551
|
- 正则表达式匹配组件名,不区分大小写
|
|
@@ -502,20 +556,21 @@ const x: any = 1;
|
|
|
502
556
|
限制特定标签使用某些属性或属性值。
|
|
503
557
|
|
|
504
558
|
**配置示例:**
|
|
559
|
+
|
|
505
560
|
```javascript
|
|
506
561
|
'@foundbyte/tag-attribute-restriction': ['error', {
|
|
507
562
|
restrictions: [
|
|
508
563
|
{
|
|
509
564
|
tag: 'image', // 标签名称
|
|
510
565
|
attributes: [ // 属性限制列表
|
|
511
|
-
{
|
|
566
|
+
{
|
|
512
567
|
name: 'mode', // 属性名称
|
|
513
568
|
disallowedValues: [ // 禁止的值列表(可选)
|
|
514
|
-
'',
|
|
569
|
+
'',
|
|
515
570
|
'scaleToFill'
|
|
516
|
-
]
|
|
571
|
+
]
|
|
517
572
|
},
|
|
518
|
-
{
|
|
573
|
+
{
|
|
519
574
|
name: 'lazy-load' // 完全禁止该属性
|
|
520
575
|
}
|
|
521
576
|
]
|
|
@@ -525,6 +580,7 @@ const x: any = 1;
|
|
|
525
580
|
```
|
|
526
581
|
|
|
527
582
|
**支持的语法:**
|
|
583
|
+
|
|
528
584
|
- 静态属性: `<image mode="" />`
|
|
529
585
|
- 动态属性: `<image :mode="'scaleToFill'" />`
|
|
530
586
|
- 无值属性: `<image lazy-load />`
|
|
@@ -532,6 +588,7 @@ const x: any = 1;
|
|
|
532
588
|
- JSX 属性
|
|
533
589
|
|
|
534
590
|
**示例:**
|
|
591
|
+
|
|
535
592
|
```vue
|
|
536
593
|
<!-- ✅ 正确 -->
|
|
537
594
|
<image src="test.jpg" mode="aspectFit" />
|
|
@@ -547,5 +604,48 @@ const x: any = 1;
|
|
|
547
604
|
```
|
|
548
605
|
|
|
549
606
|
**错误消息:**
|
|
607
|
+
|
|
550
608
|
- `标签 <image> 不允许使用属性 mode` - 当属性被完全禁止时
|
|
551
609
|
- `标签 <image> 的属性 mode 不允许使用值 ""` - 当特定属性值被禁止时
|
|
610
|
+
|
|
611
|
+
### require-json-parse-type
|
|
612
|
+
|
|
613
|
+
要求 `JSON.parse` 的返回值在赋值给变量时必须带有显式类型注解。
|
|
614
|
+
|
|
615
|
+
**配置示例:**
|
|
616
|
+
|
|
617
|
+
```javascript
|
|
618
|
+
// 基本用法 - 检查 const、let、var
|
|
619
|
+
'@foundbyte/require-json-parse-type': 'error'
|
|
620
|
+
|
|
621
|
+
// 只检查 const
|
|
622
|
+
'@foundbyte/require-json-parse-type': ['error', { declarationTypes: ['const'] }]
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
**示例:**
|
|
626
|
+
|
|
627
|
+
```typescript
|
|
628
|
+
// ✅ 正确 - 变量带有显式类型注解
|
|
629
|
+
const a: { s: number } = JSON.parse('{s:1}')
|
|
630
|
+
|
|
631
|
+
// ✅ 正确 - 解构赋值带有显式类型注解
|
|
632
|
+
const { s }: { s: number } = JSON.parse('{s:1}')
|
|
633
|
+
|
|
634
|
+
// ❌ 错误 - 缺少类型注解
|
|
635
|
+
const a = JSON.parse('{s:1}')
|
|
636
|
+
|
|
637
|
+
// ❌ 错误 - 使用 as 断言不算作变量声明类型注解
|
|
638
|
+
const a = JSON.parse('{s:1}') as { s: number }
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**配置选项:**
|
|
642
|
+
|
|
643
|
+
- `declarationTypes` (array, 默认 `['const', 'let', 'var']`): 要检查的变量声明类型
|
|
644
|
+
- 可选项:`const`、`let`、`var`
|
|
645
|
+
- 例如设置为 `['const']` 则只检查 `const` 声明
|
|
646
|
+
|
|
647
|
+
**注意:**
|
|
648
|
+
|
|
649
|
+
- 该规则只检查变量声明的初始化表达式直接是 `JSON.parse(...)` 的情况
|
|
650
|
+
- 对于 `const a = JSON.parse('...') as Type` 也会报错,因为它要求类型注解必须声明在变量本身
|
|
651
|
+
- 如希望允许 `as` 类型断言,请搭配 `@foundbyte/no-initial-as-assertion` 规则按需配置
|
package/index.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { ESLint } from 'eslint'
|
|
1
|
+
import { ESLint, Rule } from 'eslint'
|
|
2
2
|
import { Rules } from './lib/rules'
|
|
3
|
-
import { Rule } from 'eslint'
|
|
4
3
|
|
|
5
|
-
export {
|
|
4
|
+
export {
|
|
5
|
+
ArrowFunctionPaddingOptions,
|
|
6
|
+
HooksArrowFunctionOnlyOptions,
|
|
7
|
+
HooksExportLimitOptions,
|
|
8
|
+
TypeCommentRequirementOptions,
|
|
9
|
+
VueScriptLinesLimitOptions,
|
|
10
|
+
} from './types'
|
|
6
11
|
|
|
7
12
|
declare const plugin: ESLint.Plugin & {
|
|
8
13
|
rules: Rules
|
|
@@ -11,4 +16,4 @@ declare const plugin: ESLint.Plugin & {
|
|
|
11
16
|
}
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
export {
|
|
19
|
+
export { plugin as default, type Rules }
|
package/index.js
CHANGED
|
@@ -8,23 +8,26 @@ const noInitialAsAssertionRule = require('./lib/rules/no-initial-as-assertion')
|
|
|
8
8
|
const noExcessiveAnyRule = require('./lib/rules/no-excessive-any')
|
|
9
9
|
const noDisabledCommentsRule = require('./lib/rules/no-disabled-comments')
|
|
10
10
|
const noRestrictedComponentsRule = require('./lib/rules/no-restricted-components')
|
|
11
|
+
const requireJsonParseTypeRule = require('./lib/rules/require-json-parse-type')
|
|
11
12
|
|
|
12
13
|
// 限制属性,同时相当于限制某些组件禁止使用
|
|
13
14
|
const restrictions = [
|
|
14
15
|
{
|
|
15
16
|
// 禁止<image />直接使用空的mode: <image mode="" />
|
|
16
17
|
tag: 'image',
|
|
18
|
+
attributes: [{ name: 'mode', disallowedValues: [''] }],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
tag: 'page-meta',
|
|
17
22
|
attributes: [
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
// harmony 不兼容
|
|
24
|
+
{ name: 'page-style' },
|
|
25
|
+
],
|
|
20
26
|
},
|
|
21
27
|
{
|
|
22
28
|
// 禁止<kb-pro-table /> 使用v-bind,请使用main.ts全局配置
|
|
23
29
|
tag: 'kb-pro-table',
|
|
24
|
-
attributes: [
|
|
25
|
-
{ name: 'v-bind' },
|
|
26
|
-
{ name: 'bordered' },
|
|
27
|
-
],
|
|
30
|
+
attributes: [{ name: 'v-bind' }, { name: 'bordered' }],
|
|
28
31
|
},
|
|
29
32
|
]
|
|
30
33
|
|
|
@@ -40,6 +43,7 @@ module.exports = {
|
|
|
40
43
|
'no-excessive-any': noExcessiveAnyRule,
|
|
41
44
|
'no-disabled-comments': noDisabledCommentsRule,
|
|
42
45
|
'no-restricted-components': noRestrictedComponentsRule,
|
|
46
|
+
'require-json-parse-type': requireJsonParseTypeRule,
|
|
43
47
|
},
|
|
44
48
|
configs: {
|
|
45
49
|
customize: (options) => {
|
|
@@ -50,38 +54,50 @@ module.exports = {
|
|
|
50
54
|
'@foundbyte/vue-script-lines-limit': ['error', { maxLines: 300 }],
|
|
51
55
|
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 5 }],
|
|
52
56
|
'@foundbyte/hooks-export-limit': ['error', { maxExports: 20 }],
|
|
53
|
-
'@foundbyte/no-initial-as-assertion': [
|
|
54
|
-
|
|
55
|
-
excludeTypePatterns: ['^const$', '^[A-Z]$', '^unknown$', '^EAppEnv$'],
|
|
56
|
-
excludeMethodReturns: ['parse', 'querySelector'],
|
|
57
|
-
}],
|
|
58
|
-
'@foundbyte/no-restricted-components': ['error', [
|
|
57
|
+
'@foundbyte/no-initial-as-assertion': [
|
|
58
|
+
'error',
|
|
59
59
|
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
excludeVariableAssertions: true,
|
|
61
|
+
excludeTypePatterns: ['^const$', '^[A-Z]$', '^unknown$', '^EAppEnv$'],
|
|
62
|
+
excludeMethodReturns: ['parse', 'querySelector'],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
'@foundbyte/no-restricted-components': [
|
|
66
|
+
'error',
|
|
67
|
+
[
|
|
68
|
+
{
|
|
69
|
+
pattern: '/^a-(textarea|table)$/',
|
|
70
|
+
message: '禁止使用的Arco Design组件,请使用Keyblade组件替代',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
],
|
|
64
74
|
'@foundbyte/tag-attribute-restriction': ['error', { restrictions }],
|
|
65
75
|
// 类型加注释
|
|
66
|
-
'@foundbyte/type-comment-requirement': [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
'@foundbyte/type-comment-requirement': [
|
|
77
|
+
'error',
|
|
78
|
+
{
|
|
79
|
+
minCommentsPerGroup: 1,
|
|
80
|
+
groupSize: 8,
|
|
81
|
+
includeTypes: true,
|
|
82
|
+
includeInterfaces: true,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
72
85
|
// 禁止使用某些注释
|
|
73
|
-
'@foundbyte/no-disabled-comments': [
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
'@foundbyte/no-disabled-comments': [
|
|
87
|
+
'error',
|
|
88
|
+
{
|
|
89
|
+
patterns: [
|
|
90
|
+
'/^\\s*eslint-disable-next-line\\s*$/',
|
|
91
|
+
'/^\\s*eslint-disable\\s*$/',
|
|
92
|
+
'/no-explicit-any/',
|
|
93
|
+
'/vue-script-lines-limit/',
|
|
94
|
+
'/hooks-export-limit/',
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
],
|
|
82
98
|
...(options || {}),
|
|
83
|
-
}
|
|
99
|
+
},
|
|
84
100
|
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
101
|
+
},
|
|
102
|
+
},
|
|
87
103
|
}
|
package/lib/rules/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import { Rule } from 'eslint'
|
|
2
|
+
|
|
1
3
|
export { ArrowFunctionPaddingOptions } from './arrow-function-padding'
|
|
2
|
-
export { HooksExportLimitOptions } from './hooks-export-limit'
|
|
3
|
-
export { TypeCommentRequirementOptions } from './type-comment-requirement'
|
|
4
|
-
export { VueScriptLinesLimitOptions } from './vue-script-lines-limit'
|
|
5
4
|
export { HooksArrowFunctionOnlyOptions } from './hooks-arrow-function-only'
|
|
6
|
-
export {
|
|
7
|
-
export { NoInitialAsAssertionOptions } from './no-initial-as-assertion'
|
|
8
|
-
export { NoExcessiveAnyOptions } from './no-excessive-any'
|
|
5
|
+
export { HooksExportLimitOptions } from './hooks-export-limit'
|
|
9
6
|
export { NoDisabledCommentsOptions } from './no-disabled-comments'
|
|
7
|
+
export { NoExcessiveAnyOptions } from './no-excessive-any'
|
|
8
|
+
export { NoInitialAsAssertionOptions } from './no-initial-as-assertion'
|
|
10
9
|
export { NoRestrictedComponentsOptions } from './no-restricted-components'
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export { TagAttributeRestrictionOptions } from './tag-attribute-restriction'
|
|
11
|
+
export { TypeCommentRequirementOptions } from './type-comment-requirement'
|
|
12
|
+
export { VueScriptLinesLimitOptions } from './vue-script-lines-limit'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @foundbyte/eslint-plugin 规则配置
|
|
@@ -2,21 +2,21 @@ import { Rule } from 'eslint'
|
|
|
2
2
|
|
|
3
3
|
export interface NoInitialAsAssertionOptions {
|
|
4
4
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
* 是否排除对变量的 as 断言
|
|
6
|
+
* @default false
|
|
7
|
+
*/
|
|
8
8
|
excludeVariableAssertions?: boolean
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
* 排除特定方法返回类型的 as 断言
|
|
12
|
+
* @default []
|
|
13
|
+
*/
|
|
14
14
|
excludeMethodReturns?: string[]
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
* 排除匹配正则表达式模式的类型断言
|
|
18
|
+
* @default []
|
|
19
|
+
*/
|
|
20
20
|
excludeTypePatterns?: string[]
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Rule } from 'eslint'
|
|
2
|
+
|
|
3
|
+
export interface RequireJsonParseTypeOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 要检查的变量声明类型
|
|
6
|
+
* @default ['const', 'let', 'var']
|
|
7
|
+
*/
|
|
8
|
+
declarationTypes?: Array<'const' | 'let' | 'var'>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 要求 JSON.parse 的返回值必须声明显式类型
|
|
13
|
+
*/
|
|
14
|
+
declare const rule: Rule.RuleModule
|
|
15
|
+
|
|
16
|
+
export default rule
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: 'problem',
|
|
4
|
+
docs: {
|
|
5
|
+
description: '要求 JSON.parse 的返回值必须声明显式类型',
|
|
6
|
+
category: 'Best Practices',
|
|
7
|
+
recommended: false,
|
|
8
|
+
},
|
|
9
|
+
fixable: null,
|
|
10
|
+
schema: [
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
declarationTypes: {
|
|
15
|
+
type: 'array',
|
|
16
|
+
items: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
enum: ['const', 'let', 'var'],
|
|
19
|
+
},
|
|
20
|
+
default: ['const', 'let', 'var'],
|
|
21
|
+
description: '要检查的变量声明类型',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
messages: {
|
|
28
|
+
requireJsonParseType:
|
|
29
|
+
"JSON.parse 的返回值必须声明显式类型,例如:const a: { s: number } = JSON.parse('...')",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
create(context) {
|
|
34
|
+
const options = context.options[0] || {}
|
|
35
|
+
const declarationTypes = options.declarationTypes || ['const', 'let', 'var']
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 判断调用表达式是否是 JSON.parse(...)
|
|
39
|
+
* @param {Object} node AST 节点
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
function isJsonParseCall(node) {
|
|
43
|
+
if (!node || node.type !== 'CallExpression') {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const callee = node.callee
|
|
48
|
+
if (!callee || callee.type !== 'MemberExpression') {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const object = callee.object
|
|
53
|
+
const property = callee.property
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
object &&
|
|
57
|
+
object.type === 'Identifier' &&
|
|
58
|
+
object.name === 'JSON' &&
|
|
59
|
+
property &&
|
|
60
|
+
property.type === 'Identifier' &&
|
|
61
|
+
property.name === 'parse'
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 从变量声明的初始化表达式中取出最底层的调用表达式
|
|
67
|
+
* 用于处理 const a = JSON.parse('...') as Type 这种情况
|
|
68
|
+
* @param {Object} node AST 节点
|
|
69
|
+
* @returns {Object|null}
|
|
70
|
+
*/
|
|
71
|
+
function unwrapExpression(node) {
|
|
72
|
+
if (!node) {
|
|
73
|
+
return null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression') {
|
|
77
|
+
return unwrapExpression(node.expression)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return node
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 检查变量声明是否使用了 JSON.parse 并且缺少显式类型注解
|
|
85
|
+
* @param {Object} node VariableDeclaration 节点
|
|
86
|
+
*/
|
|
87
|
+
function checkVariableDeclaration(node) {
|
|
88
|
+
if (!declarationTypes.includes(node.kind)) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
node.declarations.forEach((declaration) => {
|
|
93
|
+
if (!declaration.init) {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const unwrappedInit = unwrapExpression(declaration.init)
|
|
98
|
+
if (!isJsonParseCall(unwrappedInit)) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 检查变量声明是否有显式类型注解
|
|
103
|
+
// 支持普通变量:const a: Type = ...
|
|
104
|
+
// 支持解构:const { a }: Type = ...
|
|
105
|
+
if (declaration.id && declaration.id.typeAnnotation) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
context.report({
|
|
110
|
+
node: declaration,
|
|
111
|
+
messageId: 'requireJsonParseType',
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
VariableDeclaration: checkVariableDeclaration,
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foundbyte/eslint-plugin",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5-alpha.0",
|
|
4
4
|
"description": "ESLint plugin for enforcing code style rules including arrow function padding, Vue script lines limit, and TypeScript type comment requirements",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
5
|
+
"author": "",
|
|
6
|
+
"license": "ISC",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"eslint",
|
|
9
9
|
"eslintplugin",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"type-comment",
|
|
17
17
|
"comment-requirement"
|
|
18
18
|
],
|
|
19
|
-
"
|
|
20
|
-
"
|
|
19
|
+
"main": "index.js",
|
|
20
|
+
"types": "index.d.ts",
|
|
21
21
|
"publishConfig": {
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
package/types.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
// 导出所有规则配置类型
|
|
2
|
-
export type {
|
|
2
|
+
export type {
|
|
3
|
+
ArrowFunctionPaddingOptions,
|
|
4
|
+
HooksArrowFunctionOnlyOptions,
|
|
5
|
+
HooksExportLimitOptions,
|
|
6
|
+
NoDisabledCommentsOptions,
|
|
7
|
+
NoExcessiveAnyOptions,
|
|
8
|
+
TypeCommentRequirementOptions,
|
|
9
|
+
VueScriptLinesLimitOptions,
|
|
10
|
+
} from './lib/rules'
|