@foundbyte/eslint-plugin 1.0.0 → 1.0.2

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/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  const arrowFunctionPaddingRule = require('./lib/rules/arrow-function-padding');
2
+ const vueScriptLinesLimitRule = require('./lib/rules/vue-script-lines-limit');
3
+ const typeCommentRequirementRule = require('./lib/rules/type-comment-requirement');
2
4
 
3
5
  module.exports = {
4
6
  rules: {
5
7
  'arrow-function-padding': arrowFunctionPaddingRule,
8
+ 'vue-script-lines-limit': vueScriptLinesLimitRule,
9
+ 'type-comment-requirement': typeCommentRequirementRule,
6
10
  },
7
11
  };
@@ -0,0 +1,152 @@
1
+ module.exports = {
2
+ meta: {
3
+ type: 'suggestion',
4
+ docs: {
5
+ description: '要求 TypeScript interface 和 type 定义中每 groupSize 个字段至少有 minCommentsPerGroup 个字段必须加注释',
6
+ category: 'Best Practices',
7
+ recommended: false,
8
+ },
9
+ fixable: null,
10
+ schema: [
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ groupSize: {
15
+ type: 'number',
16
+ minimum: 1,
17
+ description: '每多少个字段为一组进行检查',
18
+ },
19
+ minCommentsPerGroup: {
20
+ type: 'number',
21
+ minimum: 0,
22
+ description: '每组中至少有多少个字段必须加注释',
23
+ },
24
+ includeTypes: {
25
+ type: 'boolean',
26
+ description: '是否检查 type 定义',
27
+ default: true,
28
+ },
29
+ includeInterfaces: {
30
+ type: 'boolean',
31
+ description: '是否检查 interface 定义',
32
+ default: true,
33
+ },
34
+ },
35
+ additionalProperties: false,
36
+ },
37
+ ],
38
+ messages: {
39
+ missingComments: '{{typeName}} 中的字段组({{groupStart}} 到 {{groupEnd}})需要至少 {{requiredCount}} 个字段有注释,当前只有 {{currentCount}} 个字段有注释',
40
+ },
41
+ },
42
+
43
+ create(context) {
44
+ const options = context.options[0] || {};
45
+ const groupSize = options.groupSize || 5; // 默认每5个字段为一组
46
+ const minCommentsPerGroup = options.minCommentsPerGroup || 1; // 默认每组至少1个字段有注释
47
+ const includeTypes = options.includeTypes !== false;
48
+ const includeInterfaces = options.includeInterfaces !== false;
49
+
50
+ /**
51
+ * 检查字段是否有注释
52
+ * @param {Object} node AST节点
53
+ * @returns {boolean} 是否有注释
54
+ */
55
+ function hasComment(node) {
56
+ const sourceCode = context.getSourceCode();
57
+ const comments = sourceCode.getCommentsBefore(node);
58
+
59
+ // 检查是否有任何类型的注释(行注释 // 或块注释 /** */)
60
+ return comments.length > 0;
61
+ }
62
+
63
+ /**
64
+ * 检查字段组中的注释要求
65
+ * @param {Array} properties 字段数组
66
+ * @param {string} typeName 类型名称
67
+ * @param {string} parentPath 父级路径,用于嵌套类型
68
+ */
69
+ function checkCommentRequirement(properties, typeName, parentPath = '') {
70
+ if (properties.length === 0) return;
71
+
72
+ // 如果字段数量少于 groupSize,则不要求必须有注释
73
+ if (properties.length < groupSize) {
74
+ return;
75
+ }
76
+
77
+ // 计算总共有多少个字段组
78
+ const totalGroups = Math.ceil(properties.length / groupSize);
79
+
80
+ // 计算总共需要多少个有注释的字段
81
+ const totalRequiredComments = totalGroups * minCommentsPerGroup;
82
+
83
+ // 计算当前有注释的字段数量
84
+ const commentedFields = properties.filter(field => hasComment(field));
85
+ const currentCommentCount = commentedFields.length;
86
+
87
+ // 如果当前有注释的字段数量不足
88
+ if (currentCommentCount < totalRequiredComments) {
89
+ const fullTypeName = parentPath ? `${typeName}${parentPath}` : typeName;
90
+ context.report({
91
+ node: properties[0],
92
+ messageId: 'missingComments',
93
+ data: {
94
+ typeName: fullTypeName,
95
+ groupStart: 1,
96
+ groupEnd: properties.length,
97
+ requiredCount: totalRequiredComments,
98
+ currentCount: currentCommentCount,
99
+ },
100
+ });
101
+ }
102
+
103
+ // 递归检查嵌套的对象类型
104
+ properties.forEach((property, index) => {
105
+ // 检查是否是属性签名
106
+ if (property.type === 'TSPropertySignature' && property.typeAnnotation) {
107
+ let typeAnnotation = property.typeAnnotation;
108
+
109
+ // 处理 typeAnnotation 可能是 TSTypeAnnotation 的情况
110
+ if (typeAnnotation.type === 'TSTypeAnnotation') {
111
+ typeAnnotation = typeAnnotation.typeAnnotation;
112
+ }
113
+
114
+ // 检查是否是嵌套的对象类型
115
+ if (typeAnnotation && typeAnnotation.type === 'TSTypeLiteral') {
116
+ const nestedProperties = typeAnnotation.members || [];
117
+ const propertyName = property.key.name || `[${index}]`;
118
+ const nestedPath = parentPath ? `${parentPath}.${propertyName}` : `.${propertyName}`;
119
+
120
+ checkCommentRequirement(nestedProperties, typeName, nestedPath);
121
+ }
122
+ }
123
+ });
124
+ }
125
+
126
+ const visitors = {};
127
+
128
+ // 检查 TypeScript interface 定义
129
+ if (includeInterfaces) {
130
+ visitors.TSInterfaceDeclaration = function(node) {
131
+ const typeName = node.id.name;
132
+ const properties = node.body.body || [];
133
+ checkCommentRequirement(properties, `interface ${typeName}`);
134
+ };
135
+ }
136
+
137
+ // 检查 TypeScript type 定义
138
+ if (includeTypes) {
139
+ visitors.TSTypeAliasDeclaration = function(node) {
140
+ const typeName = node.id.name;
141
+
142
+ // 检查是否是对象类型
143
+ if (node.typeAnnotation && node.typeAnnotation.type === 'TSTypeLiteral') {
144
+ const properties = node.typeAnnotation.members || [];
145
+ checkCommentRequirement(properties, `type ${typeName}`);
146
+ }
147
+ };
148
+ }
149
+
150
+ return visitors;
151
+ },
152
+ };
@@ -0,0 +1,76 @@
1
+ module.exports = {
2
+ meta: {
3
+ type: 'problem',
4
+ docs: {
5
+ description: '限制 Vue 文件 script 标签内的行数',
6
+ category: 'Best Practices',
7
+ recommended: false,
8
+ },
9
+ fixable: null,
10
+ schema: [
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ maxLines: {
15
+ type: 'number',
16
+ minimum: 1,
17
+ },
18
+ },
19
+ additionalProperties: false,
20
+ },
21
+ ],
22
+ messages: {
23
+ exceedMaxLines: 'Vue 文件 script 标签内的代码行数不能超过 {{maxLines}} 行,当前有 {{currentLines}} 行',
24
+ },
25
+ },
26
+
27
+ create(context) {
28
+ const options = context.options[0] || {};
29
+ const maxLines = options.maxLines || 300;
30
+
31
+ // 只处理 .vue 文件
32
+ const filename = context.getFilename();
33
+ if (!filename.endsWith('.vue')) {
34
+ return {};
35
+ }
36
+
37
+ return {
38
+ Program(node) {
39
+ // 获取整个文件的源代码
40
+ const sourceCode = context.getSourceCode();
41
+ const text = sourceCode.getText();
42
+
43
+ // 使用正则表达式匹配 script 标签
44
+ const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
45
+ let match;
46
+
47
+ while ((match = scriptRegex.exec(text)) !== null) {
48
+ const scriptContent = match[1];
49
+
50
+ // 计算 script 内容中的行数
51
+ const lines = scriptContent.split('\n').filter(line => line.trim() !== '');
52
+ const lineCount = lines.length;
53
+
54
+ if (lineCount > maxLines) {
55
+ // 找到 script 标签在 AST 中的位置
56
+ const scriptStartIndex = match.index;
57
+ const scriptEndIndex = scriptStartIndex + match[0].length;
58
+
59
+ // 在 script 标签的位置报告错误
60
+ context.report({
61
+ loc: {
62
+ start: sourceCode.getLocFromIndex(scriptStartIndex),
63
+ end: sourceCode.getLocFromIndex(scriptEndIndex),
64
+ },
65
+ messageId: 'exceedMaxLines',
66
+ data: {
67
+ maxLines,
68
+ currentLines: lineCount,
69
+ },
70
+ });
71
+ }
72
+ }
73
+ },
74
+ };
75
+ },
76
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@foundbyte/eslint-plugin",
3
- "version": "1.0.0",
4
- "description": "ESLint plugin for enforcing padding between arrow function declarations",
3
+ "version": "1.0.2",
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
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -10,7 +10,13 @@
10
10
  "eslint",
11
11
  "eslintplugin",
12
12
  "arrow-function",
13
- "padding"
13
+ "padding",
14
+ "vue",
15
+ "script",
16
+ "lines-limit",
17
+ "typescript",
18
+ "type-comment",
19
+ "comment-requirement"
14
20
  ],
15
21
  "author": "",
16
22
  "license": "ISC",