frontend-guardian-core 3.0.0 → 3.4.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 CHANGED
@@ -46,6 +46,9 @@ import {
46
46
  formatWorkspaceJson,
47
47
  AIFixSuggester,
48
48
  detectAIConfig,
49
+ compareHistoryReports,
50
+ formatHistoryCompare,
51
+ formatHistoryCompareJson,
49
52
  } from "../dist/index.js";
50
53
  import pc from "picocolors";
51
54
  import { runWatchMode } from "./watch-mode.js";
@@ -78,12 +81,13 @@ const MODULE_RULES = {
78
81
 
79
82
  function showHelp() {
80
83
  console.log(`
81
- Frontend Guardian Core v3.0.0
84
+ Frontend Guardian Core v3.4.0
82
85
 
83
86
  Usage:
84
87
  fg-core <project-dir> [options]
85
88
 
86
89
  Options:
90
+ --scan 全量扫描(等价于 --module all)
87
91
  --module <name> 扫描模块: i18n | performance | a11y | security | naming | cross-file | component | hooks | platform | svelte | all
88
92
  --severity <level> 最低严重级别: critical | warning | suggestion (默认: suggestion)
89
93
  --files <pattern> 仅扫描匹配的文件
@@ -120,6 +124,7 @@ Options:
120
124
  --history 查看历史扫描记录
121
125
  --history-module <m> 历史记录按模块过滤(配合 --history)
122
126
  --history-limit <n> 历史记录显示条数限制(默认 20)
127
+ --history-compare [c] [p] 对比历史报告:不指定则对比最近两次;指定一个则与该报告对比最近一次;指定两个则对比指定报告
123
128
  --generate-dashboard 生成团队趋势看板 HTML 页面
124
129
  --monorepo 启用 Monorepo 模式:自动检测 workspace 并扫描所有子包
125
130
  --workspace <name> 仅扫描指定 workspace 包(可多次使用,配合 --monorepo)
@@ -185,6 +190,8 @@ async function main() {
185
190
  history: false,
186
191
  historyModule: undefined,
187
192
  historyLimit: 20,
193
+ historyCompare: false,
194
+ historyCompareArgs: [],
188
195
  generateDashboard: false,
189
196
  monorepo: false,
190
197
  workspace: [],
@@ -196,6 +203,9 @@ async function main() {
196
203
 
197
204
  for (let i = 0; i < args.length; i++) {
198
205
  switch (args[i]) {
206
+ case "--scan":
207
+ options.module = "all";
208
+ break;
199
209
  case "--module":
200
210
  options.module = args[++i];
201
211
  break;
@@ -307,6 +317,13 @@ async function main() {
307
317
  case "--history-limit":
308
318
  options.historyLimit = parseInt(args[++i], 10) || 20;
309
319
  break;
320
+ case "--history-compare":
321
+ options.historyCompare = true;
322
+ // 收集后续非 -- 开头的参数作为对比报告引用
323
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
324
+ options.historyCompareArgs.push(args[++i]);
325
+ }
326
+ break;
310
327
  case "--generate-dashboard":
311
328
  options.generateDashboard = true;
312
329
  break;
@@ -421,6 +438,28 @@ async function main() {
421
438
  process.exit(0);
422
439
  }
423
440
 
441
+ // v3.1.0: 历史报告对比
442
+ if (options.historyCompare) {
443
+ const [currentRef, previousRef] = options.historyCompareArgs;
444
+ const result = compareHistoryReports({
445
+ projectDir: options.projectDir,
446
+ current: currentRef,
447
+ previous: previousRef,
448
+ });
449
+
450
+ if (!result) {
451
+ console.log(pc.yellow("⚠️ 暂无历史报告可供对比。请先运行 --save-report 生成历史数据。"));
452
+ process.exit(1);
453
+ }
454
+
455
+ if (options.json) {
456
+ console.log(JSON.stringify(formatHistoryCompareJson(result), null, 2));
457
+ } else {
458
+ console.log(formatHistoryCompare(result));
459
+ }
460
+ process.exit(0);
461
+ }
462
+
424
463
  // v2.8.0: 生成趋势看板
425
464
  if (options.generateDashboard) {
426
465
  const hr = new HistoryReport(options.projectDir);
package/bin/fg-lsp.js ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Frontend Guardian LSP Server
4
+ * Usage: fg-lsp --stdio [--project-dir <dir>] [--config <file>]
5
+ *
6
+ * v3.3.0: Language Server Protocol 实现,为 IDE 提供实时诊断和快速修复。
7
+ */
8
+
9
+ import { runLSPServer } from "../dist/ide/lsp-server.js";
10
+ import { resolve } from "node:path";
11
+ import { existsSync } from "node:fs";
12
+
13
+ const args = process.argv.slice(2);
14
+
15
+ // 解析参数
16
+ let projectDir = process.cwd();
17
+ let configFile;
18
+ let minSeverity = "suggestion";
19
+
20
+ for (let i = 0; i < args.length; i++) {
21
+ const arg = args[i];
22
+ if (arg === "--project-dir" || arg === "-p") {
23
+ projectDir = resolve(args[++i] ?? process.cwd());
24
+ } else if (arg === "--config" || arg === "-c") {
25
+ configFile = resolve(args[++i] ?? "");
26
+ } else if (arg === "--severity" || arg === "-s") {
27
+ minSeverity = args[++i] ?? "suggestion";
28
+ } else if (arg === "--help" || arg === "-h") {
29
+ console.log(`Frontend Guardian LSP Server v3.3.0
30
+
31
+ Usage: fg-lsp [options]
32
+
33
+ Options:
34
+ --project-dir, -p <dir> 项目根目录(默认当前目录)
35
+ --config, -c <file> 配置文件路径
36
+ --severity, -s <level> 最低严重级别: critical|warning|suggestion(默认 suggestion)
37
+ --help, -h 显示帮助
38
+
39
+ Environment:
40
+ FG_PROJECT_DIR 项目根目录(优先级低于 --project-dir)
41
+
42
+ Examples:
43
+ fg-lsp --stdio
44
+ fg-lsp --project-dir ./my-project --config .frontend-guardian.yml
45
+ `);
46
+ process.exit(0);
47
+ }
48
+ }
49
+
50
+ // 环境变量回退
51
+ if (process.env.FG_PROJECT_DIR && !args.includes("--project-dir") && !args.includes("-p")) {
52
+ projectDir = resolve(process.env.FG_PROJECT_DIR);
53
+ }
54
+
55
+ // 验证项目目录
56
+ if (!existsSync(projectDir)) {
57
+ console.error(`Error: Project directory does not exist: ${projectDir}`);
58
+ process.exit(1);
59
+ }
60
+
61
+ // 启动 LSP 服务器
62
+ runLSPServer({
63
+ projectDir,
64
+ configFile,
65
+ minSeverity,
66
+ debounceMs: 300,
67
+ });
@@ -6,6 +6,7 @@
6
6
  * 2. 缓存持久化到 .frontend-guardian/cache.json
7
7
  * 3. 自动过期策略(默认 7 天)
8
8
  * 4. 兼容 --staged / --diff 增量模式
9
+ * 5. v3.2.0: AST 内存缓存 LRU 淘汰策略,防止大项目 OOM
9
10
  */
10
11
  import type { Issue } from "../types.js";
11
12
  export interface CacheEntry {
@@ -35,7 +36,9 @@ export declare class SmartCache {
35
36
  private ttl;
36
37
  /** v2.1.0: 内存级 AST 缓存(无需持久化,进程内复用) */
37
38
  private astCache;
38
- constructor(projectDir: string, ttl?: number);
39
+ /** v3.2.0: AST 缓存最大条目数 */
40
+ private maxAstCacheSize;
41
+ constructor(projectDir: string, ttl?: number, maxAstCacheSize?: number);
39
42
  /** 计算文件内容哈希 */
40
43
  static computeHash(content: string): string;
41
44
  /** 检查文件是否已缓存且未过期 */
@@ -56,10 +59,14 @@ export declare class SmartCache {
56
59
  valid: number;
57
60
  expired: number;
58
61
  };
59
- /** 获取缓存的 AST(内存级,不持久化) */
62
+ /** 获取缓存的 AST(内存级,不持久化)— v3.2.0 增加 LRU 淘汰 */
60
63
  getAst(filePath: string, content: string): unknown | undefined;
61
- /** 缓存 AST 解析结果 */
64
+ /** 缓存 AST 解析结果 — v3.2.0 增加 LRU 淘汰 */
62
65
  setAst(filePath: string, content: string, ast: unknown): void;
66
+ /** v3.2.0: 获取 AST 缓存当前大小 */
67
+ getAstCacheSize(): number;
68
+ /** v3.2.0: 获取 AST 缓存上限 */
69
+ getAstCacheLimit(): number;
63
70
  /** 清理过期缓存 */
64
71
  gc(): number;
65
72
  /** 加载缓存 manifest */
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/engine/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,UAAU;IACvB,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW;IACX,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,YAAY;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC1B,WAAW;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC,eAAe;IACf,YAAY,EAAE,MAAM,CAAC;CACxB;AAOD,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,GAAG,CAAS;IACpB,sCAAsC;IACtC,OAAO,CAAC,QAAQ,CAAqD;gBAEzD,UAAU,EAAE,MAAM,EAAE,GAAG,GAAE,MAAoB;IAOzD,eAAe;IACf,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAI3C,oBAAoB;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAkBpD,kBAAkB;IAClB,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE,GAAG,SAAS;IAK1C,eAAe;IACf,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI;IAS7D,aAAa;IACb,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIlC,uBAAuB;IACvB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IASxC,cAAc;IACd,IAAI,IAAI,IAAI;IAWZ,aAAa;IACb,QAAQ,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAkB7D,0BAA0B;IAC1B,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAW9D,kBAAkB;IAClB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI;IAO7D,aAAa;IACb,EAAE,IAAI,MAAM;IAcZ,oBAAoB;IACpB,OAAO,CAAC,YAAY;CAoBvB"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/engine/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,UAAU;IACvB,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW;IACX,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,YAAY;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC1B,WAAW;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC,eAAe;IACf,YAAY,EAAE,MAAM,CAAC;CACxB;AASD,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,GAAG,CAAS;IACpB,sCAAsC;IACtC,OAAO,CAAC,QAAQ,CAAqD;IACrE,0BAA0B;IAC1B,OAAO,CAAC,eAAe,CAAS;gBAEpB,UAAU,EAAE,MAAM,EAAE,GAAG,GAAE,MAAoB,EAAE,eAAe,CAAC,EAAE,MAAM;IAQnF,eAAe;IACf,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAI3C,oBAAoB;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAkBpD,kBAAkB;IAClB,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE,GAAG,SAAS;IAK1C,eAAe;IACf,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI;IAS7D,aAAa;IACb,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIlC,uBAAuB;IACvB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IASxC,cAAc;IACd,IAAI,IAAI,IAAI;IAWZ,aAAa;IACb,QAAQ,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAkB7D,4CAA4C;IAC5C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAiB9D,qCAAqC;IACrC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI;IAiB7D,4BAA4B;IAC5B,eAAe,IAAI,MAAM;IAIzB,0BAA0B;IAC1B,gBAAgB,IAAI,MAAM;IAI1B,aAAa;IACb,EAAE,IAAI,MAAM;IAcZ,oBAAoB;IACpB,OAAO,CAAC,YAAY;CAoBvB"}
@@ -7,6 +7,7 @@
7
7
  * 2. 缓存持久化到 .frontend-guardian/cache.json
8
8
  * 3. 自动过期策略(默认 7 天)
9
9
  * 4. 兼容 --staged / --diff 增量模式
10
+ * 5. v3.2.0: AST 内存缓存 LRU 淘汰策略,防止大项目 OOM
10
11
  */
11
12
  Object.defineProperty(exports, "__esModule", { value: true });
12
13
  exports.SmartCache = void 0;
@@ -16,7 +17,9 @@ const node_path_1 = require("node:path");
16
17
  /** 缓存默认存活时间(7 天) */
17
18
  const DEFAULT_TTL = 7 * 24 * 60 * 60 * 1000;
18
19
  /** 引擎版本,规则变更时递增使缓存失效 */
19
- const ENGINE_VERSION = "2.0.0";
20
+ const ENGINE_VERSION = "3.2.0";
21
+ /** v3.2.0: AST 内存缓存默认最大条目数 */
22
+ const DEFAULT_AST_CACHE_SIZE = 200;
20
23
  class SmartCache {
21
24
  manifest;
22
25
  cacheDir;
@@ -24,10 +27,13 @@ class SmartCache {
24
27
  ttl;
25
28
  /** v2.1.0: 内存级 AST 缓存(无需持久化,进程内复用) */
26
29
  astCache = new Map();
27
- constructor(projectDir, ttl = DEFAULT_TTL) {
30
+ /** v3.2.0: AST 缓存最大条目数 */
31
+ maxAstCacheSize;
32
+ constructor(projectDir, ttl = DEFAULT_TTL, maxAstCacheSize) {
28
33
  this.cacheDir = (0, node_path_1.resolve)(projectDir, ".frontend-guardian");
29
34
  this.cacheFile = (0, node_path_1.resolve)(this.cacheDir, "cache.json");
30
35
  this.ttl = ttl;
36
+ this.maxAstCacheSize = maxAstCacheSize ?? DEFAULT_AST_CACHE_SIZE;
31
37
  this.manifest = this.loadManifest();
32
38
  }
33
39
  /** 计算文件内容哈希 */
@@ -107,7 +113,7 @@ class SmartCache {
107
113
  return { total: Object.keys(this.manifest.entries).length, valid, expired };
108
114
  }
109
115
  // ── v2.1.0: AST 内存缓存 ──────────────────────────────────────────────
110
- /** 获取缓存的 AST(内存级,不持久化) */
116
+ /** 获取缓存的 AST(内存级,不持久化)— v3.2.0 增加 LRU 淘汰 */
111
117
  getAst(filePath, content) {
112
118
  const cached = this.astCache.get(filePath);
113
119
  if (!cached)
@@ -117,15 +123,35 @@ class SmartCache {
117
123
  this.astCache.delete(filePath);
118
124
  return undefined;
119
125
  }
126
+ // v3.2.0: LRU — 访问后移到末尾(最新)
127
+ this.astCache.delete(filePath);
128
+ this.astCache.set(filePath, cached);
120
129
  return cached.ast;
121
130
  }
122
- /** 缓存 AST 解析结果 */
131
+ /** 缓存 AST 解析结果 — v3.2.0 增加 LRU 淘汰 */
123
132
  setAst(filePath, content, ast) {
133
+ // v3.2.0: 超出上限时淘汰最久未访问的条目(Map 头部)
134
+ if (this.astCache.size >= this.maxAstCacheSize && !this.astCache.has(filePath)) {
135
+ const firstKey = this.astCache.keys().next().value;
136
+ if (firstKey) {
137
+ this.astCache.delete(firstKey);
138
+ }
139
+ }
140
+ // 删除旧条目(如果存在),然后重新插入到末尾(最新)
141
+ this.astCache.delete(filePath);
124
142
  this.astCache.set(filePath, {
125
143
  hash: SmartCache.computeHash(content),
126
144
  ast,
127
145
  });
128
146
  }
147
+ /** v3.2.0: 获取 AST 缓存当前大小 */
148
+ getAstCacheSize() {
149
+ return this.astCache.size;
150
+ }
151
+ /** v3.2.0: 获取 AST 缓存上限 */
152
+ getAstCacheLimit() {
153
+ return this.maxAstCacheSize;
154
+ }
129
155
  /** 清理过期缓存 */
130
156
  gc() {
131
157
  const now = Date.now();
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/engine/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/engine/cache.ts"],"names":[],"mappings":"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"}
@@ -44,6 +44,8 @@ export interface EngineOptions {
44
44
  skipLargeFilesThreshold?: number;
45
45
  /** v2.6.0: 外部传入的 SmartCache 实例(用于 Watch 模式复用缓存) */
46
46
  cacheInstance?: SmartCache;
47
+ /** v3.2.0: 增量扫描时通过 import 图分析扩展扫描范围(变更文件及其依赖方) */
48
+ incrementalImportGraph?: boolean;
47
49
  }
48
50
  export declare class RuleEngine {
49
51
  private registry;
@@ -74,6 +76,14 @@ export declare class RuleEngine {
74
76
  private moduleToCategory;
75
77
  /** 执行扫描 */
76
78
  scan(module: string): Promise<ScanResult>;
79
+ /**
80
+ * v3.3.0: 公共单文件扫描方法(用于 IDE 增量诊断)
81
+ * 快速扫描单个文件,返回所有匹配的 issues
82
+ * @param filePath 文件绝对路径
83
+ * @param module 模块名(可选,用于规则过滤)
84
+ * @returns 扫描发现的 issues
85
+ */
86
+ scanSingleFile(filePath: string, module?: string): Promise<Issue[]>;
77
87
  /** 扫描单个文件(带智能缓存 + 大文件跳过) */
78
88
  private scanFile;
79
89
  /** 获取扫描文件列表 */
@@ -82,6 +92,26 @@ export declare class RuleEngine {
82
92
  private getDiffFiles;
83
93
  /** 智能推断扫描范围:未提交修改 → 最近 5 次提交 → 全量 */
84
94
  private getAutoScopeFiles;
95
+ /**
96
+ * 构建项目的 import 图(简化版)
97
+ * 对于每个文件,解析其 import 语句,建立双向映射:
98
+ * - importMap: file → 它导入的文件集合
99
+ * - reverseMap: file → 导入它的文件集合
100
+ *
101
+ * 简化策略:只解析相对路径 import(./ 或 ../),不解析 node_modules。
102
+ * 因为增量扫描的核心场景是:修改一个内部模块后,哪些文件会受影响。
103
+ */
104
+ private buildImportGraph;
105
+ /**
106
+ * 基于 import 图扩展增量扫描文件列表
107
+ * 除变更文件本身外,还扫描所有导入这些文件的文件(上游依赖方)。
108
+ *
109
+ * 原理:如果 A 被 B import,A 修改后 B 的语义可能变化(如 A 导出的类型、常量变更)。
110
+ * 因此 B 也需要重新扫描。
111
+ */
112
+ private expandIncrementalFiles;
113
+ /** 同步获取项目内所有源文件(用于 import 图构建) */
114
+ private getAllSourceFilesSync;
85
115
  /** 创建 RuleUtils */
86
116
  private createUtils;
87
117
  /** 计算每行起始偏移 */
@@ -1 +1 @@
1
- {"version":3,"file":"rule-engine.d.ts","sourceRoot":"","sources":["../../src/engine/rule-engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rule-engine.d.ts","sourceRoot":"","sources":["../../src/engine/rule-engine.ts"],"names":[],"mappings":"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"}
@@ -136,8 +136,8 @@ class RuleEngine {
136
136
  const files = await this.getScanFiles();
137
137
  let filesWithIssues = 0;
138
138
  console.log(picocolors_1.default.blue(`🔍 [${module}] 扫描 ${files.length} 个文件,${activeRules.length} 条规则...`));
139
- // v2.1.0: 受控并发并行扫描
140
- const concurrency = this.options.concurrency ?? (0, concurrent_js_1.getDefaultConcurrency)();
139
+ // v3.2.0: 自适应并发 — 根据文件数、规则数和 CPU 动态调整
140
+ const concurrency = this.options.concurrency ?? (0, concurrent_js_1.getAdaptiveConcurrency)(files.length, activeRules.length);
141
141
  const fileResults = await (0, concurrent_js_1.concurrentMap)(files, concurrency, (file) => this.scanFile(file, activeRules));
142
142
  let filesSkipped = 0;
143
143
  for (const { issues: fileIssues, skipped } of fileResults) {
@@ -189,6 +189,28 @@ class RuleEngine {
189
189
  }
190
190
  return result;
191
191
  }
192
+ /**
193
+ * v3.3.0: 公共单文件扫描方法(用于 IDE 增量诊断)
194
+ * 快速扫描单个文件,返回所有匹配的 issues
195
+ * @param filePath 文件绝对路径
196
+ * @param module 模块名(可选,用于规则过滤)
197
+ * @returns 扫描发现的 issues
198
+ */
199
+ async scanSingleFile(filePath, module) {
200
+ const category = module ? this.moduleToCategory(module) : undefined;
201
+ const activeRules = category
202
+ ? this.filterRules({
203
+ category,
204
+ framework: this.projectMeta.framework,
205
+ platform: this.projectMeta.platforms[0],
206
+ componentLib: this.projectMeta.componentLib,
207
+ })
208
+ : this.getRules();
209
+ if (activeRules.length === 0)
210
+ return [];
211
+ const result = await this.scanFile(filePath, activeRules);
212
+ return result.issues;
213
+ }
192
214
  /** 扫描单个文件(带智能缓存 + 大文件跳过) */
193
215
  async scanFile(filePath, rules) {
194
216
  try {
@@ -264,7 +286,15 @@ class RuleEngine {
264
286
  }
265
287
  // 过滤出符合扩展名的文件
266
288
  const include = this.config.scan?.includeExtensions || [".js", ".ts", ".jsx", ".tsx", ".vue"];
267
- const filtered = diffFiles.filter((f) => include.some((ext) => f.endsWith(ext)));
289
+ let filtered = diffFiles.filter((f) => include.some((ext) => f.endsWith(ext)));
290
+ // v3.2.0: 通过 import 图分析扩展扫描范围
291
+ if (this.options.incrementalImportGraph !== false && filtered.length > 0 && filtered.length < 500) {
292
+ const expanded = this.expandIncrementalFiles(filtered, include);
293
+ if (expanded.length > filtered.length) {
294
+ console.log(picocolors_1.default.cyan(` 📎 import 图扩展: ${filtered.length} → ${expanded.length} 个文件`));
295
+ filtered = expanded;
296
+ }
297
+ }
268
298
  if (this.options.autoScope && filtered.length > 0) {
269
299
  console.log(picocolors_1.default.cyan(`🔍 智能扫描范围: ${filtered.length} 个文件`));
270
300
  }
@@ -367,6 +397,132 @@ class RuleEngine {
367
397
  return [];
368
398
  }
369
399
  }
400
+ // ──────────────────────────────────────────────────────────────────────────
401
+ // v3.2.0: 增量扫描 import 图分析
402
+ // ──────────────────────────────────────────────────────────────────────────
403
+ /**
404
+ * 构建项目的 import 图(简化版)
405
+ * 对于每个文件,解析其 import 语句,建立双向映射:
406
+ * - importMap: file → 它导入的文件集合
407
+ * - reverseMap: file → 导入它的文件集合
408
+ *
409
+ * 简化策略:只解析相对路径 import(./ 或 ../),不解析 node_modules。
410
+ * 因为增量扫描的核心场景是:修改一个内部模块后,哪些文件会受影响。
411
+ */
412
+ buildImportGraph(files) {
413
+ const importMap = new Map();
414
+ const reverseMap = new Map();
415
+ for (const file of files) {
416
+ importMap.set(file, new Set());
417
+ }
418
+ for (const file of files) {
419
+ try {
420
+ const source = (0, node_fs_1.readFileSync)(file, "utf-8");
421
+ // 简单正则提取相对路径 import(不依赖 AST,速度更快)
422
+ const importRegex = /import\s+.*?\s+from\s+['"](\.\.?\/[^'"]+)['"]|import\s*\(\s*['"](\.\.?\/[^'"]+)['"]\s*\)/g;
423
+ let match;
424
+ while ((match = importRegex.exec(source)) !== null) {
425
+ const importPath = match[1] || match[2];
426
+ if (!importPath)
427
+ continue;
428
+ // 解析相对路径为绝对路径
429
+ const { resolve: pathResolve, dirname } = require("node:path");
430
+ let resolved = pathResolve(dirname(file), importPath);
431
+ // 尝试补充扩展名
432
+ const exts = [".ts", ".tsx", ".js", ".jsx", ".vue"];
433
+ let found = false;
434
+ for (const ext of exts) {
435
+ if (files.includes(resolved + ext)) {
436
+ resolved = resolved + ext;
437
+ found = true;
438
+ break;
439
+ }
440
+ // 支持目录下的 index 文件
441
+ if (files.includes(pathResolve(resolved, "index" + ext))) {
442
+ resolved = pathResolve(resolved, "index" + ext);
443
+ found = true;
444
+ break;
445
+ }
446
+ }
447
+ if (found) {
448
+ importMap.get(file)?.add(resolved);
449
+ if (!reverseMap.has(resolved)) {
450
+ reverseMap.set(resolved, new Set());
451
+ }
452
+ reverseMap.get(resolved).add(file);
453
+ }
454
+ }
455
+ }
456
+ catch {
457
+ // 读取失败则跳过
458
+ }
459
+ }
460
+ return { importMap, reverseMap };
461
+ }
462
+ /**
463
+ * 基于 import 图扩展增量扫描文件列表
464
+ * 除变更文件本身外,还扫描所有导入这些文件的文件(上游依赖方)。
465
+ *
466
+ * 原理:如果 A 被 B import,A 修改后 B 的语义可能变化(如 A 导出的类型、常量变更)。
467
+ * 因此 B 也需要重新扫描。
468
+ */
469
+ expandIncrementalFiles(changedFiles, includeExts) {
470
+ try {
471
+ // 1. 获取项目内所有候选文件(用于构建 import 图)
472
+ const allFiles = this.getAllSourceFilesSync(includeExts);
473
+ if (allFiles.length > 5000) {
474
+ // 超大项目:import 图构建成本高,跳过扩展
475
+ return changedFiles;
476
+ }
477
+ // 2. 构建 import 图
478
+ const { reverseMap } = this.buildImportGraph(allFiles);
479
+ // 3. 收集变更文件及其所有导入方
480
+ const expanded = new Set(changedFiles);
481
+ const queue = [...changedFiles];
482
+ const visited = new Set(changedFiles);
483
+ while (queue.length > 0) {
484
+ const current = queue.shift();
485
+ const importers = reverseMap.get(current);
486
+ if (!importers)
487
+ continue;
488
+ for (const importer of importers) {
489
+ if (!visited.has(importer)) {
490
+ visited.add(importer);
491
+ expanded.add(importer);
492
+ queue.push(importer);
493
+ }
494
+ }
495
+ }
496
+ return Array.from(expanded);
497
+ }
498
+ catch {
499
+ return changedFiles;
500
+ }
501
+ }
502
+ /** 同步获取项目内所有源文件(用于 import 图构建) */
503
+ getAllSourceFilesSync(includeExts) {
504
+ try {
505
+ const { globbySync } = require("globby");
506
+ const patterns = includeExts.map((ext) => `**/*${ext}`);
507
+ const exclude = [
508
+ "**/node_modules/**",
509
+ "**/dist/**",
510
+ "**/build/**",
511
+ "**/.git/**",
512
+ "**/coverage/**",
513
+ ...(this.options.exclude || []),
514
+ ...(this.config.scan?.excludeDirs?.map((d) => `**/${d}/**`) || []),
515
+ ];
516
+ return globbySync(patterns, {
517
+ cwd: this.options.projectDir,
518
+ ignore: exclude,
519
+ absolute: true,
520
+ });
521
+ }
522
+ catch {
523
+ return [];
524
+ }
525
+ }
370
526
  /** 创建 RuleUtils */
371
527
  createUtils(filePath, source) {
372
528
  const lineOffsets = this.computeLineOffsets(source);