auto-cr-cmd 2.0.26 → 2.0.28
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 -4
- package/README.zh-CN.md +59 -4
- package/dist/index.js +210 -35
- package/dist/report/index.js +59 -35
- package/dist/types/report/index.d.ts +22 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -42,15 +42,70 @@ Common flags:
|
|
|
42
42
|
|
|
43
43
|
- `--language <zh|en>`: Switch CLI output language (defaults to auto-detection).
|
|
44
44
|
- `--rule-dir <directory>`: Load additional custom rules from a directory or package.
|
|
45
|
+
- `--output <text|json>`: Choose between human-friendly text logs or structured JSON results (defaults to `text`).
|
|
45
46
|
- `--help`: Display the full command reference.
|
|
46
47
|
|
|
47
48
|
Sample output:
|
|
48
49
|
|
|
49
50
|
```text
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
WARN [12:56:18] ⚠️ [Base Rules]: no-deep-relative-imports
|
|
52
|
+
|
|
53
|
+
File: /Volumes/Wei/Codes/github/auto-cr/examples/src/app/features/admin/pages/dashboard.ts:2
|
|
54
|
+
Description: Import path "../../../../shared/deep/utils" must not exceed max depth 2
|
|
55
|
+
Code: ../../../../shared/deep/utils
|
|
56
|
+
Suggestion: Use a path alias (for example: @shared/deep/utils). | Create an index file at a higher level to re-export the module and shorten the import.
|
|
57
|
+
|
|
58
|
+
WARN [12:56:18] ⚠️ [untagged]: no-index-import
|
|
59
|
+
|
|
60
|
+
File: /Volumes/Wei/Codes/github/auto-cr/examples/src/app/features/admin/pages/dashboard.ts:3
|
|
61
|
+
Description: Import ../../consts/index is not allowed. Import the concrete file instead.
|
|
62
|
+
|
|
63
|
+
✔ Code scan complete, scanned 3 files: 0 with errors, 1 with warnings, 0 with optimizing hints!
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
JSON output sample:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx auto-cr-cmd --output json -- ./src | jq
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"summary": {
|
|
75
|
+
"scannedFiles": 2,
|
|
76
|
+
"filesWithErrors": 1,
|
|
77
|
+
"filesWithWarnings": 0,
|
|
78
|
+
"filesWithOptimizing": 1,
|
|
79
|
+
"violationTotals": {
|
|
80
|
+
"total": 3,
|
|
81
|
+
"error": 2,
|
|
82
|
+
"warning": 0,
|
|
83
|
+
"optimizing": 1
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"files": [
|
|
87
|
+
{
|
|
88
|
+
"filePath": "/workspace/src/example.ts",
|
|
89
|
+
"severityCounts": {
|
|
90
|
+
"error": 2,
|
|
91
|
+
"warning": 0,
|
|
92
|
+
"optimizing": 1
|
|
93
|
+
},
|
|
94
|
+
"totalViolations": 3,
|
|
95
|
+
"errorViolations": 2,
|
|
96
|
+
"violations": [
|
|
97
|
+
{
|
|
98
|
+
"tag": "imports",
|
|
99
|
+
"ruleName": "no-deep-relative-imports",
|
|
100
|
+
"severity": "error",
|
|
101
|
+
"message": "Avoid deep relative imports from src/components/button",
|
|
102
|
+
"line": 13
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"notifications": []
|
|
108
|
+
}
|
|
54
109
|
```
|
|
55
110
|
|
|
56
111
|
## Writing Custom Rules
|
package/README.zh-CN.md
CHANGED
|
@@ -42,15 +42,70 @@ npx auto-cr-cmd --language zh [需要扫描的代码目录]
|
|
|
42
42
|
|
|
43
43
|
- `--language <zh|en>`:切换 CLI 输出语言(默认为自动检测)。
|
|
44
44
|
- `--rule-dir <directory>`:加载额外的自定义规则目录或包。
|
|
45
|
+
- `--output <text|json>`:选择输出格式,`text` 为友好的终端日志,`json` 用于集成脚本(默认为 `text`)。
|
|
45
46
|
- `--help`:查看完整命令说明。
|
|
46
47
|
|
|
47
48
|
示例输出:
|
|
48
49
|
|
|
49
50
|
```text
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
WARN [12:52:48] ⚠️ [基础规则]:no-deep-relative-imports
|
|
52
|
+
|
|
53
|
+
文件位置: .../dashboard.ts:2
|
|
54
|
+
错误描述: 导入路径 "../../../../shared/deep/utils",不能超过最大层级2
|
|
55
|
+
错误代码: ../../../../shared/deep/utils
|
|
56
|
+
优化建议: 使用别名路径(如 @shared/deep/utils); 或在上层聚合导出,避免过深相对路径。
|
|
57
|
+
|
|
58
|
+
WARN [12:52:48] ⚠️ [未定义]:no-index-import
|
|
59
|
+
|
|
60
|
+
文件位置: .../dashboard.ts:3
|
|
61
|
+
错误描述: 禁止直接导入 ../../consts/index,请改用具体文件
|
|
62
|
+
|
|
63
|
+
✔ 代码扫描完成,本次共扫描3个文件,其中0个文件存在错误,1个文件存在警告,0个文件存在优化建议!
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
JSON 输出示例:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx auto-cr-cmd --output json -- ./src | jq
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"summary": {
|
|
75
|
+
"scannedFiles": 2,
|
|
76
|
+
"filesWithErrors": 1,
|
|
77
|
+
"filesWithWarnings": 0,
|
|
78
|
+
"filesWithOptimizing": 1,
|
|
79
|
+
"violationTotals": {
|
|
80
|
+
"total": 3,
|
|
81
|
+
"error": 2,
|
|
82
|
+
"warning": 0,
|
|
83
|
+
"optimizing": 1
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"files": [
|
|
87
|
+
{
|
|
88
|
+
"filePath": "/workspace/src/example.ts",
|
|
89
|
+
"severityCounts": {
|
|
90
|
+
"error": 2,
|
|
91
|
+
"warning": 0,
|
|
92
|
+
"optimizing": 1
|
|
93
|
+
},
|
|
94
|
+
"totalViolations": 3,
|
|
95
|
+
"errorViolations": 2,
|
|
96
|
+
"violations": [
|
|
97
|
+
{
|
|
98
|
+
"tag": "imports",
|
|
99
|
+
"ruleName": "no-deep-relative-imports",
|
|
100
|
+
"severity": "error",
|
|
101
|
+
"message": "避免从 src/components/button 进行深层相对导入",
|
|
102
|
+
"line": 13
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"notifications": []
|
|
108
|
+
}
|
|
54
109
|
```
|
|
55
110
|
|
|
56
111
|
## 编写自定义规则
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
59
59
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
60
60
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
61
61
|
};
|
|
62
|
+
var _a;
|
|
62
63
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
63
64
|
var consola_1 = require("consola");
|
|
64
65
|
var fs_1 = __importDefault(require("fs"));
|
|
@@ -71,25 +72,76 @@ var i18n_1 = require("./i18n");
|
|
|
71
72
|
var file_1 = require("./utils/file");
|
|
72
73
|
var auto_cr_rules_1 = require("auto-cr-rules");
|
|
73
74
|
var loader_1 = require("./rules/loader");
|
|
75
|
+
var consolaLoggers = {
|
|
76
|
+
info: consola_1.consola.info.bind(consola_1.consola),
|
|
77
|
+
warn: consola_1.consola.warn.bind(consola_1.consola),
|
|
78
|
+
error: consola_1.consola.error.bind(consola_1.consola),
|
|
79
|
+
};
|
|
74
80
|
function run() {
|
|
75
|
-
return __awaiter(this, arguments, void 0, function (filePaths, ruleDir) {
|
|
76
|
-
var t, validPaths, allFiles, _i, validPaths_1, targetPath, stat, directoryFiles, scannableFiles, customRules, rules, filesWithErrors, filesWithWarnings, filesWithOptimizing, _a, scannableFiles_1, file, summary, error_1;
|
|
81
|
+
return __awaiter(this, arguments, void 0, function (filePaths, ruleDir, format) {
|
|
82
|
+
var t, notifications, log, validPaths, allFiles, _i, validPaths_1, targetPath, stat, directoryFiles, scannableFiles, customRules, rules, filesWithErrors, filesWithWarnings, filesWithOptimizing, totalViolations, totalErrorViolations, totalWarningViolations, totalOptimizingViolations, fileSummaries, _a, scannableFiles_1, file, summary, error_1;
|
|
77
83
|
if (filePaths === void 0) { filePaths = []; }
|
|
78
84
|
return __generator(this, function (_b) {
|
|
79
85
|
switch (_b.label) {
|
|
80
86
|
case 0:
|
|
81
87
|
t = (0, i18n_1.getTranslator)();
|
|
88
|
+
notifications = [];
|
|
89
|
+
log = function (level, message, detail) {
|
|
90
|
+
var detailText;
|
|
91
|
+
if (detail !== undefined) {
|
|
92
|
+
if (detail instanceof Error) {
|
|
93
|
+
detailText = detail.message;
|
|
94
|
+
}
|
|
95
|
+
else if (typeof detail === 'string') {
|
|
96
|
+
detailText = detail;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
try {
|
|
100
|
+
detailText = JSON.stringify(detail);
|
|
101
|
+
}
|
|
102
|
+
catch (_a) {
|
|
103
|
+
detailText = String(detail);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
notifications.push({ level: level, message: message, detail: detailText });
|
|
108
|
+
if (format === 'text') {
|
|
109
|
+
var logger = consolaLoggers[level];
|
|
110
|
+
if (detail === undefined) {
|
|
111
|
+
logger(message);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
logger(message, detail);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
82
118
|
_b.label = 1;
|
|
83
119
|
case 1:
|
|
84
120
|
_b.trys.push([1, 6, , 7]);
|
|
85
121
|
if (filePaths.length === 0) {
|
|
86
|
-
|
|
87
|
-
return [2 /*return*/, {
|
|
122
|
+
log('info', t.noPathsProvided());
|
|
123
|
+
return [2 /*return*/, {
|
|
124
|
+
scannedFiles: 0,
|
|
125
|
+
filesWithErrors: 0,
|
|
126
|
+
filesWithWarnings: 0,
|
|
127
|
+
filesWithOptimizing: 0,
|
|
128
|
+
violationTotals: { total: 0, error: 0, warning: 0, optimizing: 0 },
|
|
129
|
+
files: [],
|
|
130
|
+
notifications: notifications,
|
|
131
|
+
}];
|
|
88
132
|
}
|
|
89
133
|
validPaths = filePaths.filter(function (candidate) { return (0, file_1.checkPathExists)(candidate); });
|
|
90
134
|
if (validPaths.length === 0) {
|
|
91
|
-
|
|
92
|
-
return [2 /*return*/, {
|
|
135
|
+
log('error', t.allPathsMissing());
|
|
136
|
+
return [2 /*return*/, {
|
|
137
|
+
scannedFiles: 0,
|
|
138
|
+
filesWithErrors: 0,
|
|
139
|
+
filesWithWarnings: 0,
|
|
140
|
+
filesWithOptimizing: 0,
|
|
141
|
+
violationTotals: { total: 0, error: 0, warning: 0, optimizing: 0 },
|
|
142
|
+
files: [],
|
|
143
|
+
notifications: notifications,
|
|
144
|
+
}];
|
|
93
145
|
}
|
|
94
146
|
allFiles = [];
|
|
95
147
|
for (_i = 0, validPaths_1 = validPaths; _i < validPaths_1.length; _i++) {
|
|
@@ -104,30 +156,46 @@ function run() {
|
|
|
104
156
|
}
|
|
105
157
|
}
|
|
106
158
|
if (allFiles.length === 0) {
|
|
107
|
-
|
|
108
|
-
return [2 /*return*/, {
|
|
159
|
+
log('info', t.noFilesFound());
|
|
160
|
+
return [2 /*return*/, {
|
|
161
|
+
scannedFiles: 0,
|
|
162
|
+
filesWithErrors: 0,
|
|
163
|
+
filesWithWarnings: 0,
|
|
164
|
+
filesWithOptimizing: 0,
|
|
165
|
+
violationTotals: { total: 0, error: 0, warning: 0, optimizing: 0 },
|
|
166
|
+
files: [],
|
|
167
|
+
notifications: notifications,
|
|
168
|
+
}];
|
|
109
169
|
}
|
|
110
170
|
scannableFiles = allFiles.filter(function (candidate) { return !candidate.endsWith('.d.ts'); });
|
|
111
171
|
customRules = (0, loader_1.loadCustomRules)(ruleDir);
|
|
112
172
|
rules = __spreadArray(__spreadArray([], auto_cr_rules_1.builtinRules, true), customRules, true);
|
|
113
173
|
if (rules.length === 0) {
|
|
114
|
-
|
|
174
|
+
log('warn', t.noRulesLoaded());
|
|
115
175
|
return [2 /*return*/, {
|
|
116
176
|
scannedFiles: 0,
|
|
117
177
|
filesWithErrors: 0,
|
|
118
178
|
filesWithWarnings: 0,
|
|
119
179
|
filesWithOptimizing: 0,
|
|
180
|
+
violationTotals: { total: 0, error: 0, warning: 0, optimizing: 0 },
|
|
181
|
+
files: [],
|
|
182
|
+
notifications: notifications,
|
|
120
183
|
}];
|
|
121
184
|
}
|
|
122
185
|
filesWithErrors = 0;
|
|
123
186
|
filesWithWarnings = 0;
|
|
124
187
|
filesWithOptimizing = 0;
|
|
188
|
+
totalViolations = 0;
|
|
189
|
+
totalErrorViolations = 0;
|
|
190
|
+
totalWarningViolations = 0;
|
|
191
|
+
totalOptimizingViolations = 0;
|
|
192
|
+
fileSummaries = [];
|
|
125
193
|
_a = 0, scannableFiles_1 = scannableFiles;
|
|
126
194
|
_b.label = 2;
|
|
127
195
|
case 2:
|
|
128
196
|
if (!(_a < scannableFiles_1.length)) return [3 /*break*/, 5];
|
|
129
197
|
file = scannableFiles_1[_a];
|
|
130
|
-
return [4 /*yield*/, analyzeFile(file, rules)];
|
|
198
|
+
return [4 /*yield*/, analyzeFile(file, rules, format, log)];
|
|
131
199
|
case 3:
|
|
132
200
|
summary = _b.sent();
|
|
133
201
|
if (summary.severityCounts.error > 0) {
|
|
@@ -139,6 +207,17 @@ function run() {
|
|
|
139
207
|
if (summary.severityCounts.optimizing > 0) {
|
|
140
208
|
filesWithOptimizing += 1;
|
|
141
209
|
}
|
|
210
|
+
totalViolations += summary.totalViolations;
|
|
211
|
+
totalErrorViolations += summary.errorViolations;
|
|
212
|
+
totalWarningViolations += summary.severityCounts.warning;
|
|
213
|
+
totalOptimizingViolations += summary.severityCounts.optimizing;
|
|
214
|
+
fileSummaries.push({
|
|
215
|
+
filePath: file,
|
|
216
|
+
severityCounts: summary.severityCounts,
|
|
217
|
+
totalViolations: summary.totalViolations,
|
|
218
|
+
errorViolations: summary.errorViolations,
|
|
219
|
+
violations: summary.violations,
|
|
220
|
+
});
|
|
142
221
|
_b.label = 4;
|
|
143
222
|
case 4:
|
|
144
223
|
_a++;
|
|
@@ -148,6 +227,14 @@ function run() {
|
|
|
148
227
|
filesWithErrors: filesWithErrors,
|
|
149
228
|
filesWithWarnings: filesWithWarnings,
|
|
150
229
|
filesWithOptimizing: filesWithOptimizing,
|
|
230
|
+
violationTotals: {
|
|
231
|
+
total: totalViolations,
|
|
232
|
+
error: totalErrorViolations,
|
|
233
|
+
warning: totalWarningViolations,
|
|
234
|
+
optimizing: totalOptimizingViolations,
|
|
235
|
+
},
|
|
236
|
+
files: fileSummaries,
|
|
237
|
+
notifications: notifications,
|
|
151
238
|
}];
|
|
152
239
|
case 6:
|
|
153
240
|
error_1 = _b.sent();
|
|
@@ -157,27 +244,30 @@ function run() {
|
|
|
157
244
|
});
|
|
158
245
|
});
|
|
159
246
|
}
|
|
160
|
-
function analyzeFile(file, rules) {
|
|
247
|
+
function analyzeFile(file, rules, format, log) {
|
|
161
248
|
return __awaiter(this, void 0, void 0, function () {
|
|
162
249
|
var source, reporter, t, ast, parseOptions, language, baseContext, sharedHelpers, _loop_1, _i, rules_1, rule, summary;
|
|
163
250
|
return __generator(this, function (_a) {
|
|
164
251
|
switch (_a.label) {
|
|
165
252
|
case 0:
|
|
166
253
|
source = (0, file_1.readFile)(file);
|
|
167
|
-
reporter = (0, report_1.createReporter)(file, source);
|
|
254
|
+
reporter = (0, report_1.createReporter)(file, source, { format: format });
|
|
168
255
|
t = (0, i18n_1.getTranslator)();
|
|
169
256
|
try {
|
|
170
257
|
parseOptions = (0, config_1.loadParseOptions)(file);
|
|
171
258
|
ast = (0, wasm_1.parseSync)(source, parseOptions);
|
|
172
259
|
}
|
|
173
260
|
catch (error) {
|
|
174
|
-
|
|
261
|
+
log('error', t.parseFileFailed({ file: file }), error);
|
|
175
262
|
return [2 /*return*/, {
|
|
176
263
|
severityCounts: {
|
|
177
264
|
error: 1,
|
|
178
265
|
warning: 0,
|
|
179
266
|
optimizing: 0,
|
|
180
267
|
},
|
|
268
|
+
totalViolations: 1,
|
|
269
|
+
errorViolations: 1,
|
|
270
|
+
violations: [],
|
|
181
271
|
}];
|
|
182
272
|
}
|
|
183
273
|
language = (0, i18n_1.getLanguage)();
|
|
@@ -226,7 +316,7 @@ function analyzeFile(file, rules) {
|
|
|
226
316
|
return [3 /*break*/, 3];
|
|
227
317
|
case 2:
|
|
228
318
|
error_2 = _b.sent();
|
|
229
|
-
|
|
319
|
+
log('error', t.ruleExecutionFailed({ ruleName: rule.name, file: file }), error_2);
|
|
230
320
|
return [3 /*break*/, 3];
|
|
231
321
|
case 3: return [2 /*return*/];
|
|
232
322
|
}
|
|
@@ -248,6 +338,9 @@ function analyzeFile(file, rules) {
|
|
|
248
338
|
summary = reporter.flush();
|
|
249
339
|
return [2 /*return*/, {
|
|
250
340
|
severityCounts: summary.severityCounts,
|
|
341
|
+
totalViolations: summary.totalViolations,
|
|
342
|
+
errorViolations: summary.errorViolations,
|
|
343
|
+
violations: summary.violations,
|
|
251
344
|
}];
|
|
252
345
|
}
|
|
253
346
|
});
|
|
@@ -305,48 +398,130 @@ function normalizeViolationInput(input, spanArg) {
|
|
|
305
398
|
span: spanArg,
|
|
306
399
|
};
|
|
307
400
|
}
|
|
401
|
+
function parseOutputFormat(value) {
|
|
402
|
+
if (!value) {
|
|
403
|
+
return 'text';
|
|
404
|
+
}
|
|
405
|
+
var normalized = value.toLowerCase();
|
|
406
|
+
if (normalized === 'json' || normalized === 'text') {
|
|
407
|
+
return normalized;
|
|
408
|
+
}
|
|
409
|
+
throw new Error("Unsupported output format: ".concat(value, ". Use \"text\" or \"json\"."));
|
|
410
|
+
}
|
|
411
|
+
function severityToLabel(severity) {
|
|
412
|
+
switch (severity) {
|
|
413
|
+
case auto_cr_rules_1.RuleSeverity.Warning:
|
|
414
|
+
return 'warning';
|
|
415
|
+
case auto_cr_rules_1.RuleSeverity.Optimizing:
|
|
416
|
+
return 'optimizing';
|
|
417
|
+
case auto_cr_rules_1.RuleSeverity.Error:
|
|
418
|
+
default:
|
|
419
|
+
return 'error';
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function formatViolationForJson(violation) {
|
|
423
|
+
var suggestions = violation.suggestions
|
|
424
|
+
? violation.suggestions.map(function (suggestion) { return (__assign({}, suggestion)); })
|
|
425
|
+
: [];
|
|
426
|
+
var payload = {
|
|
427
|
+
tag: violation.tag,
|
|
428
|
+
ruleName: violation.ruleName,
|
|
429
|
+
severity: severityToLabel(violation.severity),
|
|
430
|
+
message: violation.message,
|
|
431
|
+
suggestions: suggestions,
|
|
432
|
+
};
|
|
433
|
+
if (typeof violation.line === 'number') {
|
|
434
|
+
payload.line = violation.line;
|
|
435
|
+
}
|
|
436
|
+
if (violation.code) {
|
|
437
|
+
payload.code = violation.code;
|
|
438
|
+
}
|
|
439
|
+
return payload;
|
|
440
|
+
}
|
|
441
|
+
function formatJsonOutput(result) {
|
|
442
|
+
return {
|
|
443
|
+
summary: {
|
|
444
|
+
scannedFiles: result.scannedFiles,
|
|
445
|
+
filesWithErrors: result.filesWithErrors,
|
|
446
|
+
filesWithWarnings: result.filesWithWarnings,
|
|
447
|
+
filesWithOptimizing: result.filesWithOptimizing,
|
|
448
|
+
violationTotals: result.violationTotals,
|
|
449
|
+
},
|
|
450
|
+
files: result.files.map(function (file) { return ({
|
|
451
|
+
filePath: file.filePath,
|
|
452
|
+
severityCounts: file.severityCounts,
|
|
453
|
+
totalViolations: file.totalViolations,
|
|
454
|
+
errorViolations: file.errorViolations,
|
|
455
|
+
violations: file.violations.map(formatViolationForJson),
|
|
456
|
+
}); }),
|
|
457
|
+
notifications: result.notifications,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
308
460
|
commander_1.program
|
|
309
461
|
.argument('[paths...]', '需要扫描的文件或目录路径列表 / Paths to scan')
|
|
310
462
|
.option('-r, --rule-dir <directory>', '自定义规则目录路径 / Custom rule directory')
|
|
311
463
|
.option('-l, --language <language>', '设置 CLI 语言 (zh/en) / Set CLI language (zh/en)')
|
|
464
|
+
.option('-o, --output <format>', '设置输出格式 (text/json) / Output format (text/json)', 'text')
|
|
312
465
|
.parse(process.argv);
|
|
313
466
|
var options = commander_1.program.opts();
|
|
314
467
|
var filePaths = commander_1.program.args.map(function (target) { return path_1.default.resolve(process.cwd(), target); });
|
|
468
|
+
(0, i18n_1.setLanguage)((_a = options.language) !== null && _a !== void 0 ? _a : process.env.LANG);
|
|
469
|
+
var outputFormat;
|
|
470
|
+
try {
|
|
471
|
+
outputFormat = parseOutputFormat(options.output);
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
474
|
+
var message = error instanceof Error ? error.message : String(error);
|
|
475
|
+
consola_1.consola.error(message);
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
;
|
|
315
479
|
(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
316
|
-
var
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
switch (_b.label) {
|
|
480
|
+
var result, t, payload, exitCode, language, resultMessage, exitCode, error_3, t, detail, payload;
|
|
481
|
+
return __generator(this, function (_a) {
|
|
482
|
+
switch (_a.label) {
|
|
320
483
|
case 0:
|
|
321
|
-
|
|
322
|
-
(
|
|
323
|
-
return [4 /*yield*/, run(filePaths, options.ruleDir)];
|
|
484
|
+
_a.trys.push([0, 2, , 3]);
|
|
485
|
+
return [4 /*yield*/, run(filePaths, options.ruleDir, outputFormat)];
|
|
324
486
|
case 1:
|
|
325
|
-
|
|
487
|
+
result = _a.sent();
|
|
326
488
|
t = (0, i18n_1.getTranslator)();
|
|
327
|
-
if (
|
|
489
|
+
if (outputFormat === 'json') {
|
|
490
|
+
payload = formatJsonOutput(result);
|
|
491
|
+
exitCode = result.filesWithErrors > 0 ? 1 : 0;
|
|
492
|
+
process.stdout.write("".concat(JSON.stringify(payload, null, 2), "\n"));
|
|
493
|
+
process.exit(exitCode);
|
|
494
|
+
}
|
|
495
|
+
if (result.scannedFiles > 0) {
|
|
328
496
|
consola_1.consola.log(' ');
|
|
329
497
|
language = (0, i18n_1.getLanguage)();
|
|
330
498
|
resultMessage = language.startsWith('zh')
|
|
331
|
-
? " ".concat(t.scanComplete(), "\uFF0C\u672C\u6B21\u5171\u626B\u63CF").concat(
|
|
332
|
-
: " ".concat(t.scanComplete(), ", scanned ").concat(
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
else {
|
|
338
|
-
consola_1.consola.success(resultMessage);
|
|
339
|
-
process.exit(0);
|
|
340
|
-
}
|
|
499
|
+
? " ".concat(t.scanComplete(), "\uFF0C\u672C\u6B21\u5171\u626B\u63CF").concat(result.scannedFiles, "\u4E2A\u6587\u4EF6\uFF0C\u5176\u4E2D").concat(result.filesWithErrors, "\u4E2A\u6587\u4EF6\u5B58\u5728\u9519\u8BEF\uFF0C").concat(result.filesWithWarnings, "\u4E2A\u6587\u4EF6\u5B58\u5728\u8B66\u544A\uFF0C").concat(result.filesWithOptimizing, "\u4E2A\u6587\u4EF6\u5B58\u5728\u4F18\u5316\u5EFA\u8BAE\uFF01")
|
|
500
|
+
: " ".concat(t.scanComplete(), ", scanned ").concat(result.scannedFiles, " files: ").concat(result.filesWithErrors, " with errors, ").concat(result.filesWithWarnings, " with warnings, ").concat(result.filesWithOptimizing, " with optimizing hints!");
|
|
501
|
+
consola_1.consola.success(resultMessage);
|
|
502
|
+
exitCode = result.filesWithErrors > 0 ? 1 : 0;
|
|
503
|
+
process.exit(exitCode);
|
|
341
504
|
}
|
|
342
505
|
else {
|
|
343
506
|
process.exit(0);
|
|
344
507
|
}
|
|
345
508
|
return [3 /*break*/, 3];
|
|
346
509
|
case 2:
|
|
347
|
-
error_3 =
|
|
510
|
+
error_3 = _a.sent();
|
|
348
511
|
t = (0, i18n_1.getTranslator)();
|
|
349
|
-
|
|
512
|
+
detail = error_3 instanceof Error ? error_3.message : String(error_3);
|
|
513
|
+
if (outputFormat === 'json') {
|
|
514
|
+
payload = {
|
|
515
|
+
error: {
|
|
516
|
+
message: t.scanError(),
|
|
517
|
+
detail: detail,
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
process.stdout.write("".concat(JSON.stringify(payload, null, 2), "\n"));
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
consola_1.consola.error(t.scanError(), detail);
|
|
524
|
+
}
|
|
350
525
|
process.exit(1);
|
|
351
526
|
return [3 /*break*/, 3];
|
|
352
527
|
case 3: return [2 /*return*/];
|
package/dist/report/index.js
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
14
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
15
|
+
if (ar || !(i in from)) {
|
|
16
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
17
|
+
ar[i] = from[i];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
21
|
+
};
|
|
2
22
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
23
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
24
|
};
|
|
@@ -9,16 +29,20 @@ var auto_cr_rules_1 = require("auto-cr-rules");
|
|
|
9
29
|
var consola_1 = __importDefault(require("consola"));
|
|
10
30
|
var i18n_1 = require("../i18n");
|
|
11
31
|
var UNTAGGED_TAG = 'untagged';
|
|
32
|
+
var DEFAULT_FORMAT = 'text';
|
|
12
33
|
var severityLoggers = (_a = {},
|
|
13
34
|
_a[auto_cr_rules_1.RuleSeverity.Error] = consola_1.default.error,
|
|
14
35
|
_a[auto_cr_rules_1.RuleSeverity.Warning] = consola_1.default.warn,
|
|
15
36
|
_a[auto_cr_rules_1.RuleSeverity.Optimizing] = consola_1.default.info,
|
|
16
37
|
_a);
|
|
17
|
-
function createReporter(filePath, source) {
|
|
38
|
+
function createReporter(filePath, source, options) {
|
|
39
|
+
var _a;
|
|
40
|
+
if (options === void 0) { options = {}; }
|
|
18
41
|
var offsets = buildLineOffsets(source);
|
|
19
42
|
var t = (0, i18n_1.getTranslator)();
|
|
20
43
|
var language = (0, i18n_1.getLanguage)();
|
|
21
44
|
var records = [];
|
|
45
|
+
var format = (_a = options.format) !== null && _a !== void 0 ? _a : DEFAULT_FORMAT;
|
|
22
46
|
var totalViolations = 0;
|
|
23
47
|
var errorViolations = 0;
|
|
24
48
|
var severityCounts = {
|
|
@@ -114,6 +138,7 @@ function createReporter(filePath, source) {
|
|
|
114
138
|
return reporterWithRecord;
|
|
115
139
|
};
|
|
116
140
|
var flush = function () {
|
|
141
|
+
var violationSnapshot = records.map(function (record) { return (__assign(__assign({}, record), { suggestions: record.suggestions ? __spreadArray([], record.suggestions, true) : undefined })); });
|
|
117
142
|
var summary = {
|
|
118
143
|
totalViolations: totalViolations,
|
|
119
144
|
errorViolations: errorViolations,
|
|
@@ -122,42 +147,41 @@ function createReporter(filePath, source) {
|
|
|
122
147
|
warning: severityCounts.warning,
|
|
123
148
|
optimizing: severityCounts.optimizing,
|
|
124
149
|
},
|
|
150
|
+
violations: violationSnapshot,
|
|
125
151
|
};
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
152
|
+
if (violationSnapshot.length > 0 && format === 'text') {
|
|
153
|
+
var locale = language === 'zh' ? 'zh-CN' : 'en-US';
|
|
154
|
+
var formatter_1 = new Intl.DateTimeFormat(locale, {
|
|
155
|
+
hour: '2-digit',
|
|
156
|
+
minute: '2-digit',
|
|
157
|
+
second: '2-digit',
|
|
158
|
+
hour12: false,
|
|
159
|
+
});
|
|
160
|
+
var indent_1 = ' ';
|
|
161
|
+
var colon_1 = language === 'zh' ? ':' : ':';
|
|
162
|
+
var headerGap_1 = language === 'zh' ? '' : ' ';
|
|
163
|
+
violationSnapshot.forEach(function (violation) {
|
|
164
|
+
var timestamp = formatter_1.format(new Date());
|
|
165
|
+
var tagLabel = t.ruleTagLabel({ tag: violation.tag });
|
|
166
|
+
var severityIcon = t.reporterSeverityIcon({ severity: violation.severity });
|
|
167
|
+
var logger = getLoggerForSeverity(violation.severity);
|
|
168
|
+
var header = "[".concat(timestamp, "] ").concat(severityIcon, " [").concat(tagLabel, "]").concat(colon_1).concat(headerGap_1).concat(violation.ruleName);
|
|
169
|
+
logger(header);
|
|
170
|
+
var location = typeof violation.line === 'number' ? "".concat(filePath, ":").concat(violation.line) : filePath;
|
|
171
|
+
consola_1.default.log("".concat(indent_1).concat(t.reporterFileLabel(), ": ").concat(location));
|
|
172
|
+
consola_1.default.log("".concat(indent_1).concat(t.reporterDescriptionLabel(), ": ").concat(violation.message));
|
|
173
|
+
if (violation.code) {
|
|
174
|
+
consola_1.default.log("".concat(indent_1).concat(t.reporterCodeLabel(), ": ").concat(violation.code));
|
|
175
|
+
}
|
|
176
|
+
if (violation.suggestions && violation.suggestions.length > 0) {
|
|
177
|
+
var suggestionSeparator = language === 'zh' ? '; ' : ' | ';
|
|
178
|
+
var suggestionLine = violation.suggestions
|
|
179
|
+
.map(function (suggestion) { return t.reporterFormatSuggestion(suggestion); })
|
|
180
|
+
.join(suggestionSeparator);
|
|
181
|
+
consola_1.default.log("".concat(indent_1).concat(t.reporterSuggestionLabel(), ": ").concat(suggestionLine));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
129
184
|
}
|
|
130
|
-
var locale = language === 'zh' ? 'zh-CN' : 'en-US';
|
|
131
|
-
var formatter = new Intl.DateTimeFormat(locale, {
|
|
132
|
-
hour: '2-digit',
|
|
133
|
-
minute: '2-digit',
|
|
134
|
-
second: '2-digit',
|
|
135
|
-
hour12: false,
|
|
136
|
-
});
|
|
137
|
-
var indent = ' ';
|
|
138
|
-
var colon = language === 'zh' ? ':' : ':';
|
|
139
|
-
var headerGap = language === 'zh' ? '' : ' ';
|
|
140
|
-
records.forEach(function (violation) {
|
|
141
|
-
var timestamp = formatter.format(new Date());
|
|
142
|
-
var tagLabel = t.ruleTagLabel({ tag: violation.tag });
|
|
143
|
-
var severityIcon = t.reporterSeverityIcon({ severity: violation.severity });
|
|
144
|
-
var logger = getLoggerForSeverity(violation.severity);
|
|
145
|
-
var header = "[".concat(timestamp, "] ").concat(severityIcon, " [").concat(tagLabel, "]").concat(colon).concat(headerGap).concat(violation.ruleName);
|
|
146
|
-
logger(header);
|
|
147
|
-
var location = typeof violation.line === 'number' ? "".concat(filePath, ":").concat(violation.line) : filePath;
|
|
148
|
-
consola_1.default.log("".concat(indent).concat(t.reporterFileLabel(), ": ").concat(location));
|
|
149
|
-
consola_1.default.log("".concat(indent).concat(t.reporterDescriptionLabel(), ": ").concat(violation.message));
|
|
150
|
-
if (violation.code) {
|
|
151
|
-
consola_1.default.log("".concat(indent).concat(t.reporterCodeLabel(), ": ").concat(violation.code));
|
|
152
|
-
}
|
|
153
|
-
if (violation.suggestions && violation.suggestions.length > 0) {
|
|
154
|
-
var suggestionSeparator = language === 'zh' ? '; ' : ' | ';
|
|
155
|
-
var suggestionLine = violation.suggestions
|
|
156
|
-
.map(function (suggestion) { return t.reporterFormatSuggestion(suggestion); })
|
|
157
|
-
.join(suggestionSeparator);
|
|
158
|
-
consola_1.default.log("".concat(indent).concat(t.reporterSuggestionLabel(), ": ").concat(suggestionLine));
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
185
|
records.length = 0;
|
|
162
186
|
resetCounters();
|
|
163
187
|
return summary;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { RuleSeverity } from 'auto-cr-rules';
|
|
1
2
|
import type { Rule, RuleReporter } from 'auto-cr-rules';
|
|
3
|
+
export type ReporterFormat = 'text' | 'json';
|
|
4
|
+
interface ReporterOptions {
|
|
5
|
+
format?: ReporterFormat;
|
|
6
|
+
}
|
|
2
7
|
export interface Reporter extends RuleReporter {
|
|
3
8
|
forRule(rule: Pick<Rule, 'name' | 'tag' | 'severity'>): RuleReporter;
|
|
4
9
|
flush(): ReporterSummary;
|
|
5
10
|
}
|
|
6
|
-
interface ReporterSummary {
|
|
11
|
+
export interface ReporterSummary {
|
|
7
12
|
totalViolations: number;
|
|
8
13
|
errorViolations: number;
|
|
9
14
|
severityCounts: {
|
|
@@ -11,6 +16,21 @@ interface ReporterSummary {
|
|
|
11
16
|
warning: number;
|
|
12
17
|
optimizing: number;
|
|
13
18
|
};
|
|
19
|
+
violations: ReadonlyArray<ViolationRecord>;
|
|
20
|
+
}
|
|
21
|
+
type Severity = RuleSeverity;
|
|
22
|
+
type SuggestionEntry = {
|
|
23
|
+
text: string;
|
|
24
|
+
link?: string;
|
|
25
|
+
};
|
|
26
|
+
export interface ViolationRecord {
|
|
27
|
+
tag: string;
|
|
28
|
+
ruleName: string;
|
|
29
|
+
severity: Severity;
|
|
30
|
+
message: string;
|
|
31
|
+
line?: number;
|
|
32
|
+
code?: string;
|
|
33
|
+
suggestions?: ReadonlyArray<SuggestionEntry>;
|
|
14
34
|
}
|
|
15
|
-
export declare function createReporter(filePath: string, source: string): Reporter;
|
|
35
|
+
export declare function createReporter(filePath: string, source: string, options?: ReporterOptions): Reporter;
|
|
16
36
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auto-cr-cmd",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.28",
|
|
4
4
|
"description": "Fast automated code review CLI powered by SWC-based static analysis",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@swc/core": "^1.13.20",
|
|
50
50
|
"@swc/wasm": "^1.13.20",
|
|
51
|
-
"auto-cr-rules": "^2.0.
|
|
51
|
+
"auto-cr-rules": "^2.0.28",
|
|
52
52
|
"commander": "^14.0.0",
|
|
53
53
|
"consola": "^3.4.2"
|
|
54
54
|
},
|