@ai0x0/utils 0.0.35 → 0.0.37
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 +13 -13
- package/eslint-rules/index.js +3 -1
- package/eslint-rules/max-lines.js +117 -0
- package/eslint-rules/require-section-divider.js +5 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,19 +63,19 @@ export default [
|
|
|
63
63
|
];
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
| Rule | Description
|
|
67
|
-
| ------------------------- |
|
|
68
|
-
| `no-hardcoded-style` | Forbid hardcoded style values
|
|
69
|
-
| `no-one-letter-vars` | Forbid single-letter variable names
|
|
70
|
-
| `no-then` | Prefer `async/await` over `.then()`
|
|
71
|
-
| `no-antd-space` | Use `<Flex>` instead of `<Space>`
|
|
72
|
-
| `require-pro-components` | Require ProComponents usage
|
|
73
|
-
| `require-section-divider` | Large files need `===` comment dividers
|
|
74
|
-
| `no-consecutive-setstate` | Merge consecutive `setState` calls
|
|
75
|
-
| `no-use-request-run` | Forbid `useRequest.run()`
|
|
76
|
-
| `require-use-form` | Use `useForm()` for form state
|
|
77
|
-
| `require-form-convention` | Form naming conventions
|
|
78
|
-
| `max-lines` | Limit each file to 500 code lines
|
|
66
|
+
| Rule | Description |
|
|
67
|
+
| ------------------------- | ----------------------------------------------------------------------- |
|
|
68
|
+
| `no-hardcoded-style` | Forbid hardcoded style values |
|
|
69
|
+
| `no-one-letter-vars` | Forbid single-letter variable names |
|
|
70
|
+
| `no-then` | Prefer `async/await` over `.then()` |
|
|
71
|
+
| `no-antd-space` | Use `<Flex>` instead of `<Space>` |
|
|
72
|
+
| `require-pro-components` | Require ProComponents usage |
|
|
73
|
+
| `require-section-divider` | Large files need `===` comment dividers |
|
|
74
|
+
| `no-consecutive-setstate` | Merge consecutive `setState` calls |
|
|
75
|
+
| `no-use-request-run` | Forbid `useRequest.run()` |
|
|
76
|
+
| `require-use-form` | Use `useForm()` for form state |
|
|
77
|
+
| `require-form-convention` | Form naming conventions |
|
|
78
|
+
| `max-lines` | Limit each file to 500 code lines and prompt splitting into child files |
|
|
79
79
|
|
|
80
80
|
### Shared restrictions (`eslint-config/`)
|
|
81
81
|
|
package/eslint-rules/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import noUseRequestRun from "./no-use-request-run.js";
|
|
|
11
11
|
import noThen from "./no-then.js";
|
|
12
12
|
import noOneLetterVars from "./no-one-letter-vars.js";
|
|
13
13
|
import noConsecutiveSetState from "./no-consecutive-setstate.js";
|
|
14
|
+
import maxLines from "./max-lines.js";
|
|
14
15
|
|
|
15
16
|
const rules = {
|
|
16
17
|
"require-section-divider": requireSectionDivider,
|
|
@@ -23,6 +24,7 @@ const rules = {
|
|
|
23
24
|
"no-then": noThen,
|
|
24
25
|
"no-one-letter-vars": noOneLetterVars,
|
|
25
26
|
"no-consecutive-setstate": noConsecutiveSetState,
|
|
27
|
+
"max-lines": maxLines,
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
const plugin = {
|
|
@@ -35,7 +37,7 @@ const plugin = {
|
|
|
35
37
|
Object.keys(rules).map((name) => [`ai0x0/${name}`, "error"]),
|
|
36
38
|
),
|
|
37
39
|
curly: "error",
|
|
38
|
-
"max-lines": [
|
|
40
|
+
"ai0x0/max-lines": [
|
|
39
41
|
"error",
|
|
40
42
|
{ max: 500, skipBlankLines: true, skipComments: true },
|
|
41
43
|
],
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const DEFAULT_MAX = 500;
|
|
2
|
+
|
|
3
|
+
function normalizeOptions(options = {}) {
|
|
4
|
+
if (typeof options === "number") {
|
|
5
|
+
return {
|
|
6
|
+
max: options,
|
|
7
|
+
skipBlankLines: true,
|
|
8
|
+
skipComments: true,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
max: options.max ?? DEFAULT_MAX,
|
|
13
|
+
skipBlankLines: options.skipBlankLines ?? true,
|
|
14
|
+
skipComments: options.skipComments ?? true,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getCommentLines(sourceCode) {
|
|
19
|
+
const lines = new Set();
|
|
20
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
21
|
+
const start = comment.loc.start.line;
|
|
22
|
+
const end = comment.loc.end.line;
|
|
23
|
+
for (let line = start; line <= end; line++) {
|
|
24
|
+
lines.add(line);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return lines;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
31
|
+
const rule = {
|
|
32
|
+
meta: {
|
|
33
|
+
type: "layout",
|
|
34
|
+
docs: {
|
|
35
|
+
description: "限制单文件代码行数,超过时提示拆分子文件。",
|
|
36
|
+
},
|
|
37
|
+
schema: [
|
|
38
|
+
{
|
|
39
|
+
oneOf: [
|
|
40
|
+
{
|
|
41
|
+
type: "integer",
|
|
42
|
+
minimum: 0,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: "object",
|
|
46
|
+
additionalProperties: false,
|
|
47
|
+
properties: {
|
|
48
|
+
max: {
|
|
49
|
+
type: "integer",
|
|
50
|
+
minimum: 0,
|
|
51
|
+
},
|
|
52
|
+
skipBlankLines: {
|
|
53
|
+
type: "boolean",
|
|
54
|
+
},
|
|
55
|
+
skipComments: {
|
|
56
|
+
type: "boolean",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
messages: {
|
|
64
|
+
tooManyLines:
|
|
65
|
+
"单文件代码行数过多({{ lineCount }} 行,最多 {{ max }} 行),请拆分为子文件。",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
create(context) {
|
|
69
|
+
const options = normalizeOptions(context.options[0]);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
"Program:exit"(node) {
|
|
73
|
+
const sourceCode = context.getSourceCode();
|
|
74
|
+
const commentLines = options.skipComments
|
|
75
|
+
? getCommentLines(sourceCode)
|
|
76
|
+
: new Set();
|
|
77
|
+
let lineCount = 0;
|
|
78
|
+
let reportLine = sourceCode.lines.length || 1;
|
|
79
|
+
|
|
80
|
+
for (let index = 0; index < sourceCode.lines.length; index++) {
|
|
81
|
+
const lineNumber = index + 1;
|
|
82
|
+
const line = sourceCode.lines[index];
|
|
83
|
+
if (options.skipBlankLines && line.trim() === "") {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (options.skipComments && commentLines.has(lineNumber)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
lineCount++;
|
|
91
|
+
if (lineCount === options.max + 1) {
|
|
92
|
+
reportLine = lineNumber;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (lineCount <= options.max) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
context.report({
|
|
101
|
+
node,
|
|
102
|
+
loc: {
|
|
103
|
+
line: reportLine,
|
|
104
|
+
column: 0,
|
|
105
|
+
},
|
|
106
|
+
messageId: "tooManyLines",
|
|
107
|
+
data: {
|
|
108
|
+
lineCount,
|
|
109
|
+
max: options.max,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default rule;
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
* ------------------------
|
|
4
4
|
* 约束:大文件必须在顶层用 `=====` 等号注释块做分区。
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* 以此类推,每 200 行增加 1 个分区块。
|
|
6
|
+
* 分区要求:
|
|
7
|
+
* - 行数只作为最低检查下限
|
|
8
|
+
* - AI 应按业务模块、状态、数据流、工具函数等结构尽量多加分区注释
|
|
9
|
+
* - 不要只按“150 行一个”机械补齐
|
|
11
10
|
*
|
|
12
11
|
* 分隔注释块格式(见 node_modules/@ai0x0/utils/.agents/skills/nextjs/SKILL.md):
|
|
13
12
|
*
|
|
@@ -78,7 +77,7 @@ const rule = {
|
|
|
78
77
|
],
|
|
79
78
|
messages: {
|
|
80
79
|
missing:
|
|
81
|
-
"大文件需要更多 `===`
|
|
80
|
+
"大文件需要更多 `===` 等号注释块分区。当前最低需要 {{required}} 个,实际只有 {{actual}} 个。请 AI 按业务模块尽量多加分区注释,不要只按 150 行一个机械补齐。",
|
|
82
81
|
},
|
|
83
82
|
},
|
|
84
83
|
create(context) {
|