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
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Baseline 管理器
|
|
4
|
+
*
|
|
5
|
+
* 支持已有问题不阻塞,仅关注新增问题的渐进式治理模式。
|
|
6
|
+
* 适用于遗留项目首次接入 frontend-guardian。
|
|
7
|
+
*
|
|
8
|
+
* 使用方式:
|
|
9
|
+
* 1. 首次运行:fg-core --baseline .fg-baseline.json --module all
|
|
10
|
+
* → 扫描全部问题并保存为 baseline 文件
|
|
11
|
+
* 2. 后续运行:fg-core --baseline .fg-baseline.json --module all
|
|
12
|
+
* → 仅报告新增问题(与 baseline 对比)
|
|
13
|
+
* 3. 定期刷新:删除 baseline 重新生成(清理已修复的遗留问题)
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.BaselineManager = void 0;
|
|
17
|
+
exports.toBaselineIssue = toBaselineIssue;
|
|
18
|
+
exports.loadBaseline = loadBaseline;
|
|
19
|
+
exports.saveBaseline = saveBaseline;
|
|
20
|
+
exports.compareWithBaseline = compareWithBaseline;
|
|
21
|
+
exports.generateBaseline = generateBaseline;
|
|
22
|
+
const node_fs_1 = require("node:fs");
|
|
23
|
+
const node_path_1 = require("node:path");
|
|
24
|
+
/** 问题匹配键 */
|
|
25
|
+
function issueKey(issue) {
|
|
26
|
+
// 列号可选,允许小范围偏移(±5列)视为同一问题
|
|
27
|
+
const col = issue.column != null ? Math.round(issue.column / 5) * 5 : 0;
|
|
28
|
+
return `${issue.file}|${issue.ruleId}|${issue.line}|${col}`;
|
|
29
|
+
}
|
|
30
|
+
/** 将 Issue 精简为 BaselineIssue */
|
|
31
|
+
function toBaselineIssue(issue) {
|
|
32
|
+
return {
|
|
33
|
+
file: issue.file,
|
|
34
|
+
ruleId: issue.ruleId,
|
|
35
|
+
line: issue.line,
|
|
36
|
+
column: issue.column,
|
|
37
|
+
severity: issue.severity,
|
|
38
|
+
title: issue.title,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** 加载 baseline 文件 */
|
|
42
|
+
function loadBaseline(filePath) {
|
|
43
|
+
try {
|
|
44
|
+
if (!(0, node_fs_1.existsSync)(filePath)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const raw = (0, node_fs_1.readFileSync)(filePath, "utf-8");
|
|
48
|
+
const data = JSON.parse(raw);
|
|
49
|
+
// 版本校验
|
|
50
|
+
if (!data.version || !Array.isArray(data.issues)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return data;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** 保存 baseline 文件 */
|
|
60
|
+
function saveBaseline(filePath, issues, meta) {
|
|
61
|
+
const baseline = {
|
|
62
|
+
version: "1.0",
|
|
63
|
+
generatedAt: Date.now(),
|
|
64
|
+
issues: issues.map(toBaselineIssue),
|
|
65
|
+
meta: {
|
|
66
|
+
toolVersion: "2.3.0",
|
|
67
|
+
...meta,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
(0, node_fs_1.writeFileSync)(filePath, JSON.stringify(baseline, null, 2), "utf-8");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 将当前扫描结果与 baseline 对比
|
|
74
|
+
* @param issues 当前扫描出的所有问题
|
|
75
|
+
* @param baselinePath baseline 文件路径(绝对或相对)
|
|
76
|
+
* @param projectDir 项目根目录(用于解析相对路径)
|
|
77
|
+
* @returns BaselineResult 对比结果
|
|
78
|
+
*/
|
|
79
|
+
function compareWithBaseline(issues, baselinePath, projectDir) {
|
|
80
|
+
const resolvedPath = (0, node_path_1.resolve)(projectDir, baselinePath);
|
|
81
|
+
const baseline = loadBaseline(resolvedPath);
|
|
82
|
+
if (!baseline) {
|
|
83
|
+
// baseline 文件不存在,返回全部问题作为 newIssues
|
|
84
|
+
return {
|
|
85
|
+
newIssues: issues,
|
|
86
|
+
knownIssues: [],
|
|
87
|
+
fixedIssues: [],
|
|
88
|
+
totalIssues: issues.length,
|
|
89
|
+
baselineLoaded: false,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const baselineKeys = new Set(baseline.issues.map((i) => issueKey(i)));
|
|
93
|
+
const currentKeys = new Set(issues.map((i) => issueKey(i)));
|
|
94
|
+
const newIssues = [];
|
|
95
|
+
const knownIssues = [];
|
|
96
|
+
for (const issue of issues) {
|
|
97
|
+
if (baselineKeys.has(issueKey(issue))) {
|
|
98
|
+
knownIssues.push(issue);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
newIssues.push(issue);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const fixedIssues = baseline.issues.filter((i) => !currentKeys.has(issueKey(i)));
|
|
105
|
+
return {
|
|
106
|
+
newIssues,
|
|
107
|
+
knownIssues,
|
|
108
|
+
fixedIssues,
|
|
109
|
+
totalIssues: issues.length,
|
|
110
|
+
baselineLoaded: true,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 生成或更新 baseline 文件
|
|
115
|
+
* 如果文件已存在则覆盖
|
|
116
|
+
*/
|
|
117
|
+
function generateBaseline(issues, baselinePath, projectDir, meta) {
|
|
118
|
+
const resolvedPath = (0, node_path_1.resolve)(projectDir, baselinePath);
|
|
119
|
+
saveBaseline(resolvedPath, issues, meta);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* BaselineManager — 面向对象的封装
|
|
123
|
+
*/
|
|
124
|
+
class BaselineManager {
|
|
125
|
+
baselinePath;
|
|
126
|
+
projectDir;
|
|
127
|
+
baseline = null;
|
|
128
|
+
constructor(baselinePath, projectDir) {
|
|
129
|
+
this.baselinePath = (0, node_path_1.resolve)(projectDir, baselinePath);
|
|
130
|
+
this.projectDir = projectDir;
|
|
131
|
+
this.baseline = loadBaseline(this.baselinePath);
|
|
132
|
+
}
|
|
133
|
+
/** 是否已加载有效的 baseline */
|
|
134
|
+
isLoaded() {
|
|
135
|
+
return this.baseline !== null;
|
|
136
|
+
}
|
|
137
|
+
/** 获取已加载的 baseline 内容 */
|
|
138
|
+
getBaseline() {
|
|
139
|
+
return this.baseline;
|
|
140
|
+
}
|
|
141
|
+
/** 对比当前 issues,返回仅含新增问题的过滤结果 */
|
|
142
|
+
filterNewIssues(issues) {
|
|
143
|
+
return compareWithBaseline(issues, this.baselinePath, this.projectDir);
|
|
144
|
+
}
|
|
145
|
+
/** 将当前 issues 保存为 baseline(覆盖) */
|
|
146
|
+
save(issues, meta) {
|
|
147
|
+
saveBaseline(this.baselinePath, issues, meta);
|
|
148
|
+
this.baseline = loadBaseline(this.baselinePath);
|
|
149
|
+
}
|
|
150
|
+
/** 获取 baseline 文件路径 */
|
|
151
|
+
getPath() {
|
|
152
|
+
return this.baselinePath;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exports.BaselineManager = BaselineManager;
|
|
156
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/utils/baseline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI 配置自动生成工具
|
|
3
|
+
* 一键生成 GitHub Actions / GitLab CI 配置文件
|
|
4
|
+
*/
|
|
5
|
+
export type CIProvider = "github" | "gitlab" | "both";
|
|
6
|
+
export interface CIGeneratorOptions {
|
|
7
|
+
provider: CIProvider;
|
|
8
|
+
/** Node.js 版本 */
|
|
9
|
+
nodeVersion?: string;
|
|
10
|
+
/** 包管理器 */
|
|
11
|
+
packageManager?: "npm" | "yarn" | "pnpm";
|
|
12
|
+
/** 是否运行测试 */
|
|
13
|
+
runTests?: boolean;
|
|
14
|
+
/** 是否上传报告为 artifact */
|
|
15
|
+
uploadArtifact?: boolean;
|
|
16
|
+
/** 扫描参数 */
|
|
17
|
+
scanArgs?: string;
|
|
18
|
+
/** 门禁配置 */
|
|
19
|
+
gate?: boolean;
|
|
20
|
+
/** 是否在 MR/PR 中自动发布评论 */
|
|
21
|
+
postComment?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 自动检测项目使用的 CI 平台
|
|
25
|
+
* 基于目录结构和 git remote URL 推断
|
|
26
|
+
*/
|
|
27
|
+
export declare function detectCIProvider(projectDir: string): "github" | "gitlab";
|
|
28
|
+
/**
|
|
29
|
+
* 生成 CI 配置文件
|
|
30
|
+
*/
|
|
31
|
+
export declare function generateCIConfig(projectDir: string, options: CIGeneratorOptions): {
|
|
32
|
+
created: string[];
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=ci-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-generator.d.ts","sourceRoot":"","sources":["../../src/utils/ci-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,EAAE,UAAU,CAAC;IACrB,iBAAiB;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACzC,aAAa;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uBAAuB;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,wBAAwB;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAuHD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CA+BxE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAyCvG"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CI 配置自动生成工具
|
|
4
|
+
* 一键生成 GitHub Actions / GitLab CI 配置文件
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.detectCIProvider = detectCIProvider;
|
|
8
|
+
exports.generateCIConfig = generateCIConfig;
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const GITHUB_ACTIONS_TEMPLATE = `name: Frontend Guardian
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
push:
|
|
15
|
+
branches: [main, master]
|
|
16
|
+
pull_request:
|
|
17
|
+
branches: [main, master]
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
scan:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
pull-requests: write
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
{{NODE_SETUP}}
|
|
28
|
+
{{INSTALL}}
|
|
29
|
+
{{TEST}}
|
|
30
|
+
- name: 🛡️ Frontend Guardian Scan
|
|
31
|
+
run: npx fg-core . --scan --gate --output fg-report.md{{ARGS}}
|
|
32
|
+
continue-on-error: true
|
|
33
|
+
|
|
34
|
+
- name: 💬 Post PR Comment
|
|
35
|
+
if: github.event_name == 'pull_request'
|
|
36
|
+
run: npx fg-core . --scan --post-comment{{ARGS}}
|
|
37
|
+
env:
|
|
38
|
+
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
39
|
+
continue-on-error: true
|
|
40
|
+
|
|
41
|
+
- name: 📊 Upload Report
|
|
42
|
+
if: always()
|
|
43
|
+
uses: actions/upload-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: frontend-guardian-report
|
|
46
|
+
path: fg-report.md
|
|
47
|
+
|
|
48
|
+
- name: 🚪 Gate Check
|
|
49
|
+
run: |
|
|
50
|
+
if [ -f fg-report.md ]; then
|
|
51
|
+
echo "报告已生成"
|
|
52
|
+
fi
|
|
53
|
+
# 如果扫描返回非 0 则失败
|
|
54
|
+
npx fg-core . --scan --gate{{ARGS}}
|
|
55
|
+
`;
|
|
56
|
+
const GITLAB_CI_TEMPLATE = `stages:
|
|
57
|
+
- test
|
|
58
|
+
|
|
59
|
+
variables:
|
|
60
|
+
NPM_CONFIG_CACHE: .npm
|
|
61
|
+
|
|
62
|
+
frontend-guardian-scan:
|
|
63
|
+
stage: test
|
|
64
|
+
image: node:{{NODE_VERSION}}-alpine
|
|
65
|
+
rules:
|
|
66
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
67
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
68
|
+
cache:
|
|
69
|
+
key:
|
|
70
|
+
files:
|
|
71
|
+
- {{LOCK_FILE}}
|
|
72
|
+
paths:
|
|
73
|
+
- .npm/
|
|
74
|
+
before_script:
|
|
75
|
+
{{INSTALL}}
|
|
76
|
+
script:
|
|
77
|
+
- npx fg-core . --scan --gate --output fg-report.md --post-comment{{ARGS}}
|
|
78
|
+
artifacts:
|
|
79
|
+
when: always
|
|
80
|
+
paths:
|
|
81
|
+
- fg-report.md
|
|
82
|
+
expire_in: 1 week
|
|
83
|
+
allow_failure: false
|
|
84
|
+
`;
|
|
85
|
+
function renderNodeSetup(pm) {
|
|
86
|
+
if (pm === "pnpm") {
|
|
87
|
+
return ` - uses: pnpm/action-setup@v2\n - uses: actions/setup-node@v4\n with:\n node-version: '{{NODE_VERSION}}'\n cache: 'pnpm'`;
|
|
88
|
+
}
|
|
89
|
+
return ` - uses: actions/setup-node@v4\n with:\n node-version: '{{NODE_VERSION}}'\n cache: '${pm}'`;
|
|
90
|
+
}
|
|
91
|
+
function renderInstall(pm) {
|
|
92
|
+
const cmds = {
|
|
93
|
+
npm: "npm ci",
|
|
94
|
+
yarn: "yarn install --frozen-lockfile",
|
|
95
|
+
pnpm: "pnpm install --frozen-lockfile",
|
|
96
|
+
};
|
|
97
|
+
const cmd = cmds[pm] || cmds.npm;
|
|
98
|
+
return ` - name: 📦 Install Dependencies\n run: ${cmd}`;
|
|
99
|
+
}
|
|
100
|
+
function renderTest(runTests) {
|
|
101
|
+
if (!runTests)
|
|
102
|
+
return "";
|
|
103
|
+
return ` - name: 🧪 Run Tests\n run: npm test\n`;
|
|
104
|
+
}
|
|
105
|
+
function renderGitlabInstall(pm) {
|
|
106
|
+
const cmds = {
|
|
107
|
+
npm: " - npm ci --cache .npm --prefer-offline",
|
|
108
|
+
yarn: " - yarn install --frozen-lockfile",
|
|
109
|
+
pnpm: " - npm install -g pnpm\n - pnpm install --frozen-lockfile",
|
|
110
|
+
};
|
|
111
|
+
return cmds[pm] || cmds.npm;
|
|
112
|
+
}
|
|
113
|
+
function renderLockFile(pm) {
|
|
114
|
+
const files = {
|
|
115
|
+
npm: "package-lock.json",
|
|
116
|
+
yarn: "yarn.lock",
|
|
117
|
+
pnpm: "pnpm-lock.yaml",
|
|
118
|
+
};
|
|
119
|
+
return files[pm] || files.npm;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 自动检测项目使用的 CI 平台
|
|
123
|
+
* 基于目录结构和 git remote URL 推断
|
|
124
|
+
*/
|
|
125
|
+
function detectCIProvider(projectDir) {
|
|
126
|
+
// 1. 检查现有 CI 配置文件
|
|
127
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(projectDir, ".gitlab-ci.yml"))) {
|
|
128
|
+
return "gitlab";
|
|
129
|
+
}
|
|
130
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(projectDir, ".github", "workflows"))) {
|
|
131
|
+
return "github";
|
|
132
|
+
}
|
|
133
|
+
// 2. 检查 git remote URL
|
|
134
|
+
try {
|
|
135
|
+
const configPath = (0, node_path_1.resolve)(projectDir, ".git", "config");
|
|
136
|
+
if ((0, node_fs_1.existsSync)(configPath)) {
|
|
137
|
+
const config = (0, node_fs_1.readFileSync)(configPath, "utf-8");
|
|
138
|
+
const urlMatch = config.match(/\[remote "origin"\]\s*\n\s*url\s*=\s*(.+)/);
|
|
139
|
+
if (urlMatch) {
|
|
140
|
+
const remoteUrl = urlMatch[1].trim();
|
|
141
|
+
if (remoteUrl.includes("gitlab.com") || remoteUrl.includes("gitlab")) {
|
|
142
|
+
return "gitlab";
|
|
143
|
+
}
|
|
144
|
+
if (remoteUrl.includes("github.com") || remoteUrl.includes("github")) {
|
|
145
|
+
return "github";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// 读取失败,忽略
|
|
152
|
+
}
|
|
153
|
+
// 3. 默认 GitHub
|
|
154
|
+
return "github";
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 生成 CI 配置文件
|
|
158
|
+
*/
|
|
159
|
+
function generateCIConfig(projectDir, options) {
|
|
160
|
+
const created = [];
|
|
161
|
+
const nodeVersion = options.nodeVersion || "20";
|
|
162
|
+
const pm = options.packageManager || "npm";
|
|
163
|
+
const args = options.scanArgs ? ` ${options.scanArgs}` : "";
|
|
164
|
+
const providers = options.provider === "both" ? ["github", "gitlab"] : [options.provider];
|
|
165
|
+
for (const provider of providers) {
|
|
166
|
+
if (provider === "github") {
|
|
167
|
+
const workflowDir = (0, node_path_1.resolve)(projectDir, ".github", "workflows");
|
|
168
|
+
if (!(0, node_fs_1.existsSync)(workflowDir)) {
|
|
169
|
+
(0, node_fs_1.mkdirSync)(workflowDir, { recursive: true });
|
|
170
|
+
}
|
|
171
|
+
let content = GITHUB_ACTIONS_TEMPLATE;
|
|
172
|
+
content = content.replace("{{NODE_SETUP}}", renderNodeSetup(pm));
|
|
173
|
+
content = content.replace("{{INSTALL}}", renderInstall(pm));
|
|
174
|
+
content = content.replace("{{TEST}}", renderTest(options.runTests ?? false));
|
|
175
|
+
content = content.replace(/{{NODE_VERSION}}/g, nodeVersion);
|
|
176
|
+
content = content.replace(/{{ARGS}}/g, args);
|
|
177
|
+
const path = (0, node_path_1.resolve)(workflowDir, "frontend-guardian.yml");
|
|
178
|
+
(0, node_fs_1.writeFileSync)(path, content, "utf-8");
|
|
179
|
+
created.push(path);
|
|
180
|
+
}
|
|
181
|
+
if (provider === "gitlab") {
|
|
182
|
+
let content = GITLAB_CI_TEMPLATE;
|
|
183
|
+
content = content.replace(/{{NODE_VERSION}}/g, nodeVersion);
|
|
184
|
+
content = content.replace("{{INSTALL}}", renderGitlabInstall(pm));
|
|
185
|
+
content = content.replace(/{{LOCK_FILE}}/g, renderLockFile(pm));
|
|
186
|
+
content = content.replace(/{{ARGS}}/g, args);
|
|
187
|
+
const path = (0, node_path_1.resolve)(projectDir, ".gitlab-ci.yml");
|
|
188
|
+
(0, node_fs_1.writeFileSync)(path, content, "utf-8");
|
|
189
|
+
created.push(path);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return { created };
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=ci-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-generator.js","sourceRoot":"","sources":["../../src/utils/ci-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.1.0: 公共工具函数 — 从各 scanner 提取,消除代码重复
|
|
3
|
+
*/
|
|
4
|
+
/** 从文件路径提取扩展名 */
|
|
5
|
+
export declare function getFileExt(filePath: string): string;
|
|
6
|
+
/** 从 JSX AST 节点提取标签名(支持 <Component.Sub />) */
|
|
7
|
+
export declare function getJSXTagName(name: any): string | null;
|
|
8
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/utils/common.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,iBAAiB;AACjB,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAqBtD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* v2.1.0: 公共工具函数 — 从各 scanner 提取,消除代码重复
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getFileExt = getFileExt;
|
|
7
|
+
exports.getJSXTagName = getJSXTagName;
|
|
8
|
+
/** 从文件路径提取扩展名 */
|
|
9
|
+
function getFileExt(filePath) {
|
|
10
|
+
const match = filePath.match(/\.[^.]+$/);
|
|
11
|
+
return match ? match[0] : ".js";
|
|
12
|
+
}
|
|
13
|
+
/** 从 JSX AST 节点提取标签名(支持 <Component.Sub />) */
|
|
14
|
+
function getJSXTagName(name) {
|
|
15
|
+
if (name.type === "JSXIdentifier") {
|
|
16
|
+
return name.name;
|
|
17
|
+
}
|
|
18
|
+
if (name.type === "JSXMemberExpression") {
|
|
19
|
+
const parts = [];
|
|
20
|
+
let current = name;
|
|
21
|
+
while (current) {
|
|
22
|
+
if (current.type === "JSXIdentifier") {
|
|
23
|
+
parts.unshift(current.name);
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
else if (current.type === "JSXMemberExpression") {
|
|
27
|
+
parts.unshift(current.property.name);
|
|
28
|
+
current = current.object;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return parts.join(".");
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/utils/common.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAGH,gCAGC;AAGD,sCAqBC;AA5BD,iBAAiB;AACjB,SAAgB,UAAU,CAAC,QAAgB;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACpC,CAAC;AAED,8CAA8C;AAC9C,SAAgB,aAAa,CAAC,IAAS;IACnC,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC,IAAI,CAAC;IACrB,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;QACtC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,OAAO,GAAG,IAAI,CAAC;QACnB,OAAO,OAAO,EAAE,CAAC;YACb,IAAI,OAAO,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;gBACnC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC5B,MAAM;YACV,CAAC;iBAAM,IAAI,OAAO,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;gBAChD,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACrC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACJ,MAAM;YACV,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrent execution utilities — 并发控制工具
|
|
3
|
+
*
|
|
4
|
+
* v2.1.0: 提供受控并发的 Promise.all 替代方案,避免大项目 OOM。
|
|
5
|
+
*/
|
|
6
|
+
/** 获取合理的默认并发数(CPU 核心数) */
|
|
7
|
+
export declare function getDefaultConcurrency(): number;
|
|
8
|
+
/**
|
|
9
|
+
* 受控并发的 map 操作
|
|
10
|
+
* @param items 待处理数组
|
|
11
|
+
* @param concurrency 最大并发数
|
|
12
|
+
* @param fn 处理函数
|
|
13
|
+
* @returns 按原顺序的结果数组
|
|
14
|
+
*/
|
|
15
|
+
export declare function concurrentMap<T, R>(items: T[], concurrency: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
16
|
+
//# sourceMappingURL=concurrent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrent.d.ts","sourceRoot":"","sources":["../../src/utils/concurrent.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,0BAA0B;AAC1B,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,CAAC,EACpC,KAAK,EAAE,CAAC,EAAE,EACV,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3C,OAAO,CAAC,CAAC,EAAE,CAAC,CAuBd"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Concurrent execution utilities — 并发控制工具
|
|
4
|
+
*
|
|
5
|
+
* v2.1.0: 提供受控并发的 Promise.all 替代方案,避免大项目 OOM。
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.getDefaultConcurrency = getDefaultConcurrency;
|
|
9
|
+
exports.concurrentMap = concurrentMap;
|
|
10
|
+
const node_os_1 = require("node:os");
|
|
11
|
+
/** 获取合理的默认并发数(CPU 核心数) */
|
|
12
|
+
function getDefaultConcurrency() {
|
|
13
|
+
try {
|
|
14
|
+
return (0, node_os_1.availableParallelism)();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return 4;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 受控并发的 map 操作
|
|
22
|
+
* @param items 待处理数组
|
|
23
|
+
* @param concurrency 最大并发数
|
|
24
|
+
* @param fn 处理函数
|
|
25
|
+
* @returns 按原顺序的结果数组
|
|
26
|
+
*/
|
|
27
|
+
async function concurrentMap(items, concurrency, fn) {
|
|
28
|
+
if (items.length === 0)
|
|
29
|
+
return [];
|
|
30
|
+
const results = new Array(items.length);
|
|
31
|
+
let index = 0;
|
|
32
|
+
let error = null;
|
|
33
|
+
async function worker() {
|
|
34
|
+
while (index < items.length && !error) {
|
|
35
|
+
const currentIndex = index++;
|
|
36
|
+
try {
|
|
37
|
+
results[currentIndex] = await fn(items[currentIndex], currentIndex);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
error = err;
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());
|
|
46
|
+
await Promise.all(workers);
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=concurrent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrent.js","sourceRoot":"","sources":["../../src/utils/concurrent.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAKH,sDAMC;AASD,sCA2BC;AA7CD,qCAA+C;AAE/C,0BAA0B;AAC1B,SAAgB,qBAAqB;IACjC,IAAI,CAAC;QACD,OAAO,IAAA,8BAAoB,GAAE,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,CAAC,CAAC;IACb,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,aAAa,CAC/B,KAAU,EACV,WAAmB,EACnB,EAA0C;IAE1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,OAAO,GAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAY,IAAI,CAAC;IAE1B,KAAK,UAAU,MAAM;QACjB,OAAO,KAAK,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,MAAM,YAAY,GAAG,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACD,OAAO,CAAC,YAAY,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC;YACxE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,KAAK,GAAG,GAAG,CAAC;gBACZ,MAAM,GAAG,CAAC;YACd,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5F,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAE3B,OAAO,OAAO,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置文件加载工具
|
|
3
|
+
* 支持 .frontend-guardian.yml 和 .frontend-guardian.json
|
|
4
|
+
* 支持 extends 继承组织级基线配置
|
|
5
|
+
*/
|
|
6
|
+
import type { ProjectConfig } from "../types.js";
|
|
7
|
+
export declare function loadConfig(projectDir: string, configFile?: string): ProjectConfig;
|
|
8
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,YAAY,CAAC;AAE5D,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,CAkCjF"}
|