autosnippet 3.0.11 → 3.0.13
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/cli.js +64 -1
- package/config/default.json +9 -0
- package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
- package/dashboard/dist/index.html +1 -1
- package/lib/cli/SetupService.js +92 -5
- package/lib/cli/UpgradeService.js +14 -5
- package/lib/core/discovery/GenericDiscoverer.js +4 -28
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +246 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +80 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +600 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +125 -342
- package/lib/external/mcp/handlers/bootstrap/refine.js +362 -0
- package/lib/external/mcp/handlers/bootstrap.js +6 -590
- package/lib/external/mcp/handlers/browse.js +119 -9
- package/lib/external/mcp/handlers/guard.js +25 -6
- package/lib/external/mcp/handlers/search.js +56 -24
- package/lib/http/routes/guardRules.js +9 -17
- package/lib/injection/ServiceContainer.js +12 -3
- package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
- package/lib/platform/ios/xcode/XcodeIntegration.js +40 -659
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +220 -0
- package/lib/service/chat/ChatAgent.js +39 -418
- package/lib/service/chat/ChatAgentPrompts.js +149 -0
- package/lib/service/chat/ChatAgentTasks.js +297 -0
- package/lib/service/chat/tools/_shared.js +61 -0
- package/lib/service/chat/tools/ai-analysis.js +284 -0
- package/lib/service/chat/tools/ast-graph.js +681 -0
- package/lib/service/chat/tools/composite.js +496 -0
- package/lib/service/chat/tools/guard.js +265 -0
- package/lib/service/chat/tools/index.js +250 -0
- package/lib/service/chat/tools/infrastructure.js +222 -0
- package/lib/service/chat/tools/knowledge-graph.js +234 -0
- package/lib/service/chat/tools/lifecycle.js +469 -0
- package/lib/service/chat/tools/project-access.js +923 -0
- package/lib/service/chat/tools/query.js +264 -0
- package/lib/service/chat/tools.js +14 -3994
- package/lib/service/cursor/AgentInstructionsGenerator.js +395 -0
- package/lib/service/cursor/CursorDeliveryPipeline.js +70 -11
- package/lib/service/cursor/FileProtection.js +116 -0
- package/lib/service/cursor/KnowledgeCompressor.js +61 -11
- package/lib/service/cursor/SkillsSyncer.js +5 -3
- package/lib/service/cursor/TopicClassifier.js +19 -3
- package/lib/service/guard/ExclusionManager.js +26 -2
- package/lib/service/guard/GuardCheckEngine.js +38 -370
- package/lib/service/guard/GuardCodeChecks.js +362 -0
- package/lib/service/guard/GuardCrossFileChecks.js +307 -0
- package/lib/service/guard/GuardPatternUtils.js +180 -0
- package/lib/service/guard/GuardService.js +80 -38
- package/lib/service/module/ModuleService.js +1 -0
- package/lib/service/search/SearchEngine.js +10 -2
- package/lib/service/wiki/WikiGenerator.js +226 -1532
- package/lib/service/wiki/WikiRenderers.js +1878 -0
- package/lib/service/wiki/WikiUtils.js +907 -0
- package/lib/shared/LanguageService.js +299 -0
- package/package.json +1 -1
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XcodeImportResolver — import 语句解析、头文件搜索与三级去重
|
|
3
|
+
*
|
|
4
|
+
* 从 XcodeIntegration.js 拆分,负责:
|
|
5
|
+
* - import 语句解析(ObjC / Swift)
|
|
6
|
+
* - 头文件物理路径搜索
|
|
7
|
+
* - import 格式化(同 target / 跨 target)
|
|
8
|
+
* - 三级去重(精确 → 模块 → 相似文件名)
|
|
9
|
+
* - 模块名推断
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
13
|
+
import { basename, dirname, resolve as pathResolve, relative, sep } from 'node:path';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 解析原始 header 字符串,提取 moduleName 和 headerName
|
|
17
|
+
*
|
|
18
|
+
* 支持格式:
|
|
19
|
+
* #import <Module/Header.h> → { moduleName: 'Module', headerName: 'Header.h', isAngle: true }
|
|
20
|
+
* #import "Header.h" → { moduleName: '', headerName: 'Header.h', isAngle: false }
|
|
21
|
+
* @import Module; → { moduleName: 'Module', headerName: '', isAngle: false, isAtImport: true }
|
|
22
|
+
* import Module (Swift) → { moduleName: 'Module', headerName: '', isAngle: false, isSwiftImport: true }
|
|
23
|
+
* Header.h → { moduleName: '', headerName: 'Header.h', isAngle: false, isRaw: true }
|
|
24
|
+
*/
|
|
25
|
+
export function parseHeaderString(header) {
|
|
26
|
+
const t = header.trim();
|
|
27
|
+
// #import <Module/Header.h>
|
|
28
|
+
let m = t.match(/^#(?:import|include)\s+<([^/> ]+)\/([^>]+)>/);
|
|
29
|
+
if (m) {
|
|
30
|
+
return { moduleName: m[1], headerName: m[2], isAngle: true };
|
|
31
|
+
}
|
|
32
|
+
// #import <Module> (framework umbrella)
|
|
33
|
+
m = t.match(/^#(?:import|include)\s+<([^>]+)>/);
|
|
34
|
+
if (m) {
|
|
35
|
+
return { moduleName: m[1], headerName: '', isAngle: true };
|
|
36
|
+
}
|
|
37
|
+
// #import "Header.h" or #import "Dir/Header.h"
|
|
38
|
+
m = t.match(/^#(?:import|include)\s+"([^"]+)"/);
|
|
39
|
+
if (m) {
|
|
40
|
+
const parts = m[1].split('/');
|
|
41
|
+
return {
|
|
42
|
+
moduleName: '',
|
|
43
|
+
headerName: parts[parts.length - 1],
|
|
44
|
+
isAngle: false,
|
|
45
|
+
quotedPath: m[1],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// @import Module;
|
|
49
|
+
m = t.match(/^@import\s+(\w+)/);
|
|
50
|
+
if (m) {
|
|
51
|
+
return { moduleName: m[1], headerName: '', isAngle: false, isAtImport: true };
|
|
52
|
+
}
|
|
53
|
+
// import Module (Swift)
|
|
54
|
+
m = t.match(/^import\s+(\w+)/);
|
|
55
|
+
if (m && !['class', 'struct', 'enum', 'protocol', 'func', 'var', 'let'].includes(m[1])) {
|
|
56
|
+
return { moduleName: m[1], headerName: '', isAngle: false, isSwiftImport: true };
|
|
57
|
+
}
|
|
58
|
+
// 裸 header 名: Header.h
|
|
59
|
+
if (/\.(h|hpp|hh)$/i.test(t)) {
|
|
60
|
+
return { moduleName: '', headerName: t, isAngle: false, isRaw: true };
|
|
61
|
+
}
|
|
62
|
+
return { moduleName: '', headerName: t, isAngle: false, isRaw: true };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 在 target 源目录中搜索头文件,返回相对于当前文件的路径
|
|
67
|
+
*
|
|
68
|
+
* 搜索策略:
|
|
69
|
+
* 1. 当前文件同目录
|
|
70
|
+
* 2. 从项目根目录递归查找(最多深度 6 层,优先 Sources/ 下)
|
|
71
|
+
* 3. 找到后计算相对于当前文件目录的路径
|
|
72
|
+
*
|
|
73
|
+
* @param {string} headerName - 头文件名 (如 "Foo.h")
|
|
74
|
+
* @param {string} currentFilePath - 当前正在编辑的文件绝对路径
|
|
75
|
+
* @param {string} [projectRoot] - 项目根目录
|
|
76
|
+
* @returns {string|null} 相对路径 (如 "Foo.h" 或 "../SubDir/Foo.h"),null 表示未找到
|
|
77
|
+
*/
|
|
78
|
+
export function findHeaderRelativePath(headerName, currentFilePath, projectRoot) {
|
|
79
|
+
if (!headerName || !currentFilePath) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const currentDir = dirname(currentFilePath);
|
|
84
|
+
|
|
85
|
+
// 1. 同目录检查
|
|
86
|
+
const sameDir = pathResolve(currentDir, headerName);
|
|
87
|
+
if (existsSync(sameDir)) {
|
|
88
|
+
return headerName;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 2. 向上找 Sources/ 或 target 根目录,在其下递归搜索
|
|
92
|
+
const searchRoots = [];
|
|
93
|
+
if (projectRoot) {
|
|
94
|
+
const sourcesDir = pathResolve(projectRoot, 'Sources');
|
|
95
|
+
if (existsSync(sourcesDir)) {
|
|
96
|
+
searchRoots.push(sourcesDir);
|
|
97
|
+
}
|
|
98
|
+
searchRoots.push(projectRoot);
|
|
99
|
+
}
|
|
100
|
+
// 也从当前文件向上找 Sources 目录
|
|
101
|
+
let dir = currentDir;
|
|
102
|
+
for (let i = 0; i < 8; i++) {
|
|
103
|
+
const base = basename(dir);
|
|
104
|
+
if (base === 'Sources' || base === 'Source' || base === 'src') {
|
|
105
|
+
searchRoots.unshift(dir);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
const parent = dirname(dir);
|
|
109
|
+
if (parent === dir) {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
dir = parent;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 在 searchRoots 中递归查找 headerName(限深度 6)
|
|
116
|
+
for (const root of searchRoots) {
|
|
117
|
+
const found = findFileRecursive(root, headerName, 6);
|
|
118
|
+
if (found) {
|
|
119
|
+
let rel = relative(currentDir, found);
|
|
120
|
+
// 统一用 / 分隔
|
|
121
|
+
rel = rel.split(sep).join('/');
|
|
122
|
+
return rel;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 递归查找文件(限最大深度)
|
|
134
|
+
*/
|
|
135
|
+
export function findFileRecursive(dir, fileName, maxDepth) {
|
|
136
|
+
if (maxDepth <= 0) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const entries = readdirSync(dir);
|
|
141
|
+
// 先在当前层查找
|
|
142
|
+
for (const e of entries) {
|
|
143
|
+
if (e === fileName) {
|
|
144
|
+
return pathResolve(dir, e);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// 再递归子目录(跳过隐藏目录和常见无关目录)
|
|
148
|
+
for (const e of entries) {
|
|
149
|
+
if (e.startsWith('.') || e === 'node_modules' || e === 'build' || e === 'DerivedData') {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const full = pathResolve(dir, e);
|
|
153
|
+
try {
|
|
154
|
+
if (statSync(full).isDirectory()) {
|
|
155
|
+
const found = findFileRecursive(full, fileName, maxDepth - 1);
|
|
156
|
+
if (found) {
|
|
157
|
+
return found;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
/* 跳过不可访问的目录 */
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
/* 跳过不可读目录 */
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 根据当前文件 target 和 header 的 module 关系,生成正确格式的 import 行
|
|
172
|
+
*
|
|
173
|
+
* 规则:
|
|
174
|
+
* Swift: 始终 `import Module`(无 quote/angle 区别)
|
|
175
|
+
* ObjC 同 target: `#import "Header.h"` (引号格式)
|
|
176
|
+
* ObjC 跨 target: `#import <Module/Header.h>` (尖括号格式)
|
|
177
|
+
* @import 格式保持原样(已经模块级)
|
|
178
|
+
*
|
|
179
|
+
* @param {string} rawHeader 原始 header 字符串
|
|
180
|
+
* @param {object} ctx { currentTarget, headerModuleName, isSwift, fullPath, projectRoot }
|
|
181
|
+
* - currentTarget: 当前文件所属的 target 名
|
|
182
|
+
* - headerModuleName: header 所属的 module/target 名(来自 recipe.moduleName 或推断)
|
|
183
|
+
* - isSwift: 目标文件是否是 Swift
|
|
184
|
+
* - fullPath: 当前编辑文件的绝对路径(用于计算同 target 相对路径)
|
|
185
|
+
* - projectRoot: 项目根目录(用于搜索头文件物理位置)
|
|
186
|
+
* @returns {string} 格式化后的完整 import 行
|
|
187
|
+
*/
|
|
188
|
+
export function resolveHeaderFormat(rawHeader, ctx) {
|
|
189
|
+
const { currentTarget, headerModuleName, isSwift, fullPath, projectRoot } = ctx;
|
|
190
|
+
const parsed = parseHeaderString(rawHeader);
|
|
191
|
+
|
|
192
|
+
// Swift: 始终 `import Module`
|
|
193
|
+
if (isSwift || parsed.isSwiftImport) {
|
|
194
|
+
// 已经是完整 swift import 语句
|
|
195
|
+
if (parsed.isSwiftImport) {
|
|
196
|
+
return rawHeader.trim();
|
|
197
|
+
}
|
|
198
|
+
// 从 ObjC 格式推断 swift import
|
|
199
|
+
const mod = parsed.moduleName || headerModuleName || '';
|
|
200
|
+
if (mod) {
|
|
201
|
+
return `import ${mod}`;
|
|
202
|
+
}
|
|
203
|
+
return rawHeader.trim(); // 无法推断,原样返回
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// @import 保持原样(模块级引用不受 target 影响)
|
|
207
|
+
if (parsed.isAtImport) {
|
|
208
|
+
return rawHeader.trim();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 已经是尖括号格式 → 保持(明确的跨模块引用)
|
|
212
|
+
if (parsed.isAngle) {
|
|
213
|
+
return rawHeader.trim();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ── ObjC: 判断同 target vs 跨 target ──
|
|
217
|
+
const effectiveModule = parsed.moduleName || headerModuleName || '';
|
|
218
|
+
|
|
219
|
+
// 如果没有 target 信息,无法判断,保持原样
|
|
220
|
+
if (!currentTarget || !effectiveModule) {
|
|
221
|
+
return rawHeader.trim();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const isSameTarget = currentTarget === effectiveModule;
|
|
225
|
+
|
|
226
|
+
if (isSameTarget) {
|
|
227
|
+
// 同 target → 引号格式,计算相对路径
|
|
228
|
+
if (parsed.headerName && fullPath) {
|
|
229
|
+
const relPath = findHeaderRelativePath(parsed.headerName, fullPath, projectRoot);
|
|
230
|
+
if (relPath) {
|
|
231
|
+
return `#import "${relPath}"`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (parsed.quotedPath) {
|
|
235
|
+
return `#import "${parsed.quotedPath}"`;
|
|
236
|
+
}
|
|
237
|
+
if (parsed.headerName) {
|
|
238
|
+
return `#import "${parsed.headerName}"`;
|
|
239
|
+
}
|
|
240
|
+
return rawHeader.trim();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 跨 target → 尖括号格式 <Module/Header.h>
|
|
244
|
+
if (parsed.headerName) {
|
|
245
|
+
return `#import <${effectiveModule}/${parsed.headerName}>`;
|
|
246
|
+
}
|
|
247
|
+
// 没有 headerName(裸模块名),用 @import
|
|
248
|
+
return `@import ${effectiveModule};`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ═══════════════════════════════════════════════════════════════
|
|
252
|
+
// 三级 import 去重
|
|
253
|
+
// ═══════════════════════════════════════════════════════════════
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 从文件中收集已有的 import 语句
|
|
257
|
+
*/
|
|
258
|
+
export function collectImportsFromFile(filePath, isSwift) {
|
|
259
|
+
try {
|
|
260
|
+
if (!existsSync(filePath)) {
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
const content = readFileSync(filePath, 'utf8');
|
|
264
|
+
const lines = content.split(/\r?\n/);
|
|
265
|
+
const imports = [];
|
|
266
|
+
for (const line of lines) {
|
|
267
|
+
const t = line.trim();
|
|
268
|
+
if (isSwift) {
|
|
269
|
+
if (t.startsWith('import ')) {
|
|
270
|
+
imports.push(t);
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
if (t.startsWith('#import ') || t.startsWith('@import ') || t.startsWith('#include ')) {
|
|
274
|
+
imports.push(t);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return imports;
|
|
279
|
+
} catch {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 收集 .m 文件对应 .h 文件中的 imports(ObjC 接口/实现配对去重)
|
|
286
|
+
*/
|
|
287
|
+
export function collectImportsFromHeaderFile(sourcePath, importArray) {
|
|
288
|
+
const dotIndex = sourcePath.lastIndexOf('.');
|
|
289
|
+
if (dotIndex <= 0) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const headerPath = `${sourcePath.substring(0, dotIndex)}.h`;
|
|
293
|
+
const importReg = /^#import\s*<[A-Za-z0-9_]+\/[A-Za-z0-9_+.-]+\.h>$/;
|
|
294
|
+
try {
|
|
295
|
+
if (!existsSync(headerPath)) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const data = readFileSync(headerPath, 'utf8');
|
|
299
|
+
for (const line of data.split('\n')) {
|
|
300
|
+
const t = line.trim();
|
|
301
|
+
if (importReg.test(t) && !importArray.includes(t)) {
|
|
302
|
+
importArray.push(t);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch {
|
|
306
|
+
/* ignore */
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 三级 import 去重检查
|
|
312
|
+
*
|
|
313
|
+
* hasHeader 精确匹配(同一 import 行)
|
|
314
|
+
* hasModule 模块级匹配(同模块不同头文件,或 @import)
|
|
315
|
+
* hasSimilarHeader 文件名 case-insensitive 匹配
|
|
316
|
+
*
|
|
317
|
+
* @param {string[]} importArray 已有的 import 行
|
|
318
|
+
* @param {string} headerLine 待插入的 import 行
|
|
319
|
+
* @param {boolean} isSwift
|
|
320
|
+
*/
|
|
321
|
+
export function checkImportStatus(importArray, headerLine, isSwift) {
|
|
322
|
+
const trimmed = headerLine.trim();
|
|
323
|
+
|
|
324
|
+
// 提取 module / headerFileName
|
|
325
|
+
let moduleName = '';
|
|
326
|
+
let headerFileName = '';
|
|
327
|
+
|
|
328
|
+
if (isSwift) {
|
|
329
|
+
const m = trimmed.match(/^import\s+(\w+)/);
|
|
330
|
+
if (m) {
|
|
331
|
+
moduleName = m[1];
|
|
332
|
+
}
|
|
333
|
+
headerFileName = moduleName;
|
|
334
|
+
} else {
|
|
335
|
+
const angle = trimmed.match(/<([^/]+)\/([^>]+)>/);
|
|
336
|
+
if (angle) {
|
|
337
|
+
moduleName = angle[1];
|
|
338
|
+
headerFileName = angle[2];
|
|
339
|
+
}
|
|
340
|
+
const quote = trimmed.match(/"([^"]+)"/);
|
|
341
|
+
if (quote) {
|
|
342
|
+
headerFileName = basename(quote[1]);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const headerFileNameLower = headerFileName.toLowerCase();
|
|
347
|
+
|
|
348
|
+
for (const imp of importArray) {
|
|
349
|
+
const impT = imp.trim();
|
|
350
|
+
|
|
351
|
+
// ── 级别 1: 精确匹配 ──
|
|
352
|
+
if (impT === trimmed) {
|
|
353
|
+
return { hasHeader: true, hasModule: false, hasSimilarHeader: false };
|
|
354
|
+
}
|
|
355
|
+
// 去掉可能的 AutoSnippet 注释后缀再比较
|
|
356
|
+
const impTClean = impT.replace(/\s*\/\/\s*AutoSnippet.*$/, '').trim();
|
|
357
|
+
if (impTClean === trimmed) {
|
|
358
|
+
return { hasHeader: true, hasModule: false, hasSimilarHeader: false };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (isSwift) {
|
|
362
|
+
// ── 级别 2: Swift 模块匹配 ──
|
|
363
|
+
const m2 = impT.match(/^import\s+(\w+)/);
|
|
364
|
+
if (m2 && m2[1] === moduleName) {
|
|
365
|
+
return { hasHeader: false, hasModule: true, hasSimilarHeader: false };
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
// ── 级别 2: ObjC 模块匹配(<Module/xxx> 或 @import Module) ──
|
|
369
|
+
if (moduleName) {
|
|
370
|
+
const impAngle = impT.match(/<([^/]+)\//);
|
|
371
|
+
if (impAngle && impAngle[1] === moduleName) {
|
|
372
|
+
return { hasHeader: false, hasModule: true, hasSimilarHeader: false };
|
|
373
|
+
}
|
|
374
|
+
const impAt = impT.match(/@import\s+(\w+)/);
|
|
375
|
+
if (impAt && impAt[1] === moduleName) {
|
|
376
|
+
return { hasHeader: false, hasModule: true, hasSimilarHeader: false };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ── 级别 3: 相似头文件名匹配(case-insensitive) ──
|
|
381
|
+
if (headerFileNameLower) {
|
|
382
|
+
let importedFileName = null;
|
|
383
|
+
const a = impT.match(/<[^/]+\/([^>]+)>/);
|
|
384
|
+
if (a) {
|
|
385
|
+
importedFileName = a[1].toLowerCase();
|
|
386
|
+
}
|
|
387
|
+
const q = impT.match(/"([^"]+)"/);
|
|
388
|
+
if (q) {
|
|
389
|
+
importedFileName = basename(q[1]).toLowerCase();
|
|
390
|
+
}
|
|
391
|
+
if (importedFileName && importedFileName === headerFileNameLower) {
|
|
392
|
+
return { hasHeader: false, hasModule: false, hasSimilarHeader: true };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return { hasHeader: false, hasModule: false, hasSimilarHeader: false };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ═══════════════════════════════════════════════════════════════
|
|
402
|
+
// 模块名推断
|
|
403
|
+
// ═══════════════════════════════════════════════════════════════
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 从 import 语句推断模块名
|
|
407
|
+
*
|
|
408
|
+
* #import <Module/Header.h> → Module
|
|
409
|
+
* @import Module; → Module
|
|
410
|
+
* import Module (Swift) → Module
|
|
411
|
+
* #import "Local.h" → null
|
|
412
|
+
*/
|
|
413
|
+
export function inferModulesFromHeaders(headers) {
|
|
414
|
+
const modules = new Set();
|
|
415
|
+
for (const h of headers) {
|
|
416
|
+
const t = h.trim();
|
|
417
|
+
let m;
|
|
418
|
+
m = t.match(/^#import\s+<([^/> ]+)/);
|
|
419
|
+
if (m) {
|
|
420
|
+
modules.add(m[1]);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
m = t.match(/^@import\s+(\w+)/);
|
|
424
|
+
if (m) {
|
|
425
|
+
modules.add(m[1]);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
m = t.match(/^import\s+(\w+)/);
|
|
429
|
+
if (m && !['class', 'struct', 'enum', 'protocol'].includes(m[1])) {
|
|
430
|
+
modules.add(m[1]);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return [...modules];
|
|
434
|
+
}
|