frontend-guardian-core 3.14.0 → 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 +349 -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/page-health.d.ts +15 -0
- package/dist/integrations/page-health.d.ts.map +1 -1
- package/dist/integrations/page-health.js +80 -12
- package/dist/integrations/page-health.js.map +1 -1
- 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/agent-preferences.d.ts +50 -0
- package/dist/mcp/agent-preferences.d.ts.map +1 -0
- package/dist/mcp/agent-preferences.js +149 -0
- package/dist/mcp/agent-preferences.js.map +1 -0
- package/dist/mcp/agent-registry.d.ts +8 -0
- package/dist/mcp/agent-registry.d.ts.map +1 -1
- package/dist/mcp/agent-registry.js +25 -1
- package/dist/mcp/agent-registry.js.map +1 -1
- package/dist/mcp/guidance.d.ts +24 -0
- package/dist/mcp/guidance.d.ts.map +1 -0
- package/dist/mcp/guidance.js +86 -0
- package/dist/mcp/guidance.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +27 -1
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +320 -18
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/types.d.ts +33 -2
- 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/ai-vision.d.ts +33 -0
- package/dist/utils/ai-vision.d.ts.map +1 -0
- package/dist/utils/ai-vision.js +172 -0
- package/dist/utils/ai-vision.js.map +1 -0
- 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/dist/utils/visual-regression.d.ts +2 -0
- package/dist/utils/visual-regression.d.ts.map +1 -1
- package/dist/utils/visual-regression.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)
|
|
@@ -186,6 +219,10 @@ Options:
|
|
|
186
219
|
--device <name> 页面健康检查模拟设备(如 "iPhone 14 Pro",配合 --page-health)
|
|
187
220
|
--viewport <WxH> 页面健康检查自定义视口(如 390x844,配合 --page-health)
|
|
188
221
|
--viewport-mobile 页面健康检查使用移动端预设视口(配合 --page-health)
|
|
222
|
+
--page-health-ai-vision 启用 LLM Vision 判断截图差异是否为噪声(配合 --page-health,需 FG_AI_API_KEY)
|
|
223
|
+
--page-health-ai-vision-strict AI 判断为噪声时仍上报 visual regression issue(配合 --page-health-ai-vision)
|
|
224
|
+
--page-health-record-video 页面健康检查时录制视频,失败时保留回放(配合 --page-health)
|
|
225
|
+
--page-health-video-dir <dir> 视频保存目录(默认 .frontend-guardian/videos/)
|
|
189
226
|
--mini-program [p] 小程序自动化测试: wechat | alipay | douyin | auto | all | wechat,alipay,... (默认 auto)
|
|
190
227
|
--miniprogram-screenshot 小程序测试时截取首页截图(配合 --mini-program)
|
|
191
228
|
--miniprogram-update-baseline 更新小程序截图基线(配合 --mini-program)
|
|
@@ -207,6 +244,22 @@ Options:
|
|
|
207
244
|
--watch-index 监听文件变更并自动同步索引
|
|
208
245
|
--index-status 查看项目索引状态
|
|
209
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)
|
|
210
263
|
--flaky-threshold-failure-rate <n> flaky 测试失败率阈值(默认 0.2)
|
|
211
264
|
--flaky-threshold-flip-rate <n> flaky 测试状态翻转率阈值(默认 0.15)
|
|
212
265
|
--flaky-min-runs <n> 参与 flaky 计算的最少历史运行次数(默认 3)
|
|
@@ -215,6 +268,8 @@ Options:
|
|
|
215
268
|
|
|
216
269
|
Examples:
|
|
217
270
|
fg-core ./my-project --module all
|
|
271
|
+
fg-core ./my-project --module backend
|
|
272
|
+
fg-core ./my-project --module backend --files "server/**/*.go"
|
|
218
273
|
fg-core ./my-project --module i18n
|
|
219
274
|
fg-core ./my-project --module i18n --severity warning --json
|
|
220
275
|
fg-core ./my-project --module performance --files "src/**/*.tsx"
|
|
@@ -318,6 +373,11 @@ async function main() {
|
|
|
318
373
|
device: undefined,
|
|
319
374
|
viewport: undefined,
|
|
320
375
|
viewportMobile: false,
|
|
376
|
+
// v3.14.1
|
|
377
|
+
pageHealthAIVision: false,
|
|
378
|
+
pageHealthAIVisionStrict: false,
|
|
379
|
+
pageHealthRecordVideo: false,
|
|
380
|
+
pageHealthVideoDir: undefined,
|
|
321
381
|
// v3.11.0
|
|
322
382
|
miniProgram: undefined,
|
|
323
383
|
miniProgramPlatforms: undefined,
|
|
@@ -340,6 +400,30 @@ async function main() {
|
|
|
340
400
|
flakyThresholdFailureRate: undefined,
|
|
341
401
|
flakyThresholdFlipRate: undefined,
|
|
342
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,
|
|
343
427
|
mcp: false,
|
|
344
428
|
};
|
|
345
429
|
|
|
@@ -582,6 +666,19 @@ async function main() {
|
|
|
582
666
|
case "--viewport-mobile":
|
|
583
667
|
options.viewportMobile = true;
|
|
584
668
|
break;
|
|
669
|
+
// v3.14.1
|
|
670
|
+
case "--page-health-ai-vision":
|
|
671
|
+
options.pageHealthAIVision = true;
|
|
672
|
+
break;
|
|
673
|
+
case "--page-health-ai-vision-strict":
|
|
674
|
+
options.pageHealthAIVisionStrict = true;
|
|
675
|
+
break;
|
|
676
|
+
case "--page-health-record-video":
|
|
677
|
+
options.pageHealthRecordVideo = true;
|
|
678
|
+
break;
|
|
679
|
+
case "--page-health-video-dir":
|
|
680
|
+
options.pageHealthVideoDir = args[++i];
|
|
681
|
+
break;
|
|
585
682
|
// v3.11.0 小程序自动化测试
|
|
586
683
|
case "--mini-program": {
|
|
587
684
|
const next = args[i + 1];
|
|
@@ -652,6 +749,65 @@ async function main() {
|
|
|
652
749
|
case "--recommend-tests":
|
|
653
750
|
options.recommendTests = true;
|
|
654
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;
|
|
655
811
|
case "--flaky-threshold-failure-rate":
|
|
656
812
|
options.flakyThresholdFailureRate = parseFloat(args[++i]);
|
|
657
813
|
break;
|
|
@@ -664,6 +820,16 @@ async function main() {
|
|
|
664
820
|
case "--mcp":
|
|
665
821
|
options.mcp = true;
|
|
666
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;
|
|
667
833
|
case "--help":
|
|
668
834
|
case "-h":
|
|
669
835
|
showHelp();
|
|
@@ -682,6 +848,102 @@ async function main() {
|
|
|
682
848
|
return;
|
|
683
849
|
}
|
|
684
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
|
+
|
|
685
947
|
// v3.6.0: E2E 测试覆盖缺口检测
|
|
686
948
|
if (options.e2eDetectGaps) {
|
|
687
949
|
const gapResult = detectE2EGaps({ projectDir: options.projectDir });
|
|
@@ -693,28 +955,49 @@ async function main() {
|
|
|
693
955
|
process.exit(gapResult.uncoveredPages.length + gapResult.uncoveredApis.length > 0 ? 1 : 0);
|
|
694
956
|
}
|
|
695
957
|
|
|
696
|
-
// v3.6.1: 运行 Playwright
|
|
958
|
+
// v3.6.1 / v3.16.0: 运行 E2E 测试(支持 Playwright / Cypress / Selenium / Katalon)
|
|
697
959
|
if (options.e2eRun) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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"));
|
|
702
985
|
process.exit(1);
|
|
703
986
|
}
|
|
704
987
|
|
|
705
|
-
console.log(pc.cyan(
|
|
988
|
+
console.log(pc.cyan(`🎭 正在运行 ${selected.name} E2E 测试...`));
|
|
706
989
|
console.log(pc.gray(" 这可能需要几分钟(取决于测试数量和浏览器启动时间)"));
|
|
707
990
|
console.log("");
|
|
708
991
|
|
|
709
992
|
const start = Date.now();
|
|
710
|
-
const issues =
|
|
993
|
+
const issues = selected.integration.run(options.projectDir);
|
|
711
994
|
const duration = Date.now() - start;
|
|
712
995
|
|
|
713
996
|
if (options.json) {
|
|
714
997
|
console.log(
|
|
715
998
|
JSON.stringify(
|
|
716
999
|
{
|
|
717
|
-
tool:
|
|
1000
|
+
tool: selected.name,
|
|
718
1001
|
total: issues.length,
|
|
719
1002
|
duration,
|
|
720
1003
|
issues,
|
|
@@ -724,12 +1007,12 @@ async function main() {
|
|
|
724
1007
|
)
|
|
725
1008
|
);
|
|
726
1009
|
} else {
|
|
727
|
-
console.log(pc.cyan(`📊
|
|
1010
|
+
console.log(pc.cyan(`📊 ${selected.name} 测试结果`));
|
|
728
1011
|
console.log(pc.gray(` 耗时: ${duration}ms`));
|
|
729
1012
|
if (issues.length === 0) {
|
|
730
1013
|
console.log(pc.green(` ✅ 所有测试通过`));
|
|
731
1014
|
} else {
|
|
732
|
-
console.log(pc.red(` ❌ ${issues.length}
|
|
1015
|
+
console.log(pc.red(` ❌ ${issues.length} 个测试失败/异常`));
|
|
733
1016
|
for (const issue of issues) {
|
|
734
1017
|
const severityColor = issue.severity === "critical" ? pc.red : pc.yellow;
|
|
735
1018
|
console.log(severityColor(` [${issue.severity.toUpperCase()}] ${issue.title}`));
|
|
@@ -796,6 +1079,11 @@ async function main() {
|
|
|
796
1079
|
device: options.device,
|
|
797
1080
|
viewport: options.viewport,
|
|
798
1081
|
viewportMobile: options.viewportMobile,
|
|
1082
|
+
// v3.14.1
|
|
1083
|
+
aiVision: options.pageHealthAIVision,
|
|
1084
|
+
aiVisionStrict: options.pageHealthAIVisionStrict,
|
|
1085
|
+
recordVideo: options.pageHealthRecordVideo,
|
|
1086
|
+
videoDir: options.pageHealthVideoDir,
|
|
799
1087
|
});
|
|
800
1088
|
|
|
801
1089
|
if (options.json) {
|
|
@@ -951,7 +1239,7 @@ async function main() {
|
|
|
951
1239
|
process.exit(0);
|
|
952
1240
|
}
|
|
953
1241
|
|
|
954
|
-
// v3.9.0: 智能测试推荐
|
|
1242
|
+
// v3.9.0 / v3.16.0: 智能测试推荐
|
|
955
1243
|
if (options.recommendTests) {
|
|
956
1244
|
const { recommendTests, formatRecommendations, formatRecommendationsJson } = await import("../dist/index.js");
|
|
957
1245
|
const changedFiles = options.files?.map((f) => resolve(options.projectDir, f));
|
|
@@ -968,12 +1256,18 @@ async function main() {
|
|
|
968
1256
|
flipRate: options.flakyThresholdFlipRate,
|
|
969
1257
|
minRuns: options.flakyMinRuns,
|
|
970
1258
|
},
|
|
1259
|
+
includeImpactGraph: options.impactGraph,
|
|
971
1260
|
});
|
|
972
1261
|
|
|
973
1262
|
if (options.json) {
|
|
974
1263
|
console.log(JSON.stringify(formatRecommendationsJson(result), null, 2));
|
|
975
1264
|
} else {
|
|
976
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
|
+
}
|
|
977
1271
|
}
|
|
978
1272
|
|
|
979
1273
|
process.exit(result.uncoveredChanges.length > 0 ? 1 : 0);
|
|
@@ -1277,7 +1571,30 @@ async function main() {
|
|
|
1277
1571
|
const scanFn = options.module === "all" ? runAllModules : runSingleModule;
|
|
1278
1572
|
// v2.6.0: Watch 模式复用 SmartCache,实现缓存预热
|
|
1279
1573
|
const cacheInstance = options.cache !== false ? new SmartCache(options.projectDir) : undefined;
|
|
1280
|
-
|
|
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);
|
|
1281
1598
|
} else if (options.module === "all") {
|
|
1282
1599
|
await runAllModules(options);
|
|
1283
1600
|
} else {
|
|
@@ -1288,7 +1605,7 @@ async function main() {
|
|
|
1288
1605
|
async function runAllModules(options, cacheInstance) {
|
|
1289
1606
|
console.log(pc.cyan("🛡️ Frontend Guardian Core"));
|
|
1290
1607
|
console.log(pc.gray(` Project: ${options.projectDir}`));
|
|
1291
|
-
console.log(pc.gray(` Module: all (
|
|
1608
|
+
console.log(pc.gray(` Module: all (${MODULES.length} modules)`));
|
|
1292
1609
|
if (options.staged) {
|
|
1293
1610
|
console.log(pc.gray(` Mode: staged (git cached only)`));
|
|
1294
1611
|
} else if (options.diffRange) {
|
|
@@ -1314,6 +1631,9 @@ async function runAllModules(options, cacheInstance) {
|
|
|
1314
1631
|
interactive: options.interactive,
|
|
1315
1632
|
skipLargeFilesThreshold: options.skipLargeFilesThreshold,
|
|
1316
1633
|
strategy: options.strategy,
|
|
1634
|
+
// v3.15.0
|
|
1635
|
+
profile: options.profile,
|
|
1636
|
+
showProgress: options.showProgress,
|
|
1317
1637
|
});
|
|
1318
1638
|
|
|
1319
1639
|
// 注册所有模块的规则
|
|
@@ -1366,6 +1686,11 @@ async function runAllModules(options, cacheInstance) {
|
|
|
1366
1686
|
}
|
|
1367
1687
|
|
|
1368
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
|
+
}
|
|
1369
1694
|
totalCritical += result.issues.critical.length;
|
|
1370
1695
|
totalWarning += result.issues.warning.length;
|
|
1371
1696
|
totalSuggestion += result.issues.suggestion.length;
|
|
@@ -1920,6 +2245,9 @@ async function runSingleModule(options, cacheInstance) {
|
|
|
1920
2245
|
interactive: options.interactive,
|
|
1921
2246
|
skipLargeFilesThreshold: options.skipLargeFilesThreshold,
|
|
1922
2247
|
strategy: options.strategy,
|
|
2248
|
+
// v3.15.0
|
|
2249
|
+
profile: options.profile,
|
|
2250
|
+
showProgress: options.showProgress,
|
|
1923
2251
|
});
|
|
1924
2252
|
|
|
1925
2253
|
// 注册规则
|
|
@@ -1951,6 +2279,11 @@ async function runSingleModule(options, cacheInstance) {
|
|
|
1951
2279
|
}
|
|
1952
2280
|
|
|
1953
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
|
+
}
|
|
1954
2287
|
let totalCritical = issues.critical.length;
|
|
1955
2288
|
let totalWarning = issues.warning.length;
|
|
1956
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({
|