frontend-guardian-core 3.14.1 → 3.20.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/bin/fg-core.js +322 -16
- package/bin/fg-lsp.js +1 -1
- package/bin/fg-server.js +2 -2
- package/bin/watch-mode.js +155 -9
- package/dist/engine/rule-engine.d.ts +17 -1
- package/dist/engine/rule-engine.d.ts.map +1 -1
- package/dist/engine/rule-engine.js +158 -3
- package/dist/engine/rule-engine.js.map +1 -1
- package/dist/index.d.ts +21 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -4
- package/dist/index.js.map +1 -1
- package/dist/integrations/cypress.d.ts +9 -0
- package/dist/integrations/cypress.d.ts.map +1 -0
- package/dist/integrations/cypress.js +104 -0
- package/dist/integrations/cypress.js.map +1 -0
- package/dist/integrations/index.d.ts +6 -0
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +8 -1
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/katalon.d.ts +9 -0
- package/dist/integrations/katalon.d.ts.map +1 -0
- package/dist/integrations/katalon.js +102 -0
- package/dist/integrations/katalon.js.map +1 -0
- package/dist/integrations/selenium.d.ts +9 -0
- package/dist/integrations/selenium.d.ts.map +1 -0
- package/dist/integrations/selenium.js +143 -0
- package/dist/integrations/selenium.js.map +1 -0
- package/dist/mcp/guidance.d.ts +1 -1
- package/dist/mcp/guidance.js +2 -2
- package/dist/mcp/mcp-server.js +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +67 -10
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/types.d.ts +3 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/rules/registry.d.ts +12 -1
- package/dist/rules/registry.d.ts.map +1 -1
- package/dist/rules/registry.js +26 -0
- package/dist/rules/registry.js.map +1 -1
- package/dist/scanners/backend-scanner.d.ts +9 -0
- package/dist/scanners/backend-scanner.d.ts.map +1 -0
- package/dist/scanners/backend-scanner.js +414 -0
- package/dist/scanners/backend-scanner.js.map +1 -0
- package/dist/scanners/css-scanner.d.ts +8 -0
- package/dist/scanners/css-scanner.d.ts.map +1 -0
- package/dist/scanners/css-scanner.js +185 -0
- package/dist/scanners/css-scanner.js.map +1 -0
- package/dist/scanners/data-scanner.d.ts +8 -0
- package/dist/scanners/data-scanner.d.ts.map +1 -0
- package/dist/scanners/data-scanner.js +377 -0
- package/dist/scanners/data-scanner.js.map +1 -0
- package/dist/types.d.ts +146 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/baseline.d.ts +1 -1
- package/dist/utils/baseline.d.ts.map +1 -1
- package/dist/utils/baseline.js +14 -2
- package/dist/utils/baseline.js.map +1 -1
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/config-loader.js +14 -0
- package/dist/utils/config-loader.js.map +1 -1
- package/dist/utils/config-recommender.d.ts +38 -0
- package/dist/utils/config-recommender.d.ts.map +1 -0
- package/dist/utils/config-recommender.js +183 -0
- package/dist/utils/config-recommender.js.map +1 -0
- package/dist/utils/e2e-gap-detector.d.ts +2 -0
- package/dist/utils/e2e-gap-detector.d.ts.map +1 -1
- package/dist/utils/e2e-gap-detector.js +62 -16
- package/dist/utils/e2e-gap-detector.js.map +1 -1
- package/dist/utils/impact-graph.d.ts +70 -0
- package/dist/utils/impact-graph.d.ts.map +1 -0
- package/dist/utils/impact-graph.js +230 -0
- package/dist/utils/impact-graph.js.map +1 -0
- package/dist/utils/init-config.js +3 -1
- package/dist/utils/init-config.js.map +1 -1
- package/dist/utils/market-index.d.ts +27 -0
- package/dist/utils/market-index.d.ts.map +1 -0
- package/dist/utils/market-index.js +151 -0
- package/dist/utils/market-index.js.map +1 -0
- package/dist/utils/project-detector.js +4 -0
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/rule-compatibility.d.ts +15 -0
- package/dist/utils/rule-compatibility.d.ts.map +1 -0
- package/dist/utils/rule-compatibility.js +129 -0
- package/dist/utils/rule-compatibility.js.map +1 -0
- package/dist/utils/rule-doc-generator.d.ts +19 -0
- package/dist/utils/rule-doc-generator.d.ts.map +1 -0
- package/dist/utils/rule-doc-generator.js +143 -0
- package/dist/utils/rule-doc-generator.js.map +1 -0
- package/dist/utils/rule-hot-reload.d.ts +22 -0
- package/dist/utils/rule-hot-reload.d.ts.map +1 -0
- package/dist/utils/rule-hot-reload.js +59 -0
- package/dist/utils/rule-hot-reload.js.map +1 -0
- package/dist/utils/rule-scoring.d.ts +18 -0
- package/dist/utils/rule-scoring.d.ts.map +1 -0
- package/dist/utils/rule-scoring.js +173 -0
- package/dist/utils/rule-scoring.js.map +1 -0
- package/dist/utils/rule-template-generator.d.ts +16 -0
- package/dist/utils/rule-template-generator.d.ts.map +1 -0
- package/dist/utils/rule-template-generator.js +146 -0
- package/dist/utils/rule-template-generator.js.map +1 -0
- package/dist/utils/test-recommender.d.ts +5 -0
- package/dist/utils/test-recommender.d.ts.map +1 -1
- package/dist/utils/test-recommender.js +20 -1
- package/dist/utils/test-recommender.js.map +1 -1
- package/market/README.md +47 -0
- package/market/index.json +50 -0
- package/package.json +2 -1
package/bin/fg-core.js
CHANGED
|
@@ -10,15 +10,21 @@ import pc from "picocolors";
|
|
|
10
10
|
import {
|
|
11
11
|
AIFixSuggester,
|
|
12
12
|
a11yRules,
|
|
13
|
+
backendRules,
|
|
13
14
|
BaselineManager,
|
|
14
15
|
buildNotificationPayload,
|
|
16
|
+
checkRuleCompatibility,
|
|
15
17
|
CodeownersParser,
|
|
16
18
|
compareHistoryReports,
|
|
17
19
|
complianceReportToMarkdown,
|
|
18
20
|
componentRules,
|
|
21
|
+
computeRuleScores,
|
|
19
22
|
createEngine,
|
|
20
23
|
createPublisher,
|
|
21
24
|
crossFileRules,
|
|
25
|
+
cssRules,
|
|
26
|
+
cypressIntegration,
|
|
27
|
+
dataRules,
|
|
22
28
|
detectAIConfig,
|
|
23
29
|
detectCIProvider,
|
|
24
30
|
detectE2EGaps,
|
|
@@ -31,36 +37,53 @@ import {
|
|
|
31
37
|
e2eRules,
|
|
32
38
|
FileWatcher,
|
|
33
39
|
formatAllAnnotations,
|
|
40
|
+
formatCompatibilityReport,
|
|
41
|
+
formatCompatibilityReportJson,
|
|
34
42
|
formatE2EGapJson,
|
|
35
43
|
formatE2EGapReport,
|
|
44
|
+
formatGeneratedDocs,
|
|
36
45
|
formatHistoryCompare,
|
|
37
46
|
formatHistoryCompareJson,
|
|
47
|
+
formatImpactGraph,
|
|
48
|
+
formatMarketIndex,
|
|
49
|
+
formatMarketIndexJson,
|
|
38
50
|
formatMiniProgramJson,
|
|
51
|
+
formatScanProfile,
|
|
39
52
|
formatMiniProgramReport,
|
|
40
53
|
formatPageHealthJson,
|
|
54
|
+
formatRecommendedConfig,
|
|
41
55
|
formatPageHealthReport,
|
|
56
|
+
formatRuleScores,
|
|
57
|
+
formatRuleScoresJson,
|
|
42
58
|
formatWorkspaceJson,
|
|
43
59
|
formatWorkspaceReport,
|
|
44
60
|
generateCIConfig,
|
|
45
61
|
generateComplianceReport,
|
|
46
62
|
generateDashboard,
|
|
47
63
|
generatePRComment,
|
|
64
|
+
generateRuleDocs,
|
|
65
|
+
generateRuleTemplate,
|
|
48
66
|
generateSarif,
|
|
49
67
|
HistoryReport,
|
|
50
68
|
hooksRules,
|
|
51
69
|
i18nRules,
|
|
52
70
|
initConfig,
|
|
53
71
|
installGitHooks,
|
|
72
|
+
isCompatibilityReportClean,
|
|
54
73
|
isGitHubActions,
|
|
55
74
|
isPlaywrightAvailable,
|
|
75
|
+
katalonIntegration,
|
|
76
|
+
loadMarketIndex,
|
|
56
77
|
namingRules,
|
|
57
78
|
ProjectIndexer,
|
|
58
79
|
performanceRules,
|
|
59
80
|
platformRules,
|
|
81
|
+
recommendConfig,
|
|
60
82
|
playwrightIntegration,
|
|
61
83
|
runFixBot,
|
|
62
84
|
runMiniProgramTest,
|
|
63
85
|
runPageHealthCheck,
|
|
86
|
+
seleniumIntegration,
|
|
64
87
|
SmartCache,
|
|
65
88
|
saveComplianceReport,
|
|
66
89
|
scanWorkspace,
|
|
@@ -87,6 +110,9 @@ const MODULES = [
|
|
|
87
110
|
"platform",
|
|
88
111
|
"svelte",
|
|
89
112
|
"e2e",
|
|
113
|
+
"css",
|
|
114
|
+
"data",
|
|
115
|
+
"backend",
|
|
90
116
|
];
|
|
91
117
|
|
|
92
118
|
const MODULE_RULES = {
|
|
@@ -101,18 +127,21 @@ const MODULE_RULES = {
|
|
|
101
127
|
platform: platformRules,
|
|
102
128
|
svelte: svelteRules,
|
|
103
129
|
e2e: e2eRules,
|
|
130
|
+
css: cssRules,
|
|
131
|
+
data: dataRules,
|
|
132
|
+
backend: backendRules,
|
|
104
133
|
};
|
|
105
134
|
|
|
106
135
|
function showHelp() {
|
|
107
136
|
console.log(`
|
|
108
|
-
Frontend Guardian Core v3.
|
|
137
|
+
Frontend Guardian Core v3.20.0
|
|
109
138
|
|
|
110
139
|
Usage:
|
|
111
140
|
fg-core <project-dir> [options]
|
|
112
141
|
|
|
113
142
|
Options:
|
|
114
143
|
--scan 全量扫描(等价于 --module all)
|
|
115
|
-
--module <name> 扫描模块: i18n | performance | a11y | security | naming | cross-file | component | hooks | platform | svelte | e2e | all
|
|
144
|
+
--module <name> 扫描模块: i18n | performance | a11y | security | naming | cross-file | component | hooks | platform | svelte | e2e | css | data | backend | all
|
|
116
145
|
--severity <level> 最低严重级别: critical | warning | suggestion (默认: suggestion)
|
|
117
146
|
--files <pattern> 仅扫描匹配的文件
|
|
118
147
|
--exclude <pattern> 排除匹配的文件
|
|
@@ -129,6 +158,9 @@ Options:
|
|
|
129
158
|
--output <file> 将扫描报告写入指定文件(Markdown 格式)
|
|
130
159
|
--external 运行外部工具集成 (ESLint / TypeScript / Stylelint)
|
|
131
160
|
--watch Watch 模式:文件变更自动增量扫描
|
|
161
|
+
--no-progress 禁用扫描进度条(v3.15.0)
|
|
162
|
+
--profile 输出扫描耗时分析:规则/文件耗时排名(v3.15.0)
|
|
163
|
+
--recommend-config 根据项目规模与框架自动推荐最优配置(v3.15.0)
|
|
132
164
|
--no-cache 禁用智能缓存
|
|
133
165
|
--config <file> 指定配置文件
|
|
134
166
|
--install-hooks 安装 Git hook(默认 pre-commit,可用 --install-hooks-type 指定)
|
|
@@ -164,7 +196,8 @@ Options:
|
|
|
164
196
|
--server <url> 扫描后上报到治理看板服务器
|
|
165
197
|
--serve 扫描前启动本地看板服务(扫描完成后停止)
|
|
166
198
|
--e2e-detect-gaps 检测 E2E 测试覆盖缺口(页面 + 接口)
|
|
167
|
-
--e2e-run 运行
|
|
199
|
+
--e2e-run 运行 E2E 测试并输出治理报告
|
|
200
|
+
--e2e-tool <tool> E2E 工具: auto | playwright | cypress | selenium | katalon (默认: auto)(v3.16.0)
|
|
168
201
|
--page-health 页面健康检查:遍历路由验证渲染、控制台错误、白屏
|
|
169
202
|
--serve <cmd> 自动启动 dev server(配合 --page-health,如 "npm run dev")
|
|
170
203
|
--port <n> dev server 端口(默认 5173,配合 --serve)
|
|
@@ -211,6 +244,22 @@ Options:
|
|
|
211
244
|
--watch-index 监听文件变更并自动同步索引
|
|
212
245
|
--index-status 查看项目索引状态
|
|
213
246
|
--recommend-tests 智能测试推荐:基于变更影响分析推荐需要运行的测试(可配合 --staged/--diff/--auto-scope/--files)
|
|
247
|
+
--impact-graph 配合 --recommend-tests 输出变更影响图(默认 mermaid 格式)(v3.16.0)
|
|
248
|
+
--impact-graph-format <format> 影响图格式: json | mermaid | dot (默认: mermaid)(v3.16.0)
|
|
249
|
+
--create-rule <id> 生成规则模板文件(默认输出到 ./rules)(v3.17.0)
|
|
250
|
+
--create-rule-dir <dir> 规则模板输出目录(默认 ./rules)(v3.17.0)
|
|
251
|
+
--create-rule-category <cat> 规则分类(默认 security)(v3.17.0)
|
|
252
|
+
--create-rule-severity <sev> 严重级别: critical | warning | suggestion (默认: warning)(v3.17.0)
|
|
253
|
+
--create-rule-fix 生成的规则模板包含 fix 示例(v3.17.0)
|
|
254
|
+
--create-rule-lang <js|ts> 规则模板语言(默认 js)(v3.17.0)
|
|
255
|
+
--market-index 打印规则市场索引(v3.17.0)
|
|
256
|
+
--market-index-json 以 JSON 格式输出规则市场索引(v3.17.0)
|
|
257
|
+
--market-index-url <url> 覆盖默认市场索引 URL(v3.17.0)
|
|
258
|
+
--rule-scores 打印规则评分(基于本地扫描历史)(v3.17.0)
|
|
259
|
+
--rule-scores-json 以 JSON 格式输出规则评分(v3.17.0)
|
|
260
|
+
--generate-rule-docs 为所有规则生成 Markdown 文档(默认输出 ./docs/rules)(v3.18.0)
|
|
261
|
+
--generate-rule-docs-dir <dir> 指定规则文档输出目录(v3.18.0)
|
|
262
|
+
--check-rule-compat 检查当前启用规则集的兼容性/冲突(v3.18.0)
|
|
214
263
|
--flaky-threshold-failure-rate <n> flaky 测试失败率阈值(默认 0.2)
|
|
215
264
|
--flaky-threshold-flip-rate <n> flaky 测试状态翻转率阈值(默认 0.15)
|
|
216
265
|
--flaky-min-runs <n> 参与 flaky 计算的最少历史运行次数(默认 3)
|
|
@@ -219,6 +268,8 @@ Options:
|
|
|
219
268
|
|
|
220
269
|
Examples:
|
|
221
270
|
fg-core ./my-project --module all
|
|
271
|
+
fg-core ./my-project --module backend
|
|
272
|
+
fg-core ./my-project --module backend --files "server/**/*.go"
|
|
222
273
|
fg-core ./my-project --module i18n
|
|
223
274
|
fg-core ./my-project --module i18n --severity warning --json
|
|
224
275
|
fg-core ./my-project --module performance --files "src/**/*.tsx"
|
|
@@ -349,6 +400,30 @@ async function main() {
|
|
|
349
400
|
flakyThresholdFailureRate: undefined,
|
|
350
401
|
flakyThresholdFlipRate: undefined,
|
|
351
402
|
flakyMinRuns: undefined,
|
|
403
|
+
// v3.16.0
|
|
404
|
+
impactGraph: false,
|
|
405
|
+
impactGraphFormat: "mermaid",
|
|
406
|
+
e2eTool: "auto",
|
|
407
|
+
// v3.17.0
|
|
408
|
+
createRule: undefined,
|
|
409
|
+
createRuleDir: "./rules",
|
|
410
|
+
createRuleCategory: "security",
|
|
411
|
+
createRuleSeverity: "warning",
|
|
412
|
+
createRuleFix: false,
|
|
413
|
+
createRuleLang: "js",
|
|
414
|
+
marketIndex: false,
|
|
415
|
+
marketIndexJson: false,
|
|
416
|
+
marketIndexUrl: undefined,
|
|
417
|
+
ruleScores: false,
|
|
418
|
+
ruleScoresJson: false,
|
|
419
|
+
// v3.18.0
|
|
420
|
+
generateRuleDocs: false,
|
|
421
|
+
generateRuleDocsDir: "./docs/rules",
|
|
422
|
+
checkRuleCompat: false,
|
|
423
|
+
// v3.15.0
|
|
424
|
+
profile: false,
|
|
425
|
+
showProgress: true,
|
|
426
|
+
recommendConfig: false,
|
|
352
427
|
mcp: false,
|
|
353
428
|
};
|
|
354
429
|
|
|
@@ -674,6 +749,65 @@ async function main() {
|
|
|
674
749
|
case "--recommend-tests":
|
|
675
750
|
options.recommendTests = true;
|
|
676
751
|
break;
|
|
752
|
+
case "--impact-graph":
|
|
753
|
+
options.impactGraph = true;
|
|
754
|
+
break;
|
|
755
|
+
case "--impact-graph-format":
|
|
756
|
+
options.impactGraphFormat = args[++i];
|
|
757
|
+
break;
|
|
758
|
+
case "--e2e-tool":
|
|
759
|
+
options.e2eTool = args[++i];
|
|
760
|
+
break;
|
|
761
|
+
// v3.17.0
|
|
762
|
+
case "--create-rule": {
|
|
763
|
+
const next = args[i + 1];
|
|
764
|
+
if (next && !next.startsWith("-")) {
|
|
765
|
+
options.createRule = args[++i];
|
|
766
|
+
} else {
|
|
767
|
+
options.createRule = true;
|
|
768
|
+
}
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
case "--create-rule-dir":
|
|
772
|
+
options.createRuleDir = args[++i];
|
|
773
|
+
break;
|
|
774
|
+
case "--create-rule-category":
|
|
775
|
+
options.createRuleCategory = args[++i];
|
|
776
|
+
break;
|
|
777
|
+
case "--create-rule-severity":
|
|
778
|
+
options.createRuleSeverity = args[++i];
|
|
779
|
+
break;
|
|
780
|
+
case "--create-rule-fix":
|
|
781
|
+
options.createRuleFix = true;
|
|
782
|
+
break;
|
|
783
|
+
case "--create-rule-lang":
|
|
784
|
+
options.createRuleLang = args[++i];
|
|
785
|
+
break;
|
|
786
|
+
case "--market-index":
|
|
787
|
+
options.marketIndex = true;
|
|
788
|
+
break;
|
|
789
|
+
case "--market-index-json":
|
|
790
|
+
options.marketIndexJson = true;
|
|
791
|
+
break;
|
|
792
|
+
case "--market-index-url":
|
|
793
|
+
options.marketIndexUrl = args[++i];
|
|
794
|
+
break;
|
|
795
|
+
case "--rule-scores":
|
|
796
|
+
options.ruleScores = true;
|
|
797
|
+
break;
|
|
798
|
+
case "--rule-scores-json":
|
|
799
|
+
options.ruleScoresJson = true;
|
|
800
|
+
break;
|
|
801
|
+
// v3.18.0
|
|
802
|
+
case "--generate-rule-docs":
|
|
803
|
+
options.generateRuleDocs = true;
|
|
804
|
+
break;
|
|
805
|
+
case "--generate-rule-docs-dir":
|
|
806
|
+
options.generateRuleDocsDir = args[++i];
|
|
807
|
+
break;
|
|
808
|
+
case "--check-rule-compat":
|
|
809
|
+
options.checkRuleCompat = true;
|
|
810
|
+
break;
|
|
677
811
|
case "--flaky-threshold-failure-rate":
|
|
678
812
|
options.flakyThresholdFailureRate = parseFloat(args[++i]);
|
|
679
813
|
break;
|
|
@@ -686,6 +820,16 @@ async function main() {
|
|
|
686
820
|
case "--mcp":
|
|
687
821
|
options.mcp = true;
|
|
688
822
|
break;
|
|
823
|
+
// v3.15.0
|
|
824
|
+
case "--profile":
|
|
825
|
+
options.profile = true;
|
|
826
|
+
break;
|
|
827
|
+
case "--no-progress":
|
|
828
|
+
options.showProgress = false;
|
|
829
|
+
break;
|
|
830
|
+
case "--recommend-config":
|
|
831
|
+
options.recommendConfig = true;
|
|
832
|
+
break;
|
|
689
833
|
case "--help":
|
|
690
834
|
case "-h":
|
|
691
835
|
showHelp();
|
|
@@ -704,6 +848,102 @@ async function main() {
|
|
|
704
848
|
return;
|
|
705
849
|
}
|
|
706
850
|
|
|
851
|
+
// v3.15.0: 配置推荐
|
|
852
|
+
if (options.recommendConfig) {
|
|
853
|
+
const rec = recommendConfig(options.projectDir);
|
|
854
|
+
if (options.json) {
|
|
855
|
+
console.log(JSON.stringify(rec, null, 2));
|
|
856
|
+
} else {
|
|
857
|
+
console.log(formatRecommendedConfig(rec));
|
|
858
|
+
}
|
|
859
|
+
process.exit(0);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// v3.17.0: 生成规则模板
|
|
863
|
+
if (options.createRule) {
|
|
864
|
+
const ruleId = typeof options.createRule === "string" ? options.createRule : undefined;
|
|
865
|
+
if (!ruleId) {
|
|
866
|
+
console.log(pc.yellow("⚠️ 请提供规则 ID,例如: --create-rule no-console-log"));
|
|
867
|
+
process.exit(1);
|
|
868
|
+
}
|
|
869
|
+
try {
|
|
870
|
+
const result = generateRuleTemplate({
|
|
871
|
+
targetDir: resolve(options.projectDir, options.createRuleDir),
|
|
872
|
+
ruleId,
|
|
873
|
+
category: options.createRuleCategory,
|
|
874
|
+
severity: options.createRuleSeverity,
|
|
875
|
+
includeFix: options.createRuleFix,
|
|
876
|
+
language: options.createRuleLang,
|
|
877
|
+
});
|
|
878
|
+
console.log(pc.cyan("📝 规则模板已生成"));
|
|
879
|
+
console.log(pc.green(` ✅ 规则文件: ${result.rulePath}`));
|
|
880
|
+
console.log(pc.green(` ✅ 测试文件: ${result.testPath}`));
|
|
881
|
+
process.exit(0);
|
|
882
|
+
} catch (err) {
|
|
883
|
+
console.error(pc.red("❌ 生成规则模板失败:"), err instanceof Error ? err.message : String(err));
|
|
884
|
+
process.exit(1);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// v3.17.0: 规则市场索引
|
|
889
|
+
if (options.marketIndex || options.marketIndexJson) {
|
|
890
|
+
const market = await loadMarketIndex({
|
|
891
|
+
url: options.marketIndexUrl,
|
|
892
|
+
projectDir: options.projectDir,
|
|
893
|
+
});
|
|
894
|
+
if (options.marketIndexJson) {
|
|
895
|
+
console.log(formatMarketIndexJson(market));
|
|
896
|
+
} else {
|
|
897
|
+
console.log(formatMarketIndex(market));
|
|
898
|
+
}
|
|
899
|
+
process.exit(0);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// v3.17.0: 规则评分
|
|
903
|
+
if (options.ruleScores || options.ruleScoresJson) {
|
|
904
|
+
const summary = computeRuleScores(options.projectDir);
|
|
905
|
+
if (options.ruleScoresJson) {
|
|
906
|
+
console.log(formatRuleScoresJson(summary));
|
|
907
|
+
} else {
|
|
908
|
+
console.log(formatRuleScores(summary));
|
|
909
|
+
}
|
|
910
|
+
process.exit(0);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// v3.18.0: 规则文档生成
|
|
914
|
+
if (options.generateRuleDocs) {
|
|
915
|
+
try {
|
|
916
|
+
const engine = createEngine({ projectDir: options.projectDir, configFile: options.configFile });
|
|
917
|
+
const rules = engine.getRules ? engine.getRules() : Object.values(MODULE_RULES).flat();
|
|
918
|
+
const result = generateRuleDocs({
|
|
919
|
+
outputDir: resolve(options.projectDir, options.generateRuleDocsDir),
|
|
920
|
+
rules,
|
|
921
|
+
});
|
|
922
|
+
console.log(pc.cyan(formatGeneratedDocs(result)));
|
|
923
|
+
process.exit(0);
|
|
924
|
+
} catch (err) {
|
|
925
|
+
console.error(pc.red("❌ 生成规则文档失败:"), err instanceof Error ? err.message : String(err));
|
|
926
|
+
process.exit(1);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// v3.18.0: 规则兼容性检查
|
|
931
|
+
if (options.checkRuleCompat) {
|
|
932
|
+
try {
|
|
933
|
+
const engine = createEngine({ projectDir: options.projectDir, configFile: options.configFile });
|
|
934
|
+
const report = engine.checkRuleCompatibility();
|
|
935
|
+
if (options.json) {
|
|
936
|
+
console.log(formatCompatibilityReportJson(report));
|
|
937
|
+
} else {
|
|
938
|
+
console.log(formatCompatibilityReport(report));
|
|
939
|
+
}
|
|
940
|
+
process.exit(isCompatibilityReportClean(report) ? 0 : 2);
|
|
941
|
+
} catch (err) {
|
|
942
|
+
console.error(pc.red("❌ 规则兼容性检查失败:"), err instanceof Error ? err.message : String(err));
|
|
943
|
+
process.exit(1);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
707
947
|
// v3.6.0: E2E 测试覆盖缺口检测
|
|
708
948
|
if (options.e2eDetectGaps) {
|
|
709
949
|
const gapResult = detectE2EGaps({ projectDir: options.projectDir });
|
|
@@ -715,28 +955,49 @@ async function main() {
|
|
|
715
955
|
process.exit(gapResult.uncoveredPages.length + gapResult.uncoveredApis.length > 0 ? 1 : 0);
|
|
716
956
|
}
|
|
717
957
|
|
|
718
|
-
// v3.6.1: 运行 Playwright
|
|
958
|
+
// v3.6.1 / v3.16.0: 运行 E2E 测试(支持 Playwright / Cypress / Selenium / Katalon)
|
|
719
959
|
if (options.e2eRun) {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
960
|
+
const tools = [
|
|
961
|
+
{ name: "Playwright", integration: playwrightIntegration },
|
|
962
|
+
{ name: "Cypress", integration: cypressIntegration },
|
|
963
|
+
{ name: "Selenium", integration: seleniumIntegration },
|
|
964
|
+
{ name: "Katalon", integration: katalonIntegration },
|
|
965
|
+
];
|
|
966
|
+
|
|
967
|
+
let selected;
|
|
968
|
+
if (options.e2eTool === "auto") {
|
|
969
|
+
selected = tools.find((t) => t.integration.isAvailable(options.projectDir));
|
|
970
|
+
} else {
|
|
971
|
+
selected = tools.find((t) => t.name.toLowerCase() === options.e2eTool.toLowerCase());
|
|
972
|
+
if (selected && !selected.integration.isAvailable(options.projectDir)) {
|
|
973
|
+
console.log(pc.yellow(`⚠️ 未检测到 ${selected.name} 配置或依赖`));
|
|
974
|
+
process.exit(1);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (!selected) {
|
|
979
|
+
if (options.e2eTool === "auto") {
|
|
980
|
+
console.log(pc.yellow("⚠️ 未检测到任何支持的 E2E 工具(Playwright / Cypress / Selenium / Katalon)"));
|
|
981
|
+
} else {
|
|
982
|
+
console.log(pc.yellow(`⚠️ 未知的 E2E 工具: ${options.e2eTool}`));
|
|
983
|
+
}
|
|
984
|
+
console.log(pc.gray(" 支持的工具: auto | playwright | cypress | selenium | katalon"));
|
|
724
985
|
process.exit(1);
|
|
725
986
|
}
|
|
726
987
|
|
|
727
|
-
console.log(pc.cyan(
|
|
988
|
+
console.log(pc.cyan(`🎭 正在运行 ${selected.name} E2E 测试...`));
|
|
728
989
|
console.log(pc.gray(" 这可能需要几分钟(取决于测试数量和浏览器启动时间)"));
|
|
729
990
|
console.log("");
|
|
730
991
|
|
|
731
992
|
const start = Date.now();
|
|
732
|
-
const issues =
|
|
993
|
+
const issues = selected.integration.run(options.projectDir);
|
|
733
994
|
const duration = Date.now() - start;
|
|
734
995
|
|
|
735
996
|
if (options.json) {
|
|
736
997
|
console.log(
|
|
737
998
|
JSON.stringify(
|
|
738
999
|
{
|
|
739
|
-
tool:
|
|
1000
|
+
tool: selected.name,
|
|
740
1001
|
total: issues.length,
|
|
741
1002
|
duration,
|
|
742
1003
|
issues,
|
|
@@ -746,12 +1007,12 @@ async function main() {
|
|
|
746
1007
|
)
|
|
747
1008
|
);
|
|
748
1009
|
} else {
|
|
749
|
-
console.log(pc.cyan(`📊
|
|
1010
|
+
console.log(pc.cyan(`📊 ${selected.name} 测试结果`));
|
|
750
1011
|
console.log(pc.gray(` 耗时: ${duration}ms`));
|
|
751
1012
|
if (issues.length === 0) {
|
|
752
1013
|
console.log(pc.green(` ✅ 所有测试通过`));
|
|
753
1014
|
} else {
|
|
754
|
-
console.log(pc.red(` ❌ ${issues.length}
|
|
1015
|
+
console.log(pc.red(` ❌ ${issues.length} 个测试失败/异常`));
|
|
755
1016
|
for (const issue of issues) {
|
|
756
1017
|
const severityColor = issue.severity === "critical" ? pc.red : pc.yellow;
|
|
757
1018
|
console.log(severityColor(` [${issue.severity.toUpperCase()}] ${issue.title}`));
|
|
@@ -978,7 +1239,7 @@ async function main() {
|
|
|
978
1239
|
process.exit(0);
|
|
979
1240
|
}
|
|
980
1241
|
|
|
981
|
-
// v3.9.0: 智能测试推荐
|
|
1242
|
+
// v3.9.0 / v3.16.0: 智能测试推荐
|
|
982
1243
|
if (options.recommendTests) {
|
|
983
1244
|
const { recommendTests, formatRecommendations, formatRecommendationsJson } = await import("../dist/index.js");
|
|
984
1245
|
const changedFiles = options.files?.map((f) => resolve(options.projectDir, f));
|
|
@@ -995,12 +1256,18 @@ async function main() {
|
|
|
995
1256
|
flipRate: options.flakyThresholdFlipRate,
|
|
996
1257
|
minRuns: options.flakyMinRuns,
|
|
997
1258
|
},
|
|
1259
|
+
includeImpactGraph: options.impactGraph,
|
|
998
1260
|
});
|
|
999
1261
|
|
|
1000
1262
|
if (options.json) {
|
|
1001
1263
|
console.log(JSON.stringify(formatRecommendationsJson(result), null, 2));
|
|
1002
1264
|
} else {
|
|
1003
1265
|
console.log(formatRecommendations(result));
|
|
1266
|
+
if (options.impactGraph && result.impactGraph) {
|
|
1267
|
+
console.log("");
|
|
1268
|
+
console.log(pc.cyan("🕸️ 变更影响图"));
|
|
1269
|
+
console.log(formatImpactGraph(result.impactGraph, options.impactGraphFormat));
|
|
1270
|
+
}
|
|
1004
1271
|
}
|
|
1005
1272
|
|
|
1006
1273
|
process.exit(result.uncoveredChanges.length > 0 ? 1 : 0);
|
|
@@ -1304,7 +1571,30 @@ async function main() {
|
|
|
1304
1571
|
const scanFn = options.module === "all" ? runAllModules : runSingleModule;
|
|
1305
1572
|
// v2.6.0: Watch 模式复用 SmartCache,实现缓存预热
|
|
1306
1573
|
const cacheInstance = options.cache !== false ? new SmartCache(options.projectDir) : undefined;
|
|
1307
|
-
|
|
1574
|
+
// v3.17.0: 需要先创建 engine,以便 watch 模式热重载自定义规则
|
|
1575
|
+
const engine = createEngine({
|
|
1576
|
+
projectDir: options.projectDir,
|
|
1577
|
+
minSeverity: options.minSeverity,
|
|
1578
|
+
files: options.files,
|
|
1579
|
+
exclude: options.exclude,
|
|
1580
|
+
configFile: options.configFile,
|
|
1581
|
+
staged: options.staged,
|
|
1582
|
+
diffRange: options.diffRange,
|
|
1583
|
+
autoScope: options.autoScope,
|
|
1584
|
+
external: options.external,
|
|
1585
|
+
cache: options.cache,
|
|
1586
|
+
cacheInstance,
|
|
1587
|
+
dryRun: options.dryRun,
|
|
1588
|
+
interactive: options.interactive,
|
|
1589
|
+
skipLargeFilesThreshold: options.skipLargeFilesThreshold,
|
|
1590
|
+
strategy: options.strategy,
|
|
1591
|
+
profile: options.profile,
|
|
1592
|
+
showProgress: options.showProgress,
|
|
1593
|
+
});
|
|
1594
|
+
for (const rules of Object.values(MODULE_RULES)) {
|
|
1595
|
+
engine.registerAll(rules);
|
|
1596
|
+
}
|
|
1597
|
+
await runWatchMode(options, scanFn, cacheInstance, runSingleModule, engine);
|
|
1308
1598
|
} else if (options.module === "all") {
|
|
1309
1599
|
await runAllModules(options);
|
|
1310
1600
|
} else {
|
|
@@ -1315,7 +1605,7 @@ async function main() {
|
|
|
1315
1605
|
async function runAllModules(options, cacheInstance) {
|
|
1316
1606
|
console.log(pc.cyan("🛡️ Frontend Guardian Core"));
|
|
1317
1607
|
console.log(pc.gray(` Project: ${options.projectDir}`));
|
|
1318
|
-
console.log(pc.gray(` Module: all (
|
|
1608
|
+
console.log(pc.gray(` Module: all (${MODULES.length} modules)`));
|
|
1319
1609
|
if (options.staged) {
|
|
1320
1610
|
console.log(pc.gray(` Mode: staged (git cached only)`));
|
|
1321
1611
|
} else if (options.diffRange) {
|
|
@@ -1341,6 +1631,9 @@ async function runAllModules(options, cacheInstance) {
|
|
|
1341
1631
|
interactive: options.interactive,
|
|
1342
1632
|
skipLargeFilesThreshold: options.skipLargeFilesThreshold,
|
|
1343
1633
|
strategy: options.strategy,
|
|
1634
|
+
// v3.15.0
|
|
1635
|
+
profile: options.profile,
|
|
1636
|
+
showProgress: options.showProgress,
|
|
1344
1637
|
});
|
|
1345
1638
|
|
|
1346
1639
|
// 注册所有模块的规则
|
|
@@ -1393,6 +1686,11 @@ async function runAllModules(options, cacheInstance) {
|
|
|
1393
1686
|
}
|
|
1394
1687
|
|
|
1395
1688
|
allResults[mod] = result;
|
|
1689
|
+
// v3.15.0: 输出扫描耗时分析
|
|
1690
|
+
if (options.profile && !options.json && result.profile) {
|
|
1691
|
+
console.log(formatScanProfile(result.profile));
|
|
1692
|
+
console.log("");
|
|
1693
|
+
}
|
|
1396
1694
|
totalCritical += result.issues.critical.length;
|
|
1397
1695
|
totalWarning += result.issues.warning.length;
|
|
1398
1696
|
totalSuggestion += result.issues.suggestion.length;
|
|
@@ -1947,6 +2245,9 @@ async function runSingleModule(options, cacheInstance) {
|
|
|
1947
2245
|
interactive: options.interactive,
|
|
1948
2246
|
skipLargeFilesThreshold: options.skipLargeFilesThreshold,
|
|
1949
2247
|
strategy: options.strategy,
|
|
2248
|
+
// v3.15.0
|
|
2249
|
+
profile: options.profile,
|
|
2250
|
+
showProgress: options.showProgress,
|
|
1950
2251
|
});
|
|
1951
2252
|
|
|
1952
2253
|
// 注册规则
|
|
@@ -1978,6 +2279,11 @@ async function runSingleModule(options, cacheInstance) {
|
|
|
1978
2279
|
}
|
|
1979
2280
|
|
|
1980
2281
|
const { issues } = result;
|
|
2282
|
+
// v3.15.0: 输出扫描耗时分析
|
|
2283
|
+
if (options.profile && !options.json && result.profile) {
|
|
2284
|
+
console.log(formatScanProfile(result.profile));
|
|
2285
|
+
console.log("");
|
|
2286
|
+
}
|
|
1981
2287
|
let totalCritical = issues.critical.length;
|
|
1982
2288
|
let totalWarning = issues.warning.length;
|
|
1983
2289
|
let totalSuggestion = issues.suggestion.length;
|
package/bin/fg-lsp.js
CHANGED
|
@@ -27,7 +27,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
27
27
|
} else if (arg === "--severity" || arg === "-s") {
|
|
28
28
|
minSeverity = args[++i] ?? "suggestion";
|
|
29
29
|
} else if (arg === "--help" || arg === "-h") {
|
|
30
|
-
console.log(`Frontend Guardian LSP Server v3.
|
|
30
|
+
console.log(`Frontend Guardian LSP Server v3.20.0
|
|
31
31
|
|
|
32
32
|
Usage: fg-lsp [options]
|
|
33
33
|
|
package/bin/fg-server.js
CHANGED
|
@@ -10,7 +10,7 @@ import { DashboardServer } from "../dist/index.js";
|
|
|
10
10
|
|
|
11
11
|
function showHelp() {
|
|
12
12
|
console.log(`
|
|
13
|
-
Frontend Guardian Dashboard Server v3.
|
|
13
|
+
Frontend Guardian Dashboard Server v3.20.0
|
|
14
14
|
|
|
15
15
|
Usage:
|
|
16
16
|
fg-server [options]
|
|
@@ -62,7 +62,7 @@ async function main() {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
console.log(pc.cyan("Frontend Guardian Dashboard Server"));
|
|
65
|
-
console.log(pc.gray(` Version: 3.
|
|
65
|
+
console.log(pc.gray(` Version: 3.20.0`));
|
|
66
66
|
console.log("");
|
|
67
67
|
|
|
68
68
|
const server = new DashboardServer({
|