@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 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': ['error', {
27
- excludeVariableAssertions: true,
28
- excludeTypePatterns: ['^const$', '^[A-Z]$', '^unknown$'],
29
- excludeMethodReturns: ['parse', 'querySelector']
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': ['error', {
33
- minCommentsPerGroup: 1,
34
- groupSize: 8,
35
- includeTypes: true,
36
- includeInterfaces: true,
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': ['error', {
41
- restrictions: [
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
- pattern: '/^a-(textarea|table)$/',
53
- message: '禁止使用的Arco Design组件,请使用Keyblade组件替代'
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': ['error', {
58
- patterns: [
59
- '/^\\s*eslint-disable-next-line\\s*$/',
60
- '/^\\s*eslint-disable\\s*$/',
61
- '/no-explicit-any/',
62
- '/vue-script-lines-limit/',
63
- '/hooks-export-limit/'
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 { a?: string, b?: string }
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', zip: '10001' }
354
+ ddress: { city: 'NY'; zip: '10001' }
321
355
  }
322
356
 
323
357
  // ❌ 错误 - 复杂对象没有类型注释
324
- interface user { name: 'John', age: 30, address: { city: 'NY', zip: '10001' } }
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 { ArrowFunctionPaddingOptions, HooksExportLimitOptions, TypeCommentRequirementOptions, VueScriptLinesLimitOptions, HooksArrowFunctionOnlyOptions } from './types'
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 { type Rules, plugin as default }
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
- { name: 'mode', disallowedValues: [''] },
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': ['error', {
54
- excludeVariableAssertions: true,
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
- pattern: '/^a-(textarea|table)$/',
61
- message: '禁止使用的Arco Design组件,请使用Keyblade组件替代'
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': ['error', {
67
- minCommentsPerGroup: 1,
68
- groupSize: 8,
69
- includeTypes: true,
70
- includeInterfaces: true,
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': ['error', {
74
- patterns: [
75
- '/^\\s*eslint-disable-next-line\\s*$/',
76
- '/^\\s*eslint-disable\\s*$/',
77
- '/no-explicit-any/',
78
- '/vue-script-lines-limit/',
79
- '/hooks-export-limit/',
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
  }
@@ -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 { TagAttributeRestrictionOptions } from './tag-attribute-restriction'
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
- import { Rule } from 'eslint'
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
- * 是否排除对变量的 as 断言
6
- * @default false
7
- */
5
+ * 是否排除对变量的 as 断言
6
+ * @default false
7
+ */
8
8
  excludeVariableAssertions?: boolean
9
9
 
10
10
  /**
11
- * 排除特定方法返回类型的 as 断言
12
- * @default []
13
- */
11
+ * 排除特定方法返回类型的 as 断言
12
+ * @default []
13
+ */
14
14
  excludeMethodReturns?: string[]
15
15
 
16
16
  /**
17
- * 排除匹配正则表达式模式的类型断言
18
- * @default []
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.4-alpha.7",
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
- "main": "index.js",
6
- "types": "index.d.ts",
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
- "author": "",
20
- "license": "ISC",
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 { ArrowFunctionPaddingOptions, HooksExportLimitOptions, TypeCommentRequirementOptions, VueScriptLinesLimitOptions, HooksArrowFunctionOnlyOptions, NoExcessiveAnyOptions, NoDisabledCommentsOptions } from './lib/rules'
2
+ export type {
3
+ ArrowFunctionPaddingOptions,
4
+ HooksArrowFunctionOnlyOptions,
5
+ HooksExportLimitOptions,
6
+ NoDisabledCommentsOptions,
7
+ NoExcessiveAnyOptions,
8
+ TypeCommentRequirementOptions,
9
+ VueScriptLinesLimitOptions,
10
+ } from './lib/rules'