@foundbyte/eslint-plugin 1.1.4-alpha.5 → 1.1.4-alpha.7
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 +59 -0
- package/index.d.ts +4 -0
- package/index.js +64 -0
- package/lib/rules/index.d.ts +6 -0
- package/lib/rules/no-restricted-components.d.ts +45 -0
- package/lib/rules/no-restricted-components.js +128 -0
- package/lib/rules/tag-attribute-restriction.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,12 @@ module.exports = {
|
|
|
47
47
|
}
|
|
48
48
|
]
|
|
49
49
|
}],
|
|
50
|
+
'@foundbyte/no-restricted-components': ['error', [
|
|
51
|
+
{
|
|
52
|
+
pattern: '/^a-(textarea|table)$/',
|
|
53
|
+
message: '禁止使用的Arco Design组件,请使用Keyblade组件替代'
|
|
54
|
+
}
|
|
55
|
+
]],
|
|
50
56
|
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 5 }],
|
|
51
57
|
'@foundbyte/no-disabled-comments': ['error', {
|
|
52
58
|
patterns: [
|
|
@@ -438,6 +444,59 @@ const x: any = 1;
|
|
|
438
444
|
- 建议只禁用特定的、有问题的规则,而不是使用无参数的 `eslint-disable` 禁用所有规则
|
|
439
445
|
- 如果确实需要排除某些文件,建议使用 `.eslintignore` 文件
|
|
440
446
|
|
|
447
|
+
### no-restricted-components
|
|
448
|
+
|
|
449
|
+
通过正则表达式配置禁止使用某些组件,支持自定义错误消息。
|
|
450
|
+
|
|
451
|
+
**配置示例:**
|
|
452
|
+
```javascript
|
|
453
|
+
'@foundbyte/no-restricted-components': ['error', [
|
|
454
|
+
{
|
|
455
|
+
pattern: '/^a-(textarea|table)$/', // 正则表达式字符串,匹配 a-textarea 和 a-input
|
|
456
|
+
message: '禁止使用 a-textarea 和 a-table 组件,请使用自定义组件替代'
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
pattern: 'kb-pro-table', // 普通字符串,自动转义为正则表达式
|
|
460
|
+
message: '禁止使用 kb-pro-table 组件,请使用其他表格组件'
|
|
461
|
+
}
|
|
462
|
+
]]
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**支持的语法:**
|
|
466
|
+
- 正则表达式字符串:以 `/` 开头和结尾,如 `/^a-(textarea|table)$/`
|
|
467
|
+
- 普通字符串:自动转义并创建正则表达式,如 `'a-textarea'`
|
|
468
|
+
- 支持自闭合标签:`<a-textarea />`
|
|
469
|
+
- 支持普通标签:`<a-textarea></a-textarea>`
|
|
470
|
+
- 支持带属性的标签:`<a-textarea v-model="value" />`
|
|
471
|
+
- 只对 .vue 文件生效,不会影响其他文件类型
|
|
472
|
+
|
|
473
|
+
**示例:**
|
|
474
|
+
```vue
|
|
475
|
+
<!-- ✅ 正确 - 不匹配任何禁止模式的组件 -->
|
|
476
|
+
<div>正常内容</div>
|
|
477
|
+
<span>正常内容</span>
|
|
478
|
+
<custom-component />
|
|
479
|
+
|
|
480
|
+
<!-- ❌ 错误 - 匹配 a-textarea 模式 -->
|
|
481
|
+
<a-textarea />
|
|
482
|
+
<a-textarea></a-textarea>
|
|
483
|
+
|
|
484
|
+
<!-- ❌ 错误 - 匹配 a-input 模式 -->
|
|
485
|
+
<a-input />
|
|
486
|
+
|
|
487
|
+
<!-- ❌ 错误 - 匹配 kb-pro-table 模式 -->
|
|
488
|
+
<kb-pro-table />
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**错误消息:**
|
|
492
|
+
- 显示配置中指定的自定义消息,如:`禁止使用 a-textarea 和 a-input 组件,请使用自定义组件替代`
|
|
493
|
+
|
|
494
|
+
**注意:**
|
|
495
|
+
- 该规则只检查 .vue 文件中的模板部分,不会检查 JavaScript/TypeScript 代码中的字符串
|
|
496
|
+
- 支持同时配置多个禁止模式,每个模式独立检查
|
|
497
|
+
- 正则表达式匹配组件名,不区分大小写
|
|
498
|
+
- 建议使用具体的组件名模式,避免过度匹配
|
|
499
|
+
|
|
441
500
|
### tag-attribute-restriction
|
|
442
501
|
|
|
443
502
|
限制特定标签使用某些属性或属性值。
|
package/index.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { ESLint } from 'eslint'
|
|
2
2
|
import { Rules } from './lib/rules'
|
|
3
|
+
import { Rule } from 'eslint'
|
|
3
4
|
|
|
4
5
|
export { ArrowFunctionPaddingOptions, HooksExportLimitOptions, TypeCommentRequirementOptions, VueScriptLinesLimitOptions, HooksArrowFunctionOnlyOptions } from './types'
|
|
5
6
|
|
|
6
7
|
declare const plugin: ESLint.Plugin & {
|
|
7
8
|
rules: Rules
|
|
9
|
+
configs: {
|
|
10
|
+
customize: (options?: Record<string, any>) => { rules: Rule }
|
|
11
|
+
}
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
export { type Rules, plugin as default }
|
package/index.js
CHANGED
|
@@ -7,6 +7,26 @@ const tagAttributeRestrictionRule = require('./lib/rules/tag-attribute-restricti
|
|
|
7
7
|
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
|
+
const noRestrictedComponentsRule = require('./lib/rules/no-restricted-components')
|
|
11
|
+
|
|
12
|
+
// 限制属性,同时相当于限制某些组件禁止使用
|
|
13
|
+
const restrictions = [
|
|
14
|
+
{
|
|
15
|
+
// 禁止<image />直接使用空的mode: <image mode="" />
|
|
16
|
+
tag: 'image',
|
|
17
|
+
attributes: [
|
|
18
|
+
{ name: 'mode', disallowedValues: [''] },
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
// 禁止<kb-pro-table /> 使用v-bind,请使用main.ts全局配置
|
|
23
|
+
tag: 'kb-pro-table',
|
|
24
|
+
attributes: [
|
|
25
|
+
{ name: 'v-bind' },
|
|
26
|
+
{ name: 'bordered' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
]
|
|
10
30
|
|
|
11
31
|
module.exports = {
|
|
12
32
|
rules: {
|
|
@@ -19,5 +39,49 @@ module.exports = {
|
|
|
19
39
|
'no-initial-as-assertion': noInitialAsAssertionRule,
|
|
20
40
|
'no-excessive-any': noExcessiveAnyRule,
|
|
21
41
|
'no-disabled-comments': noDisabledCommentsRule,
|
|
42
|
+
'no-restricted-components': noRestrictedComponentsRule,
|
|
22
43
|
},
|
|
44
|
+
configs: {
|
|
45
|
+
customize: (options) => {
|
|
46
|
+
return {
|
|
47
|
+
rules: {
|
|
48
|
+
'@foundbyte/hooks-arrow-function-only': 'error',
|
|
49
|
+
'@foundbyte/arrow-function-padding': 'error',
|
|
50
|
+
'@foundbyte/vue-script-lines-limit': ['error', { maxLines: 300 }],
|
|
51
|
+
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 5 }],
|
|
52
|
+
'@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', [
|
|
59
|
+
{
|
|
60
|
+
pattern: '/^a-(textarea|table)$/',
|
|
61
|
+
message: '禁止使用的Arco Design组件,请使用Keyblade组件替代'
|
|
62
|
+
}
|
|
63
|
+
]],
|
|
64
|
+
'@foundbyte/tag-attribute-restriction': ['error', { restrictions }],
|
|
65
|
+
// 类型加注释
|
|
66
|
+
'@foundbyte/type-comment-requirement': ['error', {
|
|
67
|
+
minCommentsPerGroup: 1,
|
|
68
|
+
groupSize: 8,
|
|
69
|
+
includeTypes: true,
|
|
70
|
+
includeInterfaces: true,
|
|
71
|
+
}],
|
|
72
|
+
// 禁止使用某些注释
|
|
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
|
+
}],
|
|
82
|
+
...(options || {}),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
23
87
|
}
|
package/lib/rules/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { TagAttributeRestrictionOptions } from './tag-attribute-restriction'
|
|
|
7
7
|
export { NoInitialAsAssertionOptions } from './no-initial-as-assertion'
|
|
8
8
|
export { NoExcessiveAnyOptions } from './no-excessive-any'
|
|
9
9
|
export { NoDisabledCommentsOptions } from './no-disabled-comments'
|
|
10
|
+
export { NoRestrictedComponentsOptions } from './no-restricted-components'
|
|
10
11
|
|
|
11
12
|
import { Rule } from 'eslint'
|
|
12
13
|
|
|
@@ -59,6 +60,11 @@ export interface Rules {
|
|
|
59
60
|
*/
|
|
60
61
|
'no-disabled-comments': Rule.RuleModule
|
|
61
62
|
|
|
63
|
+
/**
|
|
64
|
+
* 通过正则配置禁止使用某些组件
|
|
65
|
+
*/
|
|
66
|
+
'no-restricted-components': Rule.RuleModule
|
|
67
|
+
|
|
62
68
|
/**
|
|
63
69
|
* 索引签名,用于兼容 ESLint 插件类型
|
|
64
70
|
*/
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Rule } from 'eslint'
|
|
2
|
+
|
|
3
|
+
export interface NoRestrictedComponentsOptions {
|
|
4
|
+
pattern: string
|
|
5
|
+
message: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface NoRestrictedComponentsRule extends Rule.RuleModule {
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'problem'
|
|
11
|
+
docs: {
|
|
12
|
+
description: '通过正则配置禁止使用某些组件'
|
|
13
|
+
category: 'Best Practices'
|
|
14
|
+
recommended: false
|
|
15
|
+
}
|
|
16
|
+
fixable: null
|
|
17
|
+
schema: [
|
|
18
|
+
{
|
|
19
|
+
type: 'array'
|
|
20
|
+
items: {
|
|
21
|
+
type: 'object'
|
|
22
|
+
properties: {
|
|
23
|
+
pattern: {
|
|
24
|
+
type: 'string'
|
|
25
|
+
description: '用于匹配组件名的正则表达式字符串'
|
|
26
|
+
}
|
|
27
|
+
message: {
|
|
28
|
+
type: 'string'
|
|
29
|
+
description: '自定义错误消息'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
required: ['pattern', 'message']
|
|
33
|
+
additionalProperties: false
|
|
34
|
+
}
|
|
35
|
+
minItems: 1
|
|
36
|
+
},
|
|
37
|
+
]
|
|
38
|
+
messages: {
|
|
39
|
+
restrictedComponent: string
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
declare const rule: NoRestrictedComponentsRule
|
|
45
|
+
export default rule
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: 'problem',
|
|
4
|
+
docs: {
|
|
5
|
+
description: '通过正则配置禁止使用某些组件',
|
|
6
|
+
category: 'Best Practices',
|
|
7
|
+
recommended: false,
|
|
8
|
+
},
|
|
9
|
+
fixable: null,
|
|
10
|
+
schema: [
|
|
11
|
+
{
|
|
12
|
+
type: 'array',
|
|
13
|
+
items: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
pattern: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: '用于匹配组件名的正则表达式字符串',
|
|
19
|
+
},
|
|
20
|
+
message: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: '自定义错误消息',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ['pattern', 'message'],
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
},
|
|
28
|
+
minItems: 1,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
messages: {
|
|
32
|
+
restrictedComponent: '{{message}}',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
create(context) {
|
|
37
|
+
const options = context.options[0] || [];
|
|
38
|
+
|
|
39
|
+
// 如果没有配置,则不进行检查
|
|
40
|
+
if (options.length === 0) {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 将字符串模式转换为正则表达式
|
|
45
|
+
const patterns = options.map(option => {
|
|
46
|
+
const pattern = option.pattern;
|
|
47
|
+
const message = option.message;
|
|
48
|
+
|
|
49
|
+
// 如果模式已经是正则表达式字符串,如 "/a-textarea/"
|
|
50
|
+
if (pattern.startsWith('/') && pattern.endsWith('/')) {
|
|
51
|
+
const regexBody = pattern.slice(1, -1);
|
|
52
|
+
return {
|
|
53
|
+
regex: new RegExp(regexBody),
|
|
54
|
+
message,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 普通字符串,进行转义并创建正则表达式
|
|
59
|
+
const escapedPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
60
|
+
return {
|
|
61
|
+
regex: new RegExp(escapedPattern),
|
|
62
|
+
message,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// 获取文件名,只对 .vue 文件生效
|
|
67
|
+
const filename = context.getFilename();
|
|
68
|
+
if (!filename.endsWith('.vue')) {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
Program(node) {
|
|
74
|
+
// 获取整个文件的源代码
|
|
75
|
+
const sourceCode = context.getSourceCode();
|
|
76
|
+
const text = sourceCode.getText();
|
|
77
|
+
|
|
78
|
+
// 使用正则表达式匹配模板中的标签
|
|
79
|
+
// 匹配自闭合标签:<component />
|
|
80
|
+
// 匹配普通标签:<component> 或 <component attr="value">
|
|
81
|
+
const tagRegex = /<([\w-]+)(?:\s+[^>]*)?\/?>|<\/([\w-]+)>/g;
|
|
82
|
+
let match;
|
|
83
|
+
|
|
84
|
+
while ((match = tagRegex.exec(text)) !== null) {
|
|
85
|
+
// match[1] 是开始标签名,match[2] 是结束标签名
|
|
86
|
+
const tagName = match[1] || match[2];
|
|
87
|
+
|
|
88
|
+
if (tagName) {
|
|
89
|
+
// 检查标签名是否匹配任何禁止模式
|
|
90
|
+
patterns.forEach(({ regex, message }) => {
|
|
91
|
+
if (regex.test(tagName)) {
|
|
92
|
+
context.report({
|
|
93
|
+
loc: {
|
|
94
|
+
start: sourceCode.getLocFromIndex(match.index),
|
|
95
|
+
end: sourceCode.getLocFromIndex(match.index + match[0].length),
|
|
96
|
+
},
|
|
97
|
+
messageId: 'restrictedComponent',
|
|
98
|
+
data: {
|
|
99
|
+
message,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
JSXOpeningElement(node) {
|
|
109
|
+
// 处理 JSX 元素
|
|
110
|
+
if (node.name.type === 'JSXIdentifier') {
|
|
111
|
+
const tagName = node.name.name;
|
|
112
|
+
|
|
113
|
+
patterns.forEach(({ regex, message }) => {
|
|
114
|
+
if (regex.test(tagName)) {
|
|
115
|
+
context.report({
|
|
116
|
+
node,
|
|
117
|
+
messageId: 'restrictedComponent',
|
|
118
|
+
data: {
|
|
119
|
+
message,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foundbyte/eslint-plugin",
|
|
3
|
-
"version": "1.1.4-alpha.
|
|
3
|
+
"version": "1.1.4-alpha.7",
|
|
4
4
|
"description": "ESLint plugin for enforcing code style rules including arrow function padding, Vue script lines limit, and TypeScript type comment requirements",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|