frontend-guardian-core 2.6.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/LICENSE +21 -0
- package/bin/fg-core.js +1238 -0
- package/bin/watch-mode.js +123 -0
- package/dist/engine/cache.d.ts +68 -0
- package/dist/engine/cache.d.ts.map +1 -0
- package/dist/engine/cache.js +164 -0
- package/dist/engine/cache.js.map +1 -0
- package/dist/engine/rule-engine.d.ts +135 -0
- package/dist/engine/rule-engine.d.ts.map +1 -0
- package/dist/engine/rule-engine.js +716 -0
- package/dist/engine/rule-engine.js.map +1 -0
- package/dist/formatters/github-annotation.d.ts +36 -0
- package/dist/formatters/github-annotation.d.ts.map +1 -0
- package/dist/formatters/github-annotation.js +122 -0
- package/dist/formatters/github-annotation.js.map +1 -0
- package/dist/formatters/pr-comment.d.ts +43 -0
- package/dist/formatters/pr-comment.d.ts.map +1 -0
- package/dist/formatters/pr-comment.js +171 -0
- package/dist/formatters/pr-comment.js.map +1 -0
- package/dist/formatters/sarif.d.ts +104 -0
- package/dist/formatters/sarif.d.ts.map +1 -0
- package/dist/formatters/sarif.js +130 -0
- package/dist/formatters/sarif.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/base.d.ts +44 -0
- package/dist/integrations/base.d.ts.map +1 -0
- package/dist/integrations/base.js +104 -0
- package/dist/integrations/base.js.map +1 -0
- package/dist/integrations/eslint.d.ts +8 -0
- package/dist/integrations/eslint.d.ts.map +1 -0
- package/dist/integrations/eslint.js +67 -0
- package/dist/integrations/eslint.js.map +1 -0
- package/dist/integrations/formatter.d.ts +35 -0
- package/dist/integrations/formatter.d.ts.map +1 -0
- package/dist/integrations/formatter.js +182 -0
- package/dist/integrations/formatter.js.map +1 -0
- package/dist/integrations/index.d.ts +17 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +25 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/stylelint.d.ts +8 -0
- package/dist/integrations/stylelint.d.ts.map +1 -0
- package/dist/integrations/stylelint.js +59 -0
- package/dist/integrations/stylelint.js.map +1 -0
- package/dist/integrations/typescript.d.ts +8 -0
- package/dist/integrations/typescript.d.ts.map +1 -0
- package/dist/integrations/typescript.js +92 -0
- package/dist/integrations/typescript.js.map +1 -0
- package/dist/rules/registry.d.ts +83 -0
- package/dist/rules/registry.d.ts.map +1 -0
- package/dist/rules/registry.js +205 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/scanners/a11y-scanner.d.ts +14 -0
- package/dist/scanners/a11y-scanner.d.ts.map +1 -0
- package/dist/scanners/a11y-scanner.js +781 -0
- package/dist/scanners/a11y-scanner.js.map +1 -0
- package/dist/scanners/component-scanner.d.ts +12 -0
- package/dist/scanners/component-scanner.d.ts.map +1 -0
- package/dist/scanners/component-scanner.js +304 -0
- package/dist/scanners/component-scanner.js.map +1 -0
- package/dist/scanners/cross-file-scanner.d.ts +18 -0
- package/dist/scanners/cross-file-scanner.d.ts.map +1 -0
- package/dist/scanners/cross-file-scanner.js +684 -0
- package/dist/scanners/cross-file-scanner.js.map +1 -0
- package/dist/scanners/hooks-scanner.d.ts +15 -0
- package/dist/scanners/hooks-scanner.d.ts.map +1 -0
- package/dist/scanners/hooks-scanner.js +670 -0
- package/dist/scanners/hooks-scanner.js.map +1 -0
- package/dist/scanners/i18n-scanner.d.ts +13 -0
- package/dist/scanners/i18n-scanner.d.ts.map +1 -0
- package/dist/scanners/i18n-scanner.js +535 -0
- package/dist/scanners/i18n-scanner.js.map +1 -0
- package/dist/scanners/naming-scanner.d.ts +19 -0
- package/dist/scanners/naming-scanner.d.ts.map +1 -0
- package/dist/scanners/naming-scanner.js +746 -0
- package/dist/scanners/naming-scanner.js.map +1 -0
- package/dist/scanners/performance-scanner.d.ts +7 -0
- package/dist/scanners/performance-scanner.d.ts.map +1 -0
- package/dist/scanners/performance-scanner.js +402 -0
- package/dist/scanners/performance-scanner.js.map +1 -0
- package/dist/scanners/platform-scanner.d.ts +15 -0
- package/dist/scanners/platform-scanner.d.ts.map +1 -0
- package/dist/scanners/platform-scanner.js +320 -0
- package/dist/scanners/platform-scanner.js.map +1 -0
- package/dist/scanners/security-scanner.d.ts +7 -0
- package/dist/scanners/security-scanner.d.ts.map +1 -0
- package/dist/scanners/security-scanner.js +349 -0
- package/dist/scanners/security-scanner.js.map +1 -0
- package/dist/scanners/svelte-scanner.d.ts +14 -0
- package/dist/scanners/svelte-scanner.d.ts.map +1 -0
- package/dist/scanners/svelte-scanner.js +228 -0
- package/dist/scanners/svelte-scanner.js.map +1 -0
- package/dist/types.d.ts +343 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/ast-parser.d.ts +21 -0
- package/dist/utils/ast-parser.d.ts.map +1 -0
- package/dist/utils/ast-parser.js +119 -0
- package/dist/utils/ast-parser.js.map +1 -0
- package/dist/utils/baseline.d.ts +89 -0
- package/dist/utils/baseline.d.ts.map +1 -0
- package/dist/utils/baseline.js +156 -0
- package/dist/utils/baseline.js.map +1 -0
- package/dist/utils/ci-generator.d.ts +34 -0
- package/dist/utils/ci-generator.d.ts.map +1 -0
- package/dist/utils/ci-generator.js +194 -0
- package/dist/utils/ci-generator.js.map +1 -0
- package/dist/utils/common.d.ts +8 -0
- package/dist/utils/common.d.ts.map +1 -0
- package/dist/utils/common.js +38 -0
- package/dist/utils/common.js.map +1 -0
- package/dist/utils/concurrent.d.ts +16 -0
- package/dist/utils/concurrent.d.ts.map +1 -0
- package/dist/utils/concurrent.js +49 -0
- package/dist/utils/concurrent.js.map +1 -0
- package/dist/utils/config-loader.d.ts +8 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +154 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/dist/utils/fix-bot.d.ts +36 -0
- package/dist/utils/fix-bot.d.ts.map +1 -0
- package/dist/utils/fix-bot.js +274 -0
- package/dist/utils/fix-bot.js.map +1 -0
- package/dist/utils/git-hooks.d.ts +55 -0
- package/dist/utils/git-hooks.d.ts.map +1 -0
- package/dist/utils/git-hooks.js +318 -0
- package/dist/utils/git-hooks.js.map +1 -0
- package/dist/utils/history-report.d.ts +72 -0
- package/dist/utils/history-report.d.ts.map +1 -0
- package/dist/utils/history-report.js +144 -0
- package/dist/utils/history-report.js.map +1 -0
- package/dist/utils/init-config.d.ts +23 -0
- package/dist/utils/init-config.d.ts.map +1 -0
- package/dist/utils/init-config.js +146 -0
- package/dist/utils/init-config.js.map +1 -0
- package/dist/utils/pr-publisher.d.ts +64 -0
- package/dist/utils/pr-publisher.d.ts.map +1 -0
- package/dist/utils/pr-publisher.js +265 -0
- package/dist/utils/pr-publisher.js.map +1 -0
- package/dist/utils/project-detector.d.ts +20 -0
- package/dist/utils/project-detector.d.ts.map +1 -0
- package/dist/utils/project-detector.js +342 -0
- package/dist/utils/project-detector.js.map +1 -0
- package/dist/utils/report-uploader.d.ts +35 -0
- package/dist/utils/report-uploader.d.ts.map +1 -0
- package/dist/utils/report-uploader.js +106 -0
- package/dist/utils/report-uploader.js.map +1 -0
- package/package.json +78 -0
package/bin/fg-core.js
ADDED
|
@@ -0,0 +1,1238 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Frontend Guardian Core CLI
|
|
4
|
+
* Usage: fg-core [options] <project-dir>
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { writeFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import {
|
|
10
|
+
createEngine,
|
|
11
|
+
i18nRules,
|
|
12
|
+
performanceRules,
|
|
13
|
+
a11yRules,
|
|
14
|
+
securityRules,
|
|
15
|
+
namingRules,
|
|
16
|
+
crossFileRules,
|
|
17
|
+
componentRules,
|
|
18
|
+
hooksRules,
|
|
19
|
+
platformRules,
|
|
20
|
+
svelteRules,
|
|
21
|
+
installGitHooks,
|
|
22
|
+
uninstallGitHooks,
|
|
23
|
+
hasGitHook,
|
|
24
|
+
generateCIConfig,
|
|
25
|
+
detectCIProvider,
|
|
26
|
+
generateSarif,
|
|
27
|
+
formatAllAnnotations,
|
|
28
|
+
isGitHubActions,
|
|
29
|
+
writeJobSummary,
|
|
30
|
+
BaselineManager,
|
|
31
|
+
initConfig,
|
|
32
|
+
detectProjectMeta,
|
|
33
|
+
generatePRComment,
|
|
34
|
+
detectPublisherConfig,
|
|
35
|
+
createPublisher,
|
|
36
|
+
uploadReport,
|
|
37
|
+
detectUploadConfig,
|
|
38
|
+
SmartCache,
|
|
39
|
+
runFixBot,
|
|
40
|
+
detectFixBotConfig,
|
|
41
|
+
} from "../dist/index.js";
|
|
42
|
+
import pc from "picocolors";
|
|
43
|
+
import { runWatchMode } from "./watch-mode.js";
|
|
44
|
+
|
|
45
|
+
const MODULES = [
|
|
46
|
+
"i18n",
|
|
47
|
+
"performance",
|
|
48
|
+
"a11y",
|
|
49
|
+
"security",
|
|
50
|
+
"naming",
|
|
51
|
+
"cross-file",
|
|
52
|
+
"component",
|
|
53
|
+
"hooks",
|
|
54
|
+
"platform",
|
|
55
|
+
"svelte",
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const MODULE_RULES = {
|
|
59
|
+
i18n: i18nRules,
|
|
60
|
+
performance: performanceRules,
|
|
61
|
+
a11y: a11yRules,
|
|
62
|
+
security: securityRules,
|
|
63
|
+
naming: namingRules,
|
|
64
|
+
"cross-file": crossFileRules,
|
|
65
|
+
component: componentRules,
|
|
66
|
+
hooks: hooksRules,
|
|
67
|
+
platform: platformRules,
|
|
68
|
+
svelte: svelteRules,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
function showHelp() {
|
|
72
|
+
console.log(`
|
|
73
|
+
Frontend Guardian Core v2.6.0
|
|
74
|
+
|
|
75
|
+
Usage:
|
|
76
|
+
fg-core <project-dir> [options]
|
|
77
|
+
|
|
78
|
+
Options:
|
|
79
|
+
--module <name> ๆซๆๆจกๅ: i18n | performance | a11y | security | naming | cross-file | component | hooks | platform | svelte | all
|
|
80
|
+
--severity <level> ๆไฝไธฅ้็บงๅซ: critical | warning | suggestion (้ป่ฎค: suggestion)
|
|
81
|
+
--files <pattern> ไป
ๆซๆๅน้
็ๆไปถ
|
|
82
|
+
--exclude <pattern> ๆ้คๅน้
็ๆไปถ
|
|
83
|
+
--staged ไป
ๆซๆ git staged ๆไปถ
|
|
84
|
+
--diff <range> ไป
ๆซๆ git diff ่ๅดๅ
็ๆไปถ (ๅฆ main...feature)
|
|
85
|
+
--auto-scope ๆบ่ฝๆซๆ่ๅด๏ผ่ชๅจๆฃๆตๆชๆไบค/ๆ่ฟไฟฎๆน็ๆไปถ
|
|
86
|
+
--no-cluster ็ฆ็จ Issue ่็ฑป
|
|
87
|
+
--json ไปฅ JSON ๆ ผๅผ่พๅบ
|
|
88
|
+
--fix ่ชๅจไฟฎๅคๅฏไฟฎๅค็้ฎ้ข
|
|
89
|
+
--dry-run ไฟฎๅค้ข่งๆจกๅผ๏ผๅฑ็คบ diff ไธๅๅ
ฅๆไปถ๏ผ
|
|
90
|
+
--interactive ไบคไบๅผไฟฎๅค๏ผ้ๆก็กฎ่ฎค๏ผ็ฑปไผผ git add -p๏ผ
|
|
91
|
+
--skip-large-files-threshold <bytes> ๅคงๆไปถ่ทณ่ฟ้ๅผ๏ผ้ป่ฎค 512000 = 500KB๏ผ0 ่กจ็คบไธ่ทณ่ฟ๏ผ
|
|
92
|
+
--format ไฟฎๅคๅ่ชๅจๆ ผๅผๅไปฃ็ ๏ผBiome/Prettier๏ผ
|
|
93
|
+
--output <file> ๅฐๆซๆๆฅๅๅๅ
ฅๆๅฎๆไปถ๏ผMarkdown ๆ ผๅผ๏ผ
|
|
94
|
+
--external ่ฟ่กๅค้จๅทฅๅ
ท้ๆ (ESLint / TypeScript / Stylelint)
|
|
95
|
+
--watch Watch ๆจกๅผ๏ผๆไปถๅๆด่ชๅจๅข้ๆซๆ
|
|
96
|
+
--no-cache ็ฆ็จๆบ่ฝ็ผๅญ
|
|
97
|
+
--config <file> ๆๅฎ้
็ฝฎๆไปถ
|
|
98
|
+
--install-hooks ๅฎ่ฃ
Git hook๏ผ้ป่ฎค pre-commit๏ผๅฏ็จ --install-hooks-type ๆๅฎ๏ผ
|
|
99
|
+
--install-hooks-type <type> hook ็ฑปๅ: pre-commit | pre-push | commit-msg | both | all (้ป่ฎค: pre-commit)
|
|
100
|
+
--init-config ็ๆ .frontend-guardian.yml ้
็ฝฎๆไปถ
|
|
101
|
+
--init-ci ็ๆ CI ้
็ฝฎๆไปถ (่ชๅจๆฃๆตๅนณๅฐ๏ผ้ป่ฎค GitHub Actions)
|
|
102
|
+
--init-ci-provider <p> CI ๅนณๅฐ: github | gitlab | both (้ป่ฎค: ่ชๅจๆฃๆต)
|
|
103
|
+
--sarif <file> ่พๅบ SARIF ๆ ผๅผๆฅๅๅฐๆๅฎๆไปถ
|
|
104
|
+
--github-actions ๅฏ็จ GitHub Actions Annotation ่พๅบ
|
|
105
|
+
--baseline <file> Baseline ๆจกๅผ๏ผไป
ๆฅๅๆฐๅข้ฎ้ข
|
|
106
|
+
--generate-baseline ็ๆ baseline ๆไปถ๏ผ้ๅๆถๆๅฎ --baseline๏ผ
|
|
107
|
+
--post-comment ๅฐๆซๆ็ปๆๅๅธไธบ PR/MR ่ฏ่ฎบ๏ผ่ชๅจๆฃๆต CI ็ฏๅข๏ผ
|
|
108
|
+
--pr-number <n> ๆๅฎ PR/MR ็ผๅท๏ผ้
ๅ --post-comment๏ผ
|
|
109
|
+
--comment-provider <p> ่ฏ่ฎบๅนณๅฐ: github | gitlab๏ผ้
ๅ --post-comment๏ผ
|
|
110
|
+
--upload ไธไผ ๆฅๅๅฐๆๅฎไฝ็ฝฎ๏ผ้้
็ฝฎ FG_UPLOAD_PROVIDER ็ญ็ฏๅขๅ้๏ผ
|
|
111
|
+
--help, -h ๆพ็คบๅธฎๅฉ
|
|
112
|
+
|
|
113
|
+
Examples:
|
|
114
|
+
fg-core ./my-project --module all
|
|
115
|
+
fg-core ./my-project --module i18n
|
|
116
|
+
fg-core ./my-project --module i18n --severity warning --json
|
|
117
|
+
fg-core ./my-project --module performance --files "src/**/*.tsx"
|
|
118
|
+
fg-core ./my-project --module all --fix --json
|
|
119
|
+
fg-core ./my-project --module all --staged
|
|
120
|
+
fg-core ./my-project --module all --diff main...feature
|
|
121
|
+
fg-core ./my-project --install-hooks
|
|
122
|
+
fg-core ./my-project --init-ci
|
|
123
|
+
`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function main() {
|
|
127
|
+
const args = process.argv.slice(2);
|
|
128
|
+
const projectDir = args.find((arg) => !arg.startsWith("-")) || process.cwd();
|
|
129
|
+
|
|
130
|
+
const options = {
|
|
131
|
+
projectDir,
|
|
132
|
+
minSeverity: "suggestion",
|
|
133
|
+
module: "i18n",
|
|
134
|
+
json: false,
|
|
135
|
+
fix: false,
|
|
136
|
+
dryRun: false,
|
|
137
|
+
configFile: undefined,
|
|
138
|
+
files: undefined,
|
|
139
|
+
exclude: undefined,
|
|
140
|
+
staged: false,
|
|
141
|
+
diffRange: undefined,
|
|
142
|
+
cluster: true,
|
|
143
|
+
external: false,
|
|
144
|
+
watch: false,
|
|
145
|
+
cache: true,
|
|
146
|
+
format: false,
|
|
147
|
+
interactive: false,
|
|
148
|
+
skipLargeFilesThreshold: undefined,
|
|
149
|
+
installHooks: false,
|
|
150
|
+
installHooksType: "pre-commit",
|
|
151
|
+
initConfig: false,
|
|
152
|
+
initCi: false,
|
|
153
|
+
initCiProvider: undefined,
|
|
154
|
+
autoScope: false,
|
|
155
|
+
sarif: undefined,
|
|
156
|
+
githubActions: false,
|
|
157
|
+
baseline: undefined,
|
|
158
|
+
generateBaseline: false,
|
|
159
|
+
postComment: false,
|
|
160
|
+
prNumber: undefined,
|
|
161
|
+
commentProvider: undefined,
|
|
162
|
+
upload: false,
|
|
163
|
+
output: undefined,
|
|
164
|
+
fixBot: false,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < args.length; i++) {
|
|
168
|
+
switch (args[i]) {
|
|
169
|
+
case "--module":
|
|
170
|
+
options.module = args[++i];
|
|
171
|
+
break;
|
|
172
|
+
case "--severity":
|
|
173
|
+
options.minSeverity = args[++i];
|
|
174
|
+
break;
|
|
175
|
+
case "--files":
|
|
176
|
+
options.files = args[++i].split(",");
|
|
177
|
+
break;
|
|
178
|
+
case "--exclude":
|
|
179
|
+
options.exclude = args[++i].split(",");
|
|
180
|
+
break;
|
|
181
|
+
case "--config":
|
|
182
|
+
options.configFile = args[++i];
|
|
183
|
+
break;
|
|
184
|
+
case "--diff":
|
|
185
|
+
options.diffRange = args[++i];
|
|
186
|
+
break;
|
|
187
|
+
case "--auto-scope":
|
|
188
|
+
options.autoScope = true;
|
|
189
|
+
break;
|
|
190
|
+
case "--json":
|
|
191
|
+
options.json = true;
|
|
192
|
+
break;
|
|
193
|
+
case "--fix":
|
|
194
|
+
options.fix = true;
|
|
195
|
+
break;
|
|
196
|
+
case "--staged":
|
|
197
|
+
options.staged = true;
|
|
198
|
+
break;
|
|
199
|
+
case "--no-cluster":
|
|
200
|
+
options.cluster = false;
|
|
201
|
+
break;
|
|
202
|
+
case "--external":
|
|
203
|
+
options.external = true;
|
|
204
|
+
break;
|
|
205
|
+
case "--dry-run":
|
|
206
|
+
options.dryRun = true;
|
|
207
|
+
break;
|
|
208
|
+
case "--watch":
|
|
209
|
+
options.watch = true;
|
|
210
|
+
break;
|
|
211
|
+
case "--no-cache":
|
|
212
|
+
options.cache = false;
|
|
213
|
+
break;
|
|
214
|
+
case "--format":
|
|
215
|
+
options.format = true;
|
|
216
|
+
break;
|
|
217
|
+
case "--output":
|
|
218
|
+
options.output = args[++i];
|
|
219
|
+
break;
|
|
220
|
+
case "--interactive":
|
|
221
|
+
options.interactive = true;
|
|
222
|
+
break;
|
|
223
|
+
case "--skip-large-files-threshold":
|
|
224
|
+
options.skipLargeFilesThreshold = parseInt(args[++i], 10);
|
|
225
|
+
break;
|
|
226
|
+
case "--install-hooks":
|
|
227
|
+
options.installHooks = true;
|
|
228
|
+
break;
|
|
229
|
+
case "--install-hooks-type":
|
|
230
|
+
options.installHooksType = args[++i];
|
|
231
|
+
break;
|
|
232
|
+
case "--init-config":
|
|
233
|
+
options.initConfig = true;
|
|
234
|
+
break;
|
|
235
|
+
case "--init-ci":
|
|
236
|
+
options.initCi = true;
|
|
237
|
+
break;
|
|
238
|
+
case "--init-ci-provider":
|
|
239
|
+
options.initCiProvider = args[++i];
|
|
240
|
+
break;
|
|
241
|
+
case "--sarif":
|
|
242
|
+
options.sarif = args[++i];
|
|
243
|
+
break;
|
|
244
|
+
case "--github-actions":
|
|
245
|
+
options.githubActions = true;
|
|
246
|
+
break;
|
|
247
|
+
case "--baseline":
|
|
248
|
+
options.baseline = args[++i];
|
|
249
|
+
break;
|
|
250
|
+
case "--generate-baseline":
|
|
251
|
+
options.generateBaseline = true;
|
|
252
|
+
break;
|
|
253
|
+
case "--post-comment":
|
|
254
|
+
options.postComment = true;
|
|
255
|
+
break;
|
|
256
|
+
case "--pr-number":
|
|
257
|
+
options.prNumber = parseInt(args[++i], 10);
|
|
258
|
+
break;
|
|
259
|
+
case "--comment-provider":
|
|
260
|
+
options.commentProvider = args[++i];
|
|
261
|
+
break;
|
|
262
|
+
case "--upload":
|
|
263
|
+
options.upload = true;
|
|
264
|
+
break;
|
|
265
|
+
case "--fix-bot":
|
|
266
|
+
options.fixBot = true;
|
|
267
|
+
break;
|
|
268
|
+
case "--help":
|
|
269
|
+
case "-h":
|
|
270
|
+
showHelp();
|
|
271
|
+
process.exit(0);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Phase 6: ็นๆฎๅฝไปคๅค็
|
|
276
|
+
if (options.installHooks) {
|
|
277
|
+
const hookType = ["pre-commit", "pre-push", "commit-msg", "both", "all"].includes(options.installHooksType)
|
|
278
|
+
? options.installHooksType
|
|
279
|
+
: "pre-commit";
|
|
280
|
+
const result = installGitHooks(options.projectDir, {
|
|
281
|
+
type: hookType,
|
|
282
|
+
autoFix: false,
|
|
283
|
+
cache: true,
|
|
284
|
+
});
|
|
285
|
+
console.log(pc.cyan("๐ง Git Hook ๅฎ่ฃ
็ปๆ"));
|
|
286
|
+
for (const h of result.installed) {
|
|
287
|
+
console.log(pc.green(` โ
ๅทฒๅฎ่ฃ
: ${h}`));
|
|
288
|
+
}
|
|
289
|
+
for (const h of result.skipped) {
|
|
290
|
+
console.log(pc.yellow(` โ ๏ธ ่ทณ่ฟ: ${h}`));
|
|
291
|
+
}
|
|
292
|
+
if (result.installed.length === 0 && result.skipped.length === 0) {
|
|
293
|
+
console.log(pc.gray(" ๆชๆพๅฐ Git ไปๅบ"));
|
|
294
|
+
}
|
|
295
|
+
process.exit(0);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (options.initConfig) {
|
|
299
|
+
const meta = detectProjectMeta(options.projectDir);
|
|
300
|
+
const result = initConfig(options.projectDir, meta, false);
|
|
301
|
+
console.log(pc.cyan("๐ง ้
็ฝฎๅๅงๅ"));
|
|
302
|
+
if (result.existed) {
|
|
303
|
+
console.log(pc.yellow(` โ ๏ธ ้
็ฝฎๆไปถๅทฒๅญๅจ: ${result.path}`));
|
|
304
|
+
console.log(pc.gray(" ไฝฟ็จ --init-config --force ่ฆ็๏ผๆไฝฟ็จ --init-config ็ดๆฅ่ฆ็๏ผ"));
|
|
305
|
+
} else if (result.created) {
|
|
306
|
+
console.log(pc.green(` โ
ๅทฒๅๅปบ: ${result.path}`));
|
|
307
|
+
console.log(pc.gray(` ๆกๆถ: ${meta.framework ?? "auto-detect"} | ็ปไปถๅบ: ${meta.componentLib ?? "auto-detect"}`));
|
|
308
|
+
}
|
|
309
|
+
process.exit(0);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (options.initCi) {
|
|
313
|
+
let provider = options.initCiProvider;
|
|
314
|
+
if (!provider) {
|
|
315
|
+
provider = detectCIProvider(options.projectDir);
|
|
316
|
+
console.log(pc.gray(` ่ชๅจๆฃๆตๅฐ CI ๅนณๅฐ: ${provider}`));
|
|
317
|
+
}
|
|
318
|
+
const result = generateCIConfig(options.projectDir, {
|
|
319
|
+
provider,
|
|
320
|
+
runTests: true,
|
|
321
|
+
gate: true,
|
|
322
|
+
});
|
|
323
|
+
console.log(pc.cyan("๐ง CI ้
็ฝฎ็ๆ็ปๆ"));
|
|
324
|
+
for (const f of result.created) {
|
|
325
|
+
console.log(pc.green(` โ
ๅทฒๅๅปบ: ${f}`));
|
|
326
|
+
}
|
|
327
|
+
if (result.created.length === 0) {
|
|
328
|
+
console.log(pc.gray(" ๆช็ๆ้
็ฝฎๆไปถ"));
|
|
329
|
+
}
|
|
330
|
+
process.exit(0);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (options.watch) {
|
|
334
|
+
const scanFn = options.module === "all" ? runAllModules : runSingleModule;
|
|
335
|
+
// v2.6.0: Watch ๆจกๅผๅค็จ SmartCache๏ผๅฎ็ฐ็ผๅญ้ข็ญ
|
|
336
|
+
const cacheInstance = options.cache !== false ? new SmartCache(options.projectDir) : undefined;
|
|
337
|
+
await runWatchMode(options, scanFn, cacheInstance);
|
|
338
|
+
} else if (options.module === "all") {
|
|
339
|
+
await runAllModules(options);
|
|
340
|
+
} else {
|
|
341
|
+
await runSingleModule(options);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function runAllModules(options, cacheInstance) {
|
|
346
|
+
console.log(pc.cyan("๐ก๏ธ Frontend Guardian Core"));
|
|
347
|
+
console.log(pc.gray(` Project: ${options.projectDir}`));
|
|
348
|
+
console.log(pc.gray(` Module: all (9 modules)`));
|
|
349
|
+
if (options.staged) {
|
|
350
|
+
console.log(pc.gray(` Mode: staged (git cached only)`));
|
|
351
|
+
} else if (options.diffRange) {
|
|
352
|
+
console.log(pc.gray(` Diff: ${options.diffRange}`));
|
|
353
|
+
} else if (options.autoScope) {
|
|
354
|
+
console.log(pc.gray(` Mode: auto-scope (ๆบ่ฝๆซๆ่ๅด)`));
|
|
355
|
+
}
|
|
356
|
+
console.log("");
|
|
357
|
+
|
|
358
|
+
const engine = createEngine({
|
|
359
|
+
projectDir: options.projectDir,
|
|
360
|
+
minSeverity: options.minSeverity,
|
|
361
|
+
files: options.files,
|
|
362
|
+
exclude: options.exclude,
|
|
363
|
+
configFile: options.configFile,
|
|
364
|
+
staged: options.staged,
|
|
365
|
+
diffRange: options.diffRange,
|
|
366
|
+
autoScope: options.autoScope,
|
|
367
|
+
external: options.external,
|
|
368
|
+
cache: options.cache,
|
|
369
|
+
cacheInstance,
|
|
370
|
+
dryRun: options.dryRun,
|
|
371
|
+
interactive: options.interactive,
|
|
372
|
+
skipLargeFilesThreshold: options.skipLargeFilesThreshold,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// ๆณจๅๆๆๆจกๅ็่งๅ
|
|
376
|
+
for (const rules of Object.values(MODULE_RULES)) {
|
|
377
|
+
engine.registerAll(rules);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const allResults = {};
|
|
381
|
+
let totalCritical = 0;
|
|
382
|
+
let totalWarning = 0;
|
|
383
|
+
let totalSuggestion = 0;
|
|
384
|
+
let totalDuration = 0;
|
|
385
|
+
let totalFilesScanned = 0;
|
|
386
|
+
let totalFilesWithIssues = 0;
|
|
387
|
+
const allFixableIssues = [];
|
|
388
|
+
|
|
389
|
+
// Phase 4: ๅค้จๅทฅๅ
ท้ๆ
|
|
390
|
+
let externalResults = [];
|
|
391
|
+
if (options.external) {
|
|
392
|
+
externalResults = engine.runExternal();
|
|
393
|
+
for (const er of externalResults) {
|
|
394
|
+
for (const issue of er.issues) {
|
|
395
|
+
totalCritical += issue.severity === "critical" ? 1 : 0;
|
|
396
|
+
totalWarning += issue.severity === "warning" ? 1 : 0;
|
|
397
|
+
totalSuggestion += issue.severity === "suggestion" ? 1 : 0;
|
|
398
|
+
totalFilesWithIssues += 1;
|
|
399
|
+
}
|
|
400
|
+
totalDuration += er.duration;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
for (const mod of MODULES) {
|
|
405
|
+
try {
|
|
406
|
+
let result = await engine.scan(mod);
|
|
407
|
+
|
|
408
|
+
// Issue ่็ฑป
|
|
409
|
+
if (options.cluster) {
|
|
410
|
+
result = {
|
|
411
|
+
...result,
|
|
412
|
+
issues: {
|
|
413
|
+
critical: engine.clusterIssues(result.issues.critical),
|
|
414
|
+
warning: engine.clusterIssues(result.issues.warning),
|
|
415
|
+
suggestion: engine.clusterIssues(result.issues.suggestion),
|
|
416
|
+
},
|
|
417
|
+
total:
|
|
418
|
+
engine.clusterIssues(result.issues.critical).length +
|
|
419
|
+
engine.clusterIssues(result.issues.warning).length +
|
|
420
|
+
engine.clusterIssues(result.issues.suggestion).length,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
allResults[mod] = result;
|
|
425
|
+
totalCritical += result.issues.critical.length;
|
|
426
|
+
totalWarning += result.issues.warning.length;
|
|
427
|
+
totalSuggestion += result.issues.suggestion.length;
|
|
428
|
+
totalDuration += result.duration;
|
|
429
|
+
totalFilesScanned = Math.max(totalFilesScanned, result.filesScanned);
|
|
430
|
+
if (result.filesWithIssues > 0) {
|
|
431
|
+
totalFilesWithIssues += result.filesWithIssues;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (options.fix) {
|
|
435
|
+
const moduleIssues = [...result.issues.critical, ...result.issues.warning, ...result.issues.suggestion];
|
|
436
|
+
allFixableIssues.push(...moduleIssues.filter((i) => i.fix));
|
|
437
|
+
}
|
|
438
|
+
} catch (err) {
|
|
439
|
+
console.error(pc.red(` โ [${mod}] ๆซๆๅคฑ่ดฅ:`), err.message || err);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ่ชๅจไฟฎๅค / ไฟฎๅค้ข่ง
|
|
444
|
+
let fixResult = null;
|
|
445
|
+
if ((options.fix || options.dryRun) && allFixableIssues.length > 0) {
|
|
446
|
+
if (!options.json) {
|
|
447
|
+
if (options.dryRun) {
|
|
448
|
+
console.log(pc.cyan("๐ ไฟฎๅค้ข่ง๏ผdry-run ๆจกๅผ๏ผไธไผไฟฎๆนๆไปถ๏ผ๏ผ"));
|
|
449
|
+
} else {
|
|
450
|
+
console.log(pc.cyan("๐ง ๆญฃๅจๅบ็จ่ชๅจไฟฎๅค..."));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
fixResult = engine.applyFixes(allFixableIssues);
|
|
454
|
+
if (!options.json) {
|
|
455
|
+
if (options.dryRun && fixResult.previews) {
|
|
456
|
+
for (const preview of fixResult.previews) {
|
|
457
|
+
console.log(pc.cyan(`\n ๐ ${preview.file}`));
|
|
458
|
+
console.log(pc.yellow(` ${preview.title}`));
|
|
459
|
+
console.log(preview.diff);
|
|
460
|
+
}
|
|
461
|
+
console.log(pc.blue(`\n ๐ก ๅ
ฑ ${fixResult.previews.length} ๅคๅฏไฟฎๅค๏ผไฝฟ็จ --fix ๅบ็จ๏ผ`));
|
|
462
|
+
} else if (fixResult.filesModified.length > 0) {
|
|
463
|
+
console.log(
|
|
464
|
+
pc.green(` โ
ๅทฒไฟฎๅค ${fixResult.fixedCount} ไธช้ฎ้ข๏ผ${fixResult.filesModified.length} ไธชๆไปถ๏ผ`)
|
|
465
|
+
);
|
|
466
|
+
for (const f of fixResult.filesModified) {
|
|
467
|
+
console.log(pc.gray(` - ${f}`));
|
|
468
|
+
}
|
|
469
|
+
if (fixResult.skippedByUser && fixResult.skippedByUser > 0) {
|
|
470
|
+
console.log(pc.yellow(` โญ๏ธ ่ทณ่ฟ ${fixResult.skippedByUser} ไธช้ฎ้ข๏ผไฝ็ฝฎไฟกๅบฆๆ็จๆท้ๆฉ๏ผ`));
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
console.log(pc.yellow(" โ ๏ธ ๆชๅบ็จไปปไฝไฟฎๅค"));
|
|
474
|
+
}
|
|
475
|
+
if (fixResult.errors.length > 0) {
|
|
476
|
+
for (const err of fixResult.errors) {
|
|
477
|
+
console.log(pc.red(` โ ${err}`));
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
console.log("");
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ๆ ผๅผๅ๏ผ--format๏ผ
|
|
485
|
+
let formatResult = null;
|
|
486
|
+
if (options.format && !options.dryRun) {
|
|
487
|
+
const filesToFormat = options.fix && fixResult?.filesModified ? fixResult.filesModified : undefined;
|
|
488
|
+
if (!options.json) {
|
|
489
|
+
console.log(pc.cyan("๐จ ๆญฃๅจๆ ผๅผๅไปฃ็ ..."));
|
|
490
|
+
}
|
|
491
|
+
formatResult = engine.format(filesToFormat);
|
|
492
|
+
if (!options.json) {
|
|
493
|
+
if (formatResult.formatted > 0) {
|
|
494
|
+
console.log(pc.green(` โ
${formatResult.formatter} ๅทฒๆ ผๅผๅ ${formatResult.formatted} ไธชๆไปถ`));
|
|
495
|
+
} else if (formatResult.errors.length === 0) {
|
|
496
|
+
console.log(pc.gray(" ๆๆๆไปถๅทฒๆฏๆไผๆ ผๅผ"));
|
|
497
|
+
}
|
|
498
|
+
if (formatResult.errors.length > 0) {
|
|
499
|
+
for (const err of formatResult.errors) {
|
|
500
|
+
console.log(pc.red(` โ ${err}`));
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
console.log("");
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// v2.6.0: ่ชๅจไฟฎๅค Bot
|
|
508
|
+
if (options.fixBot && fixResult?.filesModified?.length > 0) {
|
|
509
|
+
const botConfig = detectFixBotConfig();
|
|
510
|
+
if (botConfig) {
|
|
511
|
+
if (!options.json) {
|
|
512
|
+
console.log(pc.cyan("๐ค ๆญฃๅจๅๅปบไฟฎๅค PR..."));
|
|
513
|
+
}
|
|
514
|
+
const botResult = await runFixBot(options.projectDir, fixResult.filesModified, botConfig);
|
|
515
|
+
if (!options.json) {
|
|
516
|
+
if (botResult.success) {
|
|
517
|
+
console.log(pc.green(` โ
ไฟฎๅค PR ๅทฒๅๅปบ`));
|
|
518
|
+
console.log(pc.gray(` ๅๆฏ: ${botResult.branch}`));
|
|
519
|
+
if (botResult.prUrl) {
|
|
520
|
+
console.log(pc.gray(` ${botResult.prUrl}`));
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
console.log(pc.red(` โ Fix Bot ๅคฑ่ดฅ: ${botResult.error}`));
|
|
524
|
+
}
|
|
525
|
+
console.log("");
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
if (!options.json) {
|
|
529
|
+
console.log(
|
|
530
|
+
pc.yellow(
|
|
531
|
+
" โ ๏ธ ๆชๆฃๆตๅฐ Fix Bot ้
็ฝฎใ่ฏท่ฎพ็ฝฎ FG_FIX_BOT_PROVIDER ๅ FG_FIX_BOT_TOKEN ็ฏๅขๅ้ใ"
|
|
532
|
+
)
|
|
533
|
+
);
|
|
534
|
+
console.log("");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// v2.3.0: Baseline ๆจกๅผ โโ ็ๆๆๅบ็จ baseline ่ฟๆปค
|
|
540
|
+
let baselineResult = null;
|
|
541
|
+
if (options.baseline) {
|
|
542
|
+
const allIssues = collectAllIssues(allResults, externalResults);
|
|
543
|
+
if (options.generateBaseline) {
|
|
544
|
+
const baseline = new BaselineManager(options.baseline, options.projectDir);
|
|
545
|
+
baseline.save(allIssues, { projectDir: options.projectDir });
|
|
546
|
+
if (!options.json) {
|
|
547
|
+
console.log(pc.cyan(`๐ Baseline ๅทฒ็ๆ: ${baseline.getPath()}`));
|
|
548
|
+
console.log(pc.gray(` ๅ
ๅซ ${allIssues.length} ไธชๅทฒ็ฅ้ฎ้ข`));
|
|
549
|
+
}
|
|
550
|
+
process.exit(0);
|
|
551
|
+
}
|
|
552
|
+
baselineResult = applyBaselineToResults(allResults, externalResults, options.baseline, options.projectDir);
|
|
553
|
+
if (baselineResult.baselineLoaded && !options.json) {
|
|
554
|
+
console.log(
|
|
555
|
+
pc.cyan(
|
|
556
|
+
`๐ Baseline ๆจกๅผ: ๅฟฝ็ฅ ${baselineResult.knownIssues.length} ไธชๅทฒ็ฅ้ฎ้ข๏ผๅ
ณๆณจ ${baselineResult.newIssues.length} ไธชๆฐๅข้ฎ้ข`
|
|
557
|
+
)
|
|
558
|
+
);
|
|
559
|
+
console.log("");
|
|
560
|
+
}
|
|
561
|
+
const newTotals = recalculateTotals(allResults, externalResults);
|
|
562
|
+
totalCritical = newTotals.critical;
|
|
563
|
+
totalWarning = newTotals.warning;
|
|
564
|
+
totalSuggestion = newTotals.suggestion;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// v2.3.0: SARIF ่พๅบ
|
|
568
|
+
if (options.sarif) {
|
|
569
|
+
const allIssues = collectAllIssues(allResults, externalResults);
|
|
570
|
+
const sarif = generateSarif(allIssues, {
|
|
571
|
+
toolName: "Frontend Guardian",
|
|
572
|
+
toolVersion: "2.3.0",
|
|
573
|
+
projectDir: options.projectDir,
|
|
574
|
+
});
|
|
575
|
+
writeFileSync(options.sarif, JSON.stringify(sarif, null, 2), "utf-8");
|
|
576
|
+
if (!options.json && !options.githubActions) {
|
|
577
|
+
console.log(pc.cyan(`๐ SARIF ๆฅๅๅทฒไฟๅญ: ${options.sarif}`));
|
|
578
|
+
console.log(pc.gray(` ๅ
ๅซ ${allIssues.length} ไธช้ฎ้ข`));
|
|
579
|
+
console.log("");
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// v2.3.0: GitHub Actions Annotation
|
|
584
|
+
if (options.githubActions || isGitHubActions()) {
|
|
585
|
+
const allIssues = collectAllIssues(allResults, externalResults);
|
|
586
|
+
if (allIssues.length > 0) {
|
|
587
|
+
const annotations = formatAllAnnotations(allIssues);
|
|
588
|
+
console.log(annotations);
|
|
589
|
+
}
|
|
590
|
+
writeJobSummary(allIssues, { totalFilesScanned, duration: totalDuration });
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// v2.5.0: PR/MR ่ฏ่ฎบ่ชๅจๅๅธ
|
|
594
|
+
if (options.postComment) {
|
|
595
|
+
const commentBody = generatePRComment(
|
|
596
|
+
allResults,
|
|
597
|
+
{
|
|
598
|
+
timestamp: new Date().toISOString(),
|
|
599
|
+
commitSha: process.env.GITHUB_SHA || process.env.CI_COMMIT_SHA,
|
|
600
|
+
duration: totalDuration,
|
|
601
|
+
filesScanned: totalFilesScanned,
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
external: externalResults.length > 0 ? externalResults : undefined,
|
|
605
|
+
fixResult: fixResult
|
|
606
|
+
? { fixedCount: fixResult.fixedCount, filesModified: fixResult.filesModified }
|
|
607
|
+
: null,
|
|
608
|
+
}
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
// ๆๅปบๅๅธๅจ้
็ฝฎ
|
|
612
|
+
let pubConfig = detectPublisherConfig();
|
|
613
|
+
if (!pubConfig && options.commentProvider) {
|
|
614
|
+
const token =
|
|
615
|
+
options.commentProvider === "github"
|
|
616
|
+
? process.env.GITHUB_TOKEN
|
|
617
|
+
: process.env.GITLAB_TOKEN;
|
|
618
|
+
const repo =
|
|
619
|
+
options.commentProvider === "github"
|
|
620
|
+
? process.env.GITHUB_REPOSITORY
|
|
621
|
+
: process.env.CI_PROJECT_ID;
|
|
622
|
+
if (token && repo && options.prNumber) {
|
|
623
|
+
pubConfig = {
|
|
624
|
+
provider: options.commentProvider,
|
|
625
|
+
token,
|
|
626
|
+
repository: repo,
|
|
627
|
+
prNumber: options.prNumber,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (pubConfig) {
|
|
633
|
+
try {
|
|
634
|
+
const publisher = createPublisher(pubConfig);
|
|
635
|
+
const result = await publisher.publish(commentBody);
|
|
636
|
+
if (result.success) {
|
|
637
|
+
console.log(
|
|
638
|
+
pc.cyan(`๐ฌ PR/MR ่ฏ่ฎบๅทฒ${result.action === "updated" ? "ๆดๆฐ" : "ๅๅธ"}`)
|
|
639
|
+
);
|
|
640
|
+
if (result.commentUrl) {
|
|
641
|
+
console.log(pc.gray(` ${result.commentUrl}`));
|
|
642
|
+
}
|
|
643
|
+
} else {
|
|
644
|
+
console.log(pc.red(` โ ่ฏ่ฎบๅๅธๅคฑ่ดฅ: ${result.error}`));
|
|
645
|
+
}
|
|
646
|
+
} catch (err) {
|
|
647
|
+
console.log(pc.red(` โ ่ฏ่ฎบๅๅธๅผๅธธ: ${err.message || err}`));
|
|
648
|
+
}
|
|
649
|
+
} else {
|
|
650
|
+
console.log(
|
|
651
|
+
pc.yellow(
|
|
652
|
+
" โ ๏ธ ๆ ๆณๆฃๆต CI ็ฏๅข๏ผ่ทณ่ฟ่ฏ่ฎบๅๅธใ่ฏทๆฃๆฅ GITHUB_TOKEN / GITLAB_TOKEN ็ฏๅขๅ้๏ผๆๆพๅผๆๅฎ --comment-provider ๅ --pr-number"
|
|
653
|
+
)
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
console.log("");
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// ็ๆๅนถๅๅ
ฅๆฅๅๆไปถ๏ผ--output๏ผ
|
|
660
|
+
let reportPath = options.output;
|
|
661
|
+
if (reportPath) {
|
|
662
|
+
const reportBody = generatePRComment(
|
|
663
|
+
allResults,
|
|
664
|
+
{
|
|
665
|
+
timestamp: new Date().toISOString(),
|
|
666
|
+
commitSha: process.env.GITHUB_SHA || process.env.CI_COMMIT_SHA,
|
|
667
|
+
duration: totalDuration,
|
|
668
|
+
filesScanned: totalFilesScanned,
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
external: externalResults.length > 0 ? externalResults : undefined,
|
|
672
|
+
fixResult: fixResult
|
|
673
|
+
? { fixedCount: fixResult.fixedCount, filesModified: fixResult.filesModified }
|
|
674
|
+
: null,
|
|
675
|
+
}
|
|
676
|
+
);
|
|
677
|
+
writeFileSync(reportPath, reportBody, "utf-8");
|
|
678
|
+
if (!options.json) {
|
|
679
|
+
console.log(pc.cyan("๐ ๆฅๅๅทฒๅๅ
ฅ"));
|
|
680
|
+
console.log(pc.gray(` ${reportPath}`));
|
|
681
|
+
console.log("");
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// v2.5.0: ๆฅๅไธไผ ๏ผ--upload๏ผ
|
|
686
|
+
if (options.upload) {
|
|
687
|
+
const uploadConfig = detectUploadConfig();
|
|
688
|
+
if (uploadConfig) {
|
|
689
|
+
const targetPath = reportPath || (() => {
|
|
690
|
+
const tmpPath = join(process.cwd(), `fg-report-${Date.now()}.md`);
|
|
691
|
+
const reportBody = generatePRComment(
|
|
692
|
+
allResults,
|
|
693
|
+
{
|
|
694
|
+
timestamp: new Date().toISOString(),
|
|
695
|
+
duration: totalDuration,
|
|
696
|
+
filesScanned: totalFilesScanned,
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
external: externalResults.length > 0 ? externalResults : undefined,
|
|
700
|
+
fixResult: fixResult
|
|
701
|
+
? { fixedCount: fixResult.fixedCount, filesModified: fixResult.filesModified }
|
|
702
|
+
: null,
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
writeFileSync(tmpPath, reportBody, "utf-8");
|
|
706
|
+
return tmpPath;
|
|
707
|
+
})();
|
|
708
|
+
const uploadResult = await uploadReport(targetPath, uploadConfig);
|
|
709
|
+
if (uploadResult.success) {
|
|
710
|
+
console.log(pc.cyan("๐ค ๆฅๅๅทฒไธไผ "));
|
|
711
|
+
if (uploadResult.reportUrl) {
|
|
712
|
+
console.log(pc.gray(` ${uploadResult.reportUrl}`));
|
|
713
|
+
}
|
|
714
|
+
} else {
|
|
715
|
+
console.log(pc.red(` โ ไธไผ ๅคฑ่ดฅ: ${uploadResult.error}`));
|
|
716
|
+
}
|
|
717
|
+
console.log("");
|
|
718
|
+
} else {
|
|
719
|
+
console.log(
|
|
720
|
+
pc.yellow(
|
|
721
|
+
" โ ๏ธ ๆชๆฃๆตๅฐไธไผ ้
็ฝฎใ่ฏท่ฎพ็ฝฎ FG_UPLOAD_PROVIDER ๅ FG_UPLOAD_URL / FG_UPLOAD_DIR ็ฏๅขๅ้ใ"
|
|
722
|
+
)
|
|
723
|
+
);
|
|
724
|
+
console.log("");
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (options.json) {
|
|
729
|
+
const jsonOutput = {
|
|
730
|
+
modules: allResults,
|
|
731
|
+
fix: fixResult,
|
|
732
|
+
format: formatResult,
|
|
733
|
+
external: externalResults.length > 0 ? externalResults : undefined,
|
|
734
|
+
summary: {
|
|
735
|
+
totalFilesScanned,
|
|
736
|
+
issuesBySeverity: {
|
|
737
|
+
critical: totalCritical,
|
|
738
|
+
warning: totalWarning,
|
|
739
|
+
suggestion: totalSuggestion,
|
|
740
|
+
},
|
|
741
|
+
totalDuration,
|
|
742
|
+
},
|
|
743
|
+
};
|
|
744
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
745
|
+
process.exit(totalCritical > 0 ? 1 : 0);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Phase 4: ็ป็ซฏ่พๅบๅค้จๅทฅๅ
ท็ปๆ
|
|
749
|
+
if (externalResults.length > 0) {
|
|
750
|
+
console.log(pc.cyan("๐ ๅค้จๅทฅๅ
ทๆฃๆฅ็ปๆ"));
|
|
751
|
+
for (const er of externalResults) {
|
|
752
|
+
if (er.issues.length === 0) continue;
|
|
753
|
+
const color = er.issues.some((i) => i.severity === "critical")
|
|
754
|
+
? pc.red
|
|
755
|
+
: er.issues.some((i) => i.severity === "warning")
|
|
756
|
+
? pc.yellow
|
|
757
|
+
: pc.blue;
|
|
758
|
+
console.log(color(` ${er.tool}: ${er.issues.length} ไธช้ฎ้ข`));
|
|
759
|
+
}
|
|
760
|
+
console.log("");
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// ็ป็ซฏ่พๅบ
|
|
764
|
+
if (totalCritical + totalWarning + totalSuggestion === 0) {
|
|
765
|
+
console.log(pc.green("โ
ๆชๅ็ฐ้ฎ้ข"));
|
|
766
|
+
} else {
|
|
767
|
+
console.log("");
|
|
768
|
+
console.log(pc.cyan("๐ ๆซๆ็ปๆๆฑๆป"));
|
|
769
|
+
console.log(pc.red(` ๐ด Critical: ${totalCritical}`));
|
|
770
|
+
console.log(pc.yellow(` ๐ก Warning: ${totalWarning}`));
|
|
771
|
+
console.log(pc.blue(` ๐ก Suggestion: ${totalSuggestion}`));
|
|
772
|
+
console.log("");
|
|
773
|
+
|
|
774
|
+
for (const mod of MODULES) {
|
|
775
|
+
const r = allResults[mod];
|
|
776
|
+
if (!r || r.total === 0) continue;
|
|
777
|
+
|
|
778
|
+
console.log(pc.cyan(`๐ฆ ${mod}`));
|
|
779
|
+
if (r.issues.critical.length) {
|
|
780
|
+
console.log(pc.red(` ๐ด Critical: ${r.issues.critical.length}`));
|
|
781
|
+
for (const issue of r.issues.critical) {
|
|
782
|
+
printIssue(issue, pc.red);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (r.issues.warning.length) {
|
|
786
|
+
console.log(pc.yellow(` ๐ก Warning: ${r.issues.warning.length}`));
|
|
787
|
+
for (const issue of r.issues.warning) {
|
|
788
|
+
printIssue(issue, pc.yellow);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (r.issues.suggestion.length) {
|
|
792
|
+
console.log(pc.blue(` ๐ก Suggestion: ${r.issues.suggestion.length}`));
|
|
793
|
+
for (const issue of r.issues.suggestion) {
|
|
794
|
+
printIssue(issue, pc.blue);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
console.log("");
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
console.log(
|
|
802
|
+
pc.gray(
|
|
803
|
+
`โฑ๏ธ ๆป่ๆถ: ${totalDuration}ms | ๆซๆ ${totalFilesScanned} ไธชๆไปถ | ${totalFilesWithIssues} ไธชๆไปถๆ้ฎ้ข`
|
|
804
|
+
)
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
if (totalCritical > 0) {
|
|
808
|
+
process.exit(1);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
async function runSingleModule(options, cacheInstance) {
|
|
813
|
+
console.log(pc.cyan("๐ก๏ธ Frontend Guardian Core"));
|
|
814
|
+
console.log(pc.gray(` Project: ${options.projectDir}`));
|
|
815
|
+
console.log(pc.gray(` Module: ${options.module}`));
|
|
816
|
+
if (options.staged) {
|
|
817
|
+
console.log(pc.gray(` Mode: staged (git cached only)`));
|
|
818
|
+
} else if (options.diffRange) {
|
|
819
|
+
console.log(pc.gray(` Diff: ${options.diffRange}`));
|
|
820
|
+
} else if (options.autoScope) {
|
|
821
|
+
console.log(pc.gray(` Mode: auto-scope (ๆบ่ฝๆซๆ่ๅด)`));
|
|
822
|
+
}
|
|
823
|
+
console.log("");
|
|
824
|
+
|
|
825
|
+
const engine = createEngine({
|
|
826
|
+
projectDir: options.projectDir,
|
|
827
|
+
minSeverity: options.minSeverity,
|
|
828
|
+
files: options.files,
|
|
829
|
+
exclude: options.exclude,
|
|
830
|
+
configFile: options.configFile,
|
|
831
|
+
staged: options.staged,
|
|
832
|
+
diffRange: options.diffRange,
|
|
833
|
+
autoScope: options.autoScope,
|
|
834
|
+
cache: options.cache,
|
|
835
|
+
cacheInstance,
|
|
836
|
+
dryRun: options.dryRun,
|
|
837
|
+
interactive: options.interactive,
|
|
838
|
+
skipLargeFilesThreshold: options.skipLargeFilesThreshold,
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
// ๆณจๅ่งๅ
|
|
842
|
+
if (MODULE_RULES[options.module]) {
|
|
843
|
+
engine.registerAll(MODULE_RULES[options.module]);
|
|
844
|
+
} else {
|
|
845
|
+
console.log(pc.red(`โ ๆช็ฅๆจกๅ: ${options.module}`));
|
|
846
|
+
console.log(pc.gray(` ๅฏ็จๆจกๅ: ${MODULES.join(", ")}, all`));
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
let result = await engine.scan(options.module);
|
|
852
|
+
|
|
853
|
+
// Issue ่็ฑป
|
|
854
|
+
if (options.cluster) {
|
|
855
|
+
result = {
|
|
856
|
+
...result,
|
|
857
|
+
issues: {
|
|
858
|
+
critical: engine.clusterIssues(result.issues.critical),
|
|
859
|
+
warning: engine.clusterIssues(result.issues.warning),
|
|
860
|
+
suggestion: engine.clusterIssues(result.issues.suggestion),
|
|
861
|
+
},
|
|
862
|
+
total:
|
|
863
|
+
engine.clusterIssues(result.issues.critical).length +
|
|
864
|
+
engine.clusterIssues(result.issues.warning).length +
|
|
865
|
+
engine.clusterIssues(result.issues.suggestion).length,
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const { issues } = result;
|
|
870
|
+
let totalCritical = issues.critical.length;
|
|
871
|
+
let totalWarning = issues.warning.length;
|
|
872
|
+
let totalSuggestion = issues.suggestion.length;
|
|
873
|
+
|
|
874
|
+
// ่ชๅจไฟฎๅค / ไฟฎๅค้ข่ง
|
|
875
|
+
let fixResult = null;
|
|
876
|
+
if (options.fix || options.dryRun) {
|
|
877
|
+
const allIssues = [...issues.critical, ...issues.warning, ...issues.suggestion];
|
|
878
|
+
const fixableIssues = allIssues.filter((i) => i.fix);
|
|
879
|
+
if (!options.json) {
|
|
880
|
+
if (options.dryRun) {
|
|
881
|
+
console.log(pc.cyan("๐ ไฟฎๅค้ข่ง๏ผdry-run ๆจกๅผ๏ผไธไผไฟฎๆนๆไปถ๏ผ๏ผ"));
|
|
882
|
+
} else {
|
|
883
|
+
console.log(pc.cyan("๐ง ๆญฃๅจๅบ็จ่ชๅจไฟฎๅค..."));
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
fixResult = engine.applyFixes(fixableIssues);
|
|
887
|
+
if (!options.json) {
|
|
888
|
+
if (options.dryRun && fixResult.previews) {
|
|
889
|
+
for (const preview of fixResult.previews) {
|
|
890
|
+
console.log(pc.cyan(`\n ๐ ${preview.file}`));
|
|
891
|
+
console.log(pc.yellow(` ${preview.title}`));
|
|
892
|
+
console.log(preview.diff);
|
|
893
|
+
}
|
|
894
|
+
console.log(pc.blue(`\n ๐ก ๅ
ฑ ${fixResult.previews.length} ๅคๅฏไฟฎๅค๏ผไฝฟ็จ --fix ๅบ็จ๏ผ`));
|
|
895
|
+
} else if (fixResult.filesModified.length > 0) {
|
|
896
|
+
console.log(
|
|
897
|
+
pc.green(
|
|
898
|
+
` โ
ๅทฒไฟฎๅค ${fixResult.fixedCount} ไธช้ฎ้ข๏ผ${fixResult.filesModified.length} ไธชๆไปถ๏ผ`
|
|
899
|
+
)
|
|
900
|
+
);
|
|
901
|
+
for (const f of fixResult.filesModified) {
|
|
902
|
+
console.log(pc.gray(` - ${f}`));
|
|
903
|
+
}
|
|
904
|
+
if (fixResult.skippedByUser && fixResult.skippedByUser > 0) {
|
|
905
|
+
console.log(pc.yellow(` โญ๏ธ ่ทณ่ฟ ${fixResult.skippedByUser} ไธช้ฎ้ข๏ผไฝ็ฝฎไฟกๅบฆๆ็จๆท้ๆฉ๏ผ`));
|
|
906
|
+
}
|
|
907
|
+
} else {
|
|
908
|
+
console.log(pc.yellow(" โ ๏ธ ๆชๅบ็จไปปไฝไฟฎๅค"));
|
|
909
|
+
}
|
|
910
|
+
if (fixResult.errors.length > 0) {
|
|
911
|
+
for (const err of fixResult.errors) {
|
|
912
|
+
console.log(pc.red(` โ ${err}`));
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
console.log("");
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// ๆ ผๅผๅ๏ผ--format๏ผ
|
|
920
|
+
let formatResult = null;
|
|
921
|
+
if (options.format && !options.dryRun) {
|
|
922
|
+
const filesToFormat = options.fix && fixResult?.filesModified ? fixResult.filesModified : undefined;
|
|
923
|
+
if (!options.json) {
|
|
924
|
+
console.log(pc.cyan("๐จ ๆญฃๅจๆ ผๅผๅไปฃ็ ..."));
|
|
925
|
+
}
|
|
926
|
+
formatResult = engine.format(filesToFormat);
|
|
927
|
+
if (!options.json) {
|
|
928
|
+
if (formatResult.formatted > 0) {
|
|
929
|
+
console.log(pc.green(` โ
${formatResult.formatter} ๅทฒๆ ผๅผๅ ${formatResult.formatted} ไธชๆไปถ`));
|
|
930
|
+
} else if (formatResult.errors.length === 0) {
|
|
931
|
+
console.log(pc.gray(" ๆๆๆไปถๅทฒๆฏๆไผๆ ผๅผ"));
|
|
932
|
+
}
|
|
933
|
+
if (formatResult.errors.length > 0) {
|
|
934
|
+
for (const err of formatResult.errors) {
|
|
935
|
+
console.log(pc.red(` โ ${err}`));
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
console.log("");
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// v2.6.0: ่ชๅจไฟฎๅค Bot
|
|
943
|
+
if (options.fixBot && fixResult?.filesModified?.length > 0) {
|
|
944
|
+
const botConfig = detectFixBotConfig();
|
|
945
|
+
if (botConfig) {
|
|
946
|
+
if (!options.json) {
|
|
947
|
+
console.log(pc.cyan("๐ค ๆญฃๅจๅๅปบไฟฎๅค PR..."));
|
|
948
|
+
}
|
|
949
|
+
const botResult = await runFixBot(options.projectDir, fixResult.filesModified, botConfig);
|
|
950
|
+
if (!options.json) {
|
|
951
|
+
if (botResult.success) {
|
|
952
|
+
console.log(pc.green(` โ
ไฟฎๅค PR ๅทฒๅๅปบ`));
|
|
953
|
+
console.log(pc.gray(` ๅๆฏ: ${botResult.branch}`));
|
|
954
|
+
if (botResult.prUrl) {
|
|
955
|
+
console.log(pc.gray(` ${botResult.prUrl}`));
|
|
956
|
+
}
|
|
957
|
+
} else {
|
|
958
|
+
console.log(pc.red(` โ Fix Bot ๅคฑ่ดฅ: ${botResult.error}`));
|
|
959
|
+
}
|
|
960
|
+
console.log("");
|
|
961
|
+
}
|
|
962
|
+
} else {
|
|
963
|
+
if (!options.json) {
|
|
964
|
+
console.log(
|
|
965
|
+
pc.yellow(
|
|
966
|
+
" โ ๏ธ ๆชๆฃๆตๅฐ Fix Bot ้
็ฝฎใ่ฏท่ฎพ็ฝฎ FG_FIX_BOT_PROVIDER ๅ FG_FIX_BOT_TOKEN ็ฏๅขๅ้ใ"
|
|
967
|
+
)
|
|
968
|
+
);
|
|
969
|
+
console.log("");
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// v2.3.0: Baseline ๆจกๅผ
|
|
975
|
+
let allIssues = [...issues.critical, ...issues.warning, ...issues.suggestion];
|
|
976
|
+
if (options.baseline) {
|
|
977
|
+
if (options.generateBaseline) {
|
|
978
|
+
const baseline = new BaselineManager(options.baseline, options.projectDir);
|
|
979
|
+
baseline.save(allIssues, { projectDir: options.projectDir });
|
|
980
|
+
if (!options.json) {
|
|
981
|
+
console.log(pc.cyan(`๐ Baseline ๅทฒ็ๆ: ${baseline.getPath()}`));
|
|
982
|
+
console.log(pc.gray(` ๅ
ๅซ ${allIssues.length} ไธชๅทฒ็ฅ้ฎ้ข`));
|
|
983
|
+
}
|
|
984
|
+
process.exit(0);
|
|
985
|
+
}
|
|
986
|
+
const baseline = new BaselineManager(options.baseline, options.projectDir);
|
|
987
|
+
const baselineResult = baseline.filterNewIssues(allIssues);
|
|
988
|
+
if (baselineResult.baselineLoaded && !options.json) {
|
|
989
|
+
console.log(
|
|
990
|
+
pc.cyan(
|
|
991
|
+
`๐ Baseline ๆจกๅผ: ๅฟฝ็ฅ ${baselineResult.knownIssues.length} ไธชๅทฒ็ฅ้ฎ้ข๏ผๅ
ณๆณจ ${baselineResult.newIssues.length} ไธชๆฐๅข้ฎ้ข`
|
|
992
|
+
)
|
|
993
|
+
);
|
|
994
|
+
console.log("");
|
|
995
|
+
}
|
|
996
|
+
// ่ฟๆปค issues
|
|
997
|
+
const newKeySet = new Set(baselineResult.newIssues.map((i) => `${i.file}|${i.ruleId}|${i.line}`));
|
|
998
|
+
issues.critical = issues.critical.filter((i) => newKeySet.has(`${i.file}|${i.ruleId}|${i.line}`));
|
|
999
|
+
issues.warning = issues.warning.filter((i) => newKeySet.has(`${i.file}|${i.ruleId}|${i.line}`));
|
|
1000
|
+
issues.suggestion = issues.suggestion.filter((i) => newKeySet.has(`${i.file}|${i.ruleId}|${i.line}`));
|
|
1001
|
+
result.total = issues.critical.length + issues.warning.length + issues.suggestion.length;
|
|
1002
|
+
result.filesWithIssues = new Set([...issues.critical, ...issues.warning, ...issues.suggestion].map((i) => i.file)).size;
|
|
1003
|
+
allIssues = baselineResult.newIssues;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// v2.3.0: SARIF ่พๅบ
|
|
1007
|
+
if (options.sarif) {
|
|
1008
|
+
const sarif = generateSarif(allIssues, {
|
|
1009
|
+
toolName: "Frontend Guardian",
|
|
1010
|
+
toolVersion: "2.3.0",
|
|
1011
|
+
projectDir: options.projectDir,
|
|
1012
|
+
});
|
|
1013
|
+
writeFileSync(options.sarif, JSON.stringify(sarif, null, 2), "utf-8");
|
|
1014
|
+
if (!options.json && !options.githubActions) {
|
|
1015
|
+
console.log(pc.cyan(`๐ SARIF ๆฅๅๅทฒไฟๅญ: ${options.sarif}`));
|
|
1016
|
+
console.log(pc.gray(` ๅ
ๅซ ${allIssues.length} ไธช้ฎ้ข`));
|
|
1017
|
+
console.log("");
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// v2.3.0: GitHub Actions Annotation
|
|
1022
|
+
if (options.githubActions || isGitHubActions()) {
|
|
1023
|
+
if (allIssues.length > 0) {
|
|
1024
|
+
console.log(formatAllAnnotations(allIssues));
|
|
1025
|
+
}
|
|
1026
|
+
writeJobSummary(allIssues, { totalFilesScanned: result.filesScanned, duration: result.duration });
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// v2.5.0: PR/MR ่ฏ่ฎบ่ชๅจๅๅธ
|
|
1030
|
+
if (options.postComment) {
|
|
1031
|
+
const singleResult = { [options.module]: result };
|
|
1032
|
+
const commentBody = generatePRComment(
|
|
1033
|
+
singleResult,
|
|
1034
|
+
{
|
|
1035
|
+
timestamp: new Date().toISOString(),
|
|
1036
|
+
commitSha: process.env.GITHUB_SHA || process.env.CI_COMMIT_SHA,
|
|
1037
|
+
duration: result.duration,
|
|
1038
|
+
filesScanned: result.filesScanned,
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
fixResult: fixResult
|
|
1042
|
+
? { fixedCount: fixResult.fixedCount, filesModified: fixResult.filesModified }
|
|
1043
|
+
: null,
|
|
1044
|
+
}
|
|
1045
|
+
);
|
|
1046
|
+
|
|
1047
|
+
let pubConfig = detectPublisherConfig();
|
|
1048
|
+
if (!pubConfig && options.commentProvider) {
|
|
1049
|
+
const token =
|
|
1050
|
+
options.commentProvider === "github"
|
|
1051
|
+
? process.env.GITHUB_TOKEN
|
|
1052
|
+
: process.env.GITLAB_TOKEN;
|
|
1053
|
+
const repo =
|
|
1054
|
+
options.commentProvider === "github"
|
|
1055
|
+
? process.env.GITHUB_REPOSITORY
|
|
1056
|
+
: process.env.CI_PROJECT_ID;
|
|
1057
|
+
if (token && repo && options.prNumber) {
|
|
1058
|
+
pubConfig = {
|
|
1059
|
+
provider: options.commentProvider,
|
|
1060
|
+
token,
|
|
1061
|
+
repository: repo,
|
|
1062
|
+
prNumber: options.prNumber,
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
if (pubConfig) {
|
|
1068
|
+
try {
|
|
1069
|
+
const publisher = createPublisher(pubConfig);
|
|
1070
|
+
const pubResult = await publisher.publish(commentBody);
|
|
1071
|
+
if (pubResult.success) {
|
|
1072
|
+
console.log(
|
|
1073
|
+
pc.cyan(`๐ฌ PR/MR ่ฏ่ฎบๅทฒ${pubResult.action === "updated" ? "ๆดๆฐ" : "ๅๅธ"}`)
|
|
1074
|
+
);
|
|
1075
|
+
if (pubResult.commentUrl) {
|
|
1076
|
+
console.log(pc.gray(` ${pubResult.commentUrl}`));
|
|
1077
|
+
}
|
|
1078
|
+
} else {
|
|
1079
|
+
console.log(pc.red(` โ ่ฏ่ฎบๅๅธๅคฑ่ดฅ: ${pubResult.error}`));
|
|
1080
|
+
}
|
|
1081
|
+
} catch (err) {
|
|
1082
|
+
console.log(pc.red(` โ ่ฏ่ฎบๅๅธๅผๅธธ: ${err.message || err}`));
|
|
1083
|
+
}
|
|
1084
|
+
} else {
|
|
1085
|
+
console.log(
|
|
1086
|
+
pc.yellow(
|
|
1087
|
+
" โ ๏ธ ๆ ๆณๆฃๆต CI ็ฏๅข๏ผ่ทณ่ฟ่ฏ่ฎบๅๅธใ่ฏทๆฃๆฅ็ฏๅขๅ้๏ผๆๆพๅผๆๅฎ --comment-provider ๅ --pr-number"
|
|
1088
|
+
)
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
console.log("");
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (options.json) {
|
|
1095
|
+
const jsonOutput = {
|
|
1096
|
+
...result,
|
|
1097
|
+
fix: fixResult,
|
|
1098
|
+
format: formatResult,
|
|
1099
|
+
};
|
|
1100
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
1101
|
+
process.exit(issues.critical.length > 0 ? 1 : 0);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// ็ป็ซฏ่พๅบ๏ผbaseline ่ฟๆปคๅ้ๆฐ่ฎก็ฎ totals๏ผ
|
|
1105
|
+
totalCritical = issues.critical.length;
|
|
1106
|
+
totalWarning = issues.warning.length;
|
|
1107
|
+
totalSuggestion = issues.suggestion.length;
|
|
1108
|
+
|
|
1109
|
+
if (totalCritical + totalWarning + totalSuggestion === 0) {
|
|
1110
|
+
console.log(pc.green("โ
ๆชๅ็ฐ้ฎ้ข"));
|
|
1111
|
+
} else {
|
|
1112
|
+
console.log(pc.red(`๐ด Critical: ${totalCritical}`));
|
|
1113
|
+
console.log(pc.yellow(`๐ก Warning: ${totalWarning}`));
|
|
1114
|
+
console.log(pc.blue(`๐ก Suggestion: ${totalSuggestion}`));
|
|
1115
|
+
console.log("");
|
|
1116
|
+
|
|
1117
|
+
const allIssues = [...issues.critical, ...issues.warning, ...issues.suggestion];
|
|
1118
|
+
|
|
1119
|
+
for (const issue of allIssues) {
|
|
1120
|
+
printIssue(
|
|
1121
|
+
issue,
|
|
1122
|
+
issue.severity === "critical" ? pc.red : issue.severity === "warning" ? pc.yellow : pc.blue
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
console.log(
|
|
1128
|
+
pc.gray(
|
|
1129
|
+
`โฑ๏ธ ่ๆถ: ${result.duration}ms | ๆซๆ ${result.filesScanned} ไธชๆไปถ | ${result.filesWithIssues} ไธชๆไปถๆ้ฎ้ข`
|
|
1130
|
+
)
|
|
1131
|
+
);
|
|
1132
|
+
|
|
1133
|
+
if (totalCritical > 0) {
|
|
1134
|
+
process.exit(1);
|
|
1135
|
+
}
|
|
1136
|
+
} catch (err) {
|
|
1137
|
+
console.error(pc.red("โ ๆซๆๅคฑ่ดฅ:"), err);
|
|
1138
|
+
process.exit(1);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
function printIssue(issue, colorFn) {
|
|
1143
|
+
console.log(colorFn(` [${issue.severity.toUpperCase()}] ${issue.title}`));
|
|
1144
|
+
console.log(` ๐ ${issue.file}:${issue.line}:${issue.column}`);
|
|
1145
|
+
console.log(` ${issue.description}`);
|
|
1146
|
+
if (issue.source) {
|
|
1147
|
+
console.log(pc.gray(` > ${issue.source}`));
|
|
1148
|
+
}
|
|
1149
|
+
if (issue.docsUrl) {
|
|
1150
|
+
console.log(pc.blue(` ๐ ${issue.docsUrl}`));
|
|
1151
|
+
}
|
|
1152
|
+
console.log("");
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1156
|
+
// v2.3.0: Baseline / SARIF / GitHub Actions ่พ
ๅฉๅฝๆฐ
|
|
1157
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1158
|
+
|
|
1159
|
+
/** ไป allResults ๆถ้ๆๆ issues๏ผๅซๅค้จๅทฅๅ
ท๏ผ */
|
|
1160
|
+
function collectAllIssues(allResults, externalResults) {
|
|
1161
|
+
const issues = [];
|
|
1162
|
+
for (const mod of MODULES) {
|
|
1163
|
+
const r = allResults[mod];
|
|
1164
|
+
if (r) {
|
|
1165
|
+
issues.push(...r.issues.critical, ...r.issues.warning, ...r.issues.suggestion);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
for (const er of externalResults) {
|
|
1169
|
+
issues.push(...er.issues);
|
|
1170
|
+
}
|
|
1171
|
+
return issues;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/** ๅบ็จ baseline ่ฟๆปคๅฐ allResults๏ผ่ฟๅ BaselineResult */
|
|
1175
|
+
function applyBaselineToResults(allResults, externalResults, baselinePath, projectDir) {
|
|
1176
|
+
const allIssues = collectAllIssues(allResults, externalResults);
|
|
1177
|
+
const baseline = new BaselineManager(baselinePath, projectDir);
|
|
1178
|
+
const baselineResult = baseline.filterNewIssues(allIssues);
|
|
1179
|
+
|
|
1180
|
+
if (!baselineResult.baselineLoaded) {
|
|
1181
|
+
return baselineResult;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
const newKeySet = new Set(
|
|
1185
|
+
baselineResult.newIssues.map((i) => `${i.file}|${i.ruleId}|${i.line}`)
|
|
1186
|
+
);
|
|
1187
|
+
|
|
1188
|
+
function isNew(issue) {
|
|
1189
|
+
return newKeySet.has(`${issue.file}|${issue.ruleId}|${issue.line}`);
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// ่ฟๆปคๆฏไธชๆจกๅ็ issues
|
|
1193
|
+
for (const mod of MODULES) {
|
|
1194
|
+
const r = allResults[mod];
|
|
1195
|
+
if (!r) continue;
|
|
1196
|
+
r.issues.critical = r.issues.critical.filter(isNew);
|
|
1197
|
+
r.issues.warning = r.issues.warning.filter(isNew);
|
|
1198
|
+
r.issues.suggestion = r.issues.suggestion.filter(isNew);
|
|
1199
|
+
r.total = r.issues.critical.length + r.issues.warning.length + r.issues.suggestion.length;
|
|
1200
|
+
r.filesWithIssues =
|
|
1201
|
+
new Set([
|
|
1202
|
+
...r.issues.critical,
|
|
1203
|
+
...r.issues.warning,
|
|
1204
|
+
...r.issues.suggestion,
|
|
1205
|
+
].map((i) => i.file)).size;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// ่ฟๆปคๅค้จๅทฅๅ
ท issues
|
|
1209
|
+
for (const er of externalResults) {
|
|
1210
|
+
er.issues = er.issues.filter(isNew);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
return baselineResult;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/** ้ๆฐ่ฎก็ฎ totals */
|
|
1217
|
+
function recalculateTotals(allResults, externalResults) {
|
|
1218
|
+
let critical = 0;
|
|
1219
|
+
let warning = 0;
|
|
1220
|
+
let suggestion = 0;
|
|
1221
|
+
for (const mod of MODULES) {
|
|
1222
|
+
const r = allResults[mod];
|
|
1223
|
+
if (!r) continue;
|
|
1224
|
+
critical += r.issues.critical.length;
|
|
1225
|
+
warning += r.issues.warning.length;
|
|
1226
|
+
suggestion += r.issues.suggestion.length;
|
|
1227
|
+
}
|
|
1228
|
+
for (const er of externalResults) {
|
|
1229
|
+
for (const issue of er.issues) {
|
|
1230
|
+
if (issue.severity === "critical") critical++;
|
|
1231
|
+
else if (issue.severity === "warning") warning++;
|
|
1232
|
+
else suggestion++;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return { critical, warning, suggestion };
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
main();
|