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
|
@@ -13,267 +13,26 @@
|
|
|
13
13
|
* §9 完整插入流程 — cut 触发行 → preflight → headers → offset → paste
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
17
|
-
import { basename, dirname, resolve as pathResolve, relative, sep } from 'node:path';
|
|
16
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
18
17
|
import { saveEventFilter } from './SaveEventFilter.js';
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return importLine;
|
|
38
|
-
}
|
|
39
|
-
return `${importLine} ${note}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 解析原始 header 字符串,提取 moduleName 和 headerName
|
|
44
|
-
*
|
|
45
|
-
* 支持格式:
|
|
46
|
-
* #import <Module/Header.h> → { moduleName: 'Module', headerName: 'Header.h', isAngle: true }
|
|
47
|
-
* #import "Header.h" → { moduleName: '', headerName: 'Header.h', isAngle: false }
|
|
48
|
-
* @import Module; → { moduleName: 'Module', headerName: '', isAngle: false, isAtImport: true }
|
|
49
|
-
* import Module (Swift) → { moduleName: 'Module', headerName: '', isAngle: false, isSwiftImport: true }
|
|
50
|
-
* Header.h → { moduleName: '', headerName: 'Header.h', isAngle: false, isRaw: true }
|
|
51
|
-
*/
|
|
52
|
-
function _parseHeaderString(header) {
|
|
53
|
-
const t = header.trim();
|
|
54
|
-
// #import <Module/Header.h>
|
|
55
|
-
let m = t.match(/^#(?:import|include)\s+<([^/> ]+)\/([^>]+)>/);
|
|
56
|
-
if (m) {
|
|
57
|
-
return { moduleName: m[1], headerName: m[2], isAngle: true };
|
|
58
|
-
}
|
|
59
|
-
// #import <Module> (framework umbrella)
|
|
60
|
-
m = t.match(/^#(?:import|include)\s+<([^>]+)>/);
|
|
61
|
-
if (m) {
|
|
62
|
-
return { moduleName: m[1], headerName: '', isAngle: true };
|
|
63
|
-
}
|
|
64
|
-
// #import "Header.h" or #import "Dir/Header.h"
|
|
65
|
-
m = t.match(/^#(?:import|include)\s+"([^"]+)"/);
|
|
66
|
-
if (m) {
|
|
67
|
-
const parts = m[1].split('/');
|
|
68
|
-
return {
|
|
69
|
-
moduleName: '',
|
|
70
|
-
headerName: parts[parts.length - 1],
|
|
71
|
-
isAngle: false,
|
|
72
|
-
quotedPath: m[1],
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
// @import Module;
|
|
76
|
-
m = t.match(/^@import\s+(\w+)/);
|
|
77
|
-
if (m) {
|
|
78
|
-
return { moduleName: m[1], headerName: '', isAngle: false, isAtImport: true };
|
|
79
|
-
}
|
|
80
|
-
// import Module (Swift)
|
|
81
|
-
m = t.match(/^import\s+(\w+)/);
|
|
82
|
-
if (m && !['class', 'struct', 'enum', 'protocol', 'func', 'var', 'let'].includes(m[1])) {
|
|
83
|
-
return { moduleName: m[1], headerName: '', isAngle: false, isSwiftImport: true };
|
|
84
|
-
}
|
|
85
|
-
// 裸 header 名: Header.h
|
|
86
|
-
if (/\.(h|hpp|hh)$/i.test(t)) {
|
|
87
|
-
return { moduleName: '', headerName: t, isAngle: false, isRaw: true };
|
|
88
|
-
}
|
|
89
|
-
return { moduleName: '', headerName: t, isAngle: false, isRaw: true };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 在 target 源目录中搜索头文件,返回相对于当前文件的路径
|
|
94
|
-
*
|
|
95
|
-
* 搜索策略:
|
|
96
|
-
* 1. 当前文件同目录
|
|
97
|
-
* 2. 从项目根目录递归查找(最多深度 6 层,优先 Sources/ 下)
|
|
98
|
-
* 3. 找到后计算相对于当前文件目录的路径
|
|
99
|
-
*
|
|
100
|
-
* @param {string} headerName - 头文件名 (如 "Foo.h")
|
|
101
|
-
* @param {string} currentFilePath - 当前正在编辑的文件绝对路径
|
|
102
|
-
* @param {string} [projectRoot] - 项目根目录
|
|
103
|
-
* @returns {string|null} 相对路径 (如 "Foo.h" 或 "../SubDir/Foo.h"),null 表示未找到
|
|
104
|
-
*/
|
|
105
|
-
function _findHeaderRelativePath(headerName, currentFilePath, projectRoot) {
|
|
106
|
-
if (!headerName || !currentFilePath) {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
try {
|
|
110
|
-
const currentDir = dirname(currentFilePath);
|
|
111
|
-
|
|
112
|
-
// 1. 同目录检查
|
|
113
|
-
const sameDir = pathResolve(currentDir, headerName);
|
|
114
|
-
if (existsSync(sameDir)) {
|
|
115
|
-
return headerName;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 2. 向上找 Sources/ 或 target 根目录,在其下递归搜索
|
|
119
|
-
const searchRoots = [];
|
|
120
|
-
if (projectRoot) {
|
|
121
|
-
const sourcesDir = pathResolve(projectRoot, 'Sources');
|
|
122
|
-
if (existsSync(sourcesDir)) {
|
|
123
|
-
searchRoots.push(sourcesDir);
|
|
124
|
-
}
|
|
125
|
-
searchRoots.push(projectRoot);
|
|
126
|
-
}
|
|
127
|
-
// 也从当前文件向上找 Sources 目录
|
|
128
|
-
let dir = currentDir;
|
|
129
|
-
for (let i = 0; i < 8; i++) {
|
|
130
|
-
const base = basename(dir);
|
|
131
|
-
if (base === 'Sources' || base === 'Source' || base === 'src') {
|
|
132
|
-
searchRoots.unshift(dir);
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
const parent = dirname(dir);
|
|
136
|
-
if (parent === dir) {
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
dir = parent;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// 在 searchRoots 中递归查找 headerName(限深度 6)
|
|
143
|
-
for (const root of searchRoots) {
|
|
144
|
-
const found = _findFileRecursive(root, headerName, 6);
|
|
145
|
-
if (found) {
|
|
146
|
-
let rel = relative(currentDir, found);
|
|
147
|
-
// 统一用 / 分隔
|
|
148
|
-
rel = rel.split(sep).join('/');
|
|
149
|
-
return rel;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return null;
|
|
154
|
-
} catch {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* 递归查找文件(限最大深度)
|
|
161
|
-
*/
|
|
162
|
-
function _findFileRecursive(dir, fileName, maxDepth) {
|
|
163
|
-
if (maxDepth <= 0) {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
const entries = readdirSync(dir);
|
|
168
|
-
// 先在当前层查找
|
|
169
|
-
for (const e of entries) {
|
|
170
|
-
if (e === fileName) {
|
|
171
|
-
return pathResolve(dir, e);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
// 再递归子目录(跳过隐藏目录和常见无关目录)
|
|
175
|
-
for (const e of entries) {
|
|
176
|
-
if (e.startsWith('.') || e === 'node_modules' || e === 'build' || e === 'DerivedData') {
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
const full = pathResolve(dir, e);
|
|
180
|
-
try {
|
|
181
|
-
if (statSync(full).isDirectory()) {
|
|
182
|
-
const found = _findFileRecursive(full, fileName, maxDepth - 1);
|
|
183
|
-
if (found) {
|
|
184
|
-
return found;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
} catch {
|
|
188
|
-
/* 跳过不可访问的目录 */
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
} catch {
|
|
192
|
-
/* 跳过不可读目录 */
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* 根据当前文件 target 和 header 的 module 关系,生成正确格式的 import 行
|
|
199
|
-
*
|
|
200
|
-
* 规则:
|
|
201
|
-
* Swift: 始终 `import Module`(无 quote/angle 区别)
|
|
202
|
-
* ObjC 同 target: `#import "Header.h"` (引号格式)
|
|
203
|
-
* ObjC 跨 target: `#import <Module/Header.h>` (尖括号格式)
|
|
204
|
-
* @import 格式保持原样(已经模块级)
|
|
205
|
-
*
|
|
206
|
-
* @param {string} rawHeader 原始 header 字符串
|
|
207
|
-
* @param {object} ctx { currentTarget, headerModuleName, isSwift, fullPath, projectRoot }
|
|
208
|
-
* - currentTarget: 当前文件所属的 target 名
|
|
209
|
-
* - headerModuleName: header 所属的 module/target 名(来自 recipe.moduleName 或推断)
|
|
210
|
-
* - isSwift: 目标文件是否是 Swift
|
|
211
|
-
* - fullPath: 当前编辑文件的绝对路径(用于计算同 target 相对路径)
|
|
212
|
-
* - projectRoot: 项目根目录(用于搜索头文件物理位置)
|
|
213
|
-
* @returns {string} 格式化后的完整 import 行
|
|
214
|
-
*/
|
|
215
|
-
function _resolveHeaderFormat(rawHeader, ctx) {
|
|
216
|
-
const { currentTarget, headerModuleName, isSwift, fullPath, projectRoot } = ctx;
|
|
217
|
-
const parsed = _parseHeaderString(rawHeader);
|
|
218
|
-
|
|
219
|
-
// Swift: 始终 `import Module`
|
|
220
|
-
if (isSwift || parsed.isSwiftImport) {
|
|
221
|
-
// 已经是完整 swift import 语句
|
|
222
|
-
if (parsed.isSwiftImport) {
|
|
223
|
-
return rawHeader.trim();
|
|
224
|
-
}
|
|
225
|
-
// 从 ObjC 格式推断 swift import
|
|
226
|
-
const mod = parsed.moduleName || headerModuleName || '';
|
|
227
|
-
if (mod) {
|
|
228
|
-
return `import ${mod}`;
|
|
229
|
-
}
|
|
230
|
-
return rawHeader.trim(); // 无法推断,原样返回
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// @import 保持原样(模块级引用不受 target 影响)
|
|
234
|
-
if (parsed.isAtImport) {
|
|
235
|
-
return rawHeader.trim();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// 已经是尖括号格式 → 保持(明确的跨模块引用)
|
|
239
|
-
if (parsed.isAngle) {
|
|
240
|
-
return rawHeader.trim();
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// ── ObjC: 判断同 target vs 跨 target ──
|
|
244
|
-
const effectiveModule = parsed.moduleName || headerModuleName || '';
|
|
245
|
-
|
|
246
|
-
// 如果没有 target 信息,无法判断,保持原样
|
|
247
|
-
if (!currentTarget || !effectiveModule) {
|
|
248
|
-
return rawHeader.trim();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const isSameTarget = currentTarget === effectiveModule;
|
|
252
|
-
|
|
253
|
-
if (isSameTarget) {
|
|
254
|
-
// 同 target → 引号格式,计算相对路径
|
|
255
|
-
if (parsed.headerName && fullPath) {
|
|
256
|
-
const relPath = _findHeaderRelativePath(parsed.headerName, fullPath, projectRoot);
|
|
257
|
-
if (relPath) {
|
|
258
|
-
return `#import "${relPath}"`;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (parsed.quotedPath) {
|
|
262
|
-
return `#import "${parsed.quotedPath}"`;
|
|
263
|
-
}
|
|
264
|
-
if (parsed.headerName) {
|
|
265
|
-
return `#import "${parsed.headerName}"`;
|
|
266
|
-
}
|
|
267
|
-
return rawHeader.trim();
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// 跨 target → 尖括号格式 <Module/Header.h>
|
|
271
|
-
if (parsed.headerName) {
|
|
272
|
-
return `#import <${effectiveModule}/${parsed.headerName}>`;
|
|
273
|
-
}
|
|
274
|
-
// 没有 headerName(裸模块名),用 @import
|
|
275
|
-
return `@import ${effectiveModule};`;
|
|
276
|
-
}
|
|
19
|
+
import {
|
|
20
|
+
resolveHeaderFormat,
|
|
21
|
+
collectImportsFromFile,
|
|
22
|
+
collectImportsFromHeaderFile,
|
|
23
|
+
checkImportStatus,
|
|
24
|
+
inferModulesFromHeaders,
|
|
25
|
+
} from './XcodeImportResolver.js';
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
sleep,
|
|
29
|
+
withAutoSnippetNote,
|
|
30
|
+
evaluateDepResult,
|
|
31
|
+
handleDepReview,
|
|
32
|
+
writeImportLineXcode,
|
|
33
|
+
writeImportLineFile,
|
|
34
|
+
computePasteLineNumber,
|
|
35
|
+
} from './XcodeWriteUtils.js';
|
|
277
36
|
|
|
278
37
|
/** 常见 Apple 系统框架(无需 SPM 依赖检查) */
|
|
279
38
|
const _SYSTEM_FRAMEWORKS = new Set([
|
|
@@ -329,384 +88,6 @@ const _SYSTEM_FRAMEWORKS = new Set([
|
|
|
329
88
|
'XCTest',
|
|
330
89
|
]);
|
|
331
90
|
|
|
332
|
-
// ═══════════════════════════════════════════════════════════════
|
|
333
|
-
// §4 三级 import 去重
|
|
334
|
-
// ═══════════════════════════════════════════════════════════════
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* 从文件中收集已有的 import 语句
|
|
338
|
-
*/
|
|
339
|
-
function _collectImportsFromFile(filePath, isSwift) {
|
|
340
|
-
try {
|
|
341
|
-
if (!existsSync(filePath)) {
|
|
342
|
-
return [];
|
|
343
|
-
}
|
|
344
|
-
const content = readFileSync(filePath, 'utf8');
|
|
345
|
-
const lines = content.split(/\r?\n/);
|
|
346
|
-
const imports = [];
|
|
347
|
-
for (const line of lines) {
|
|
348
|
-
const t = line.trim();
|
|
349
|
-
if (isSwift) {
|
|
350
|
-
if (t.startsWith('import ')) {
|
|
351
|
-
imports.push(t);
|
|
352
|
-
}
|
|
353
|
-
} else {
|
|
354
|
-
if (t.startsWith('#import ') || t.startsWith('@import ') || t.startsWith('#include ')) {
|
|
355
|
-
imports.push(t);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
return imports;
|
|
360
|
-
} catch {
|
|
361
|
-
return [];
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* 收集 .m 文件对应 .h 文件中的 imports(ObjC 接口/实现配对去重)
|
|
367
|
-
*/
|
|
368
|
-
function _collectImportsFromHeaderFile(sourcePath, importArray) {
|
|
369
|
-
const dotIndex = sourcePath.lastIndexOf('.');
|
|
370
|
-
if (dotIndex <= 0) {
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
const headerPath = `${sourcePath.substring(0, dotIndex)}.h`;
|
|
374
|
-
const importReg = /^#import\s*<[A-Za-z0-9_]+\/[A-Za-z0-9_+.-]+\.h>$/;
|
|
375
|
-
try {
|
|
376
|
-
if (!existsSync(headerPath)) {
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
const data = readFileSync(headerPath, 'utf8');
|
|
380
|
-
for (const line of data.split('\n')) {
|
|
381
|
-
const t = line.trim();
|
|
382
|
-
if (importReg.test(t) && !importArray.includes(t)) {
|
|
383
|
-
importArray.push(t);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
} catch {
|
|
387
|
-
/* ignore */
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* 三级 import 去重检查
|
|
393
|
-
*
|
|
394
|
-
* hasHeader 精确匹配(同一 import 行)
|
|
395
|
-
* hasModule 模块级匹配(同模块不同头文件,或 @import)
|
|
396
|
-
* hasSimilarHeader 文件名 case-insensitive 匹配
|
|
397
|
-
*
|
|
398
|
-
* @param {string[]} importArray 已有的 import 行
|
|
399
|
-
* @param {string} headerLine 待插入的 import 行
|
|
400
|
-
* @param {boolean} isSwift
|
|
401
|
-
*/
|
|
402
|
-
function _checkImportStatus(importArray, headerLine, isSwift) {
|
|
403
|
-
const trimmed = headerLine.trim();
|
|
404
|
-
|
|
405
|
-
// 提取 module / headerFileName
|
|
406
|
-
let moduleName = '';
|
|
407
|
-
let headerFileName = '';
|
|
408
|
-
|
|
409
|
-
if (isSwift) {
|
|
410
|
-
const m = trimmed.match(/^import\s+(\w+)/);
|
|
411
|
-
if (m) {
|
|
412
|
-
moduleName = m[1];
|
|
413
|
-
}
|
|
414
|
-
headerFileName = moduleName;
|
|
415
|
-
} else {
|
|
416
|
-
const angle = trimmed.match(/<([^/]+)\/([^>]+)>/);
|
|
417
|
-
if (angle) {
|
|
418
|
-
moduleName = angle[1];
|
|
419
|
-
headerFileName = angle[2];
|
|
420
|
-
}
|
|
421
|
-
const quote = trimmed.match(/"([^"]+)"/);
|
|
422
|
-
if (quote) {
|
|
423
|
-
headerFileName = basename(quote[1]);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const headerFileNameLower = headerFileName.toLowerCase();
|
|
428
|
-
|
|
429
|
-
for (const imp of importArray) {
|
|
430
|
-
const impT = imp.trim();
|
|
431
|
-
|
|
432
|
-
// ── 级别 1: 精确匹配 ──
|
|
433
|
-
if (impT === trimmed) {
|
|
434
|
-
return { hasHeader: true, hasModule: false, hasSimilarHeader: false };
|
|
435
|
-
}
|
|
436
|
-
// 去掉可能的 AutoSnippet 注释后缀再比较
|
|
437
|
-
const impTClean = impT.replace(/\s*\/\/\s*AutoSnippet.*$/, '').trim();
|
|
438
|
-
if (impTClean === trimmed) {
|
|
439
|
-
return { hasHeader: true, hasModule: false, hasSimilarHeader: false };
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (isSwift) {
|
|
443
|
-
// ── 级别 2: Swift 模块匹配 ──
|
|
444
|
-
const m2 = impT.match(/^import\s+(\w+)/);
|
|
445
|
-
if (m2 && m2[1] === moduleName) {
|
|
446
|
-
return { hasHeader: false, hasModule: true, hasSimilarHeader: false };
|
|
447
|
-
}
|
|
448
|
-
} else {
|
|
449
|
-
// ── 级别 2: ObjC 模块匹配(<Module/xxx> 或 @import Module) ──
|
|
450
|
-
if (moduleName) {
|
|
451
|
-
const impAngle = impT.match(/<([^/]+)\//);
|
|
452
|
-
if (impAngle && impAngle[1] === moduleName) {
|
|
453
|
-
return { hasHeader: false, hasModule: true, hasSimilarHeader: false };
|
|
454
|
-
}
|
|
455
|
-
const impAt = impT.match(/@import\s+(\w+)/);
|
|
456
|
-
if (impAt && impAt[1] === moduleName) {
|
|
457
|
-
return { hasHeader: false, hasModule: true, hasSimilarHeader: false };
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// ── 级别 3: 相似头文件名匹配(case-insensitive) ──
|
|
462
|
-
if (headerFileNameLower) {
|
|
463
|
-
let importedFileName = null;
|
|
464
|
-
const a = impT.match(/<[^/]+\/([^>]+)>/);
|
|
465
|
-
if (a) {
|
|
466
|
-
importedFileName = a[1].toLowerCase();
|
|
467
|
-
}
|
|
468
|
-
const q = impT.match(/"([^"]+)"/);
|
|
469
|
-
if (q) {
|
|
470
|
-
importedFileName = basename(q[1]).toLowerCase();
|
|
471
|
-
}
|
|
472
|
-
if (importedFileName && importedFileName === headerFileNameLower) {
|
|
473
|
-
return { hasHeader: false, hasModule: false, hasSimilarHeader: true };
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
return { hasHeader: false, hasModule: false, hasSimilarHeader: false };
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// ═══════════════════════════════════════════════════════════════
|
|
483
|
-
// §5 模块名推断
|
|
484
|
-
// ═══════════════════════════════════════════════════════════════
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* 从 import 语句推断模块名
|
|
488
|
-
*
|
|
489
|
-
* #import <Module/Header.h> → Module
|
|
490
|
-
* @import Module; → Module
|
|
491
|
-
* import Module (Swift) → Module
|
|
492
|
-
* #import "Local.h" → null
|
|
493
|
-
*/
|
|
494
|
-
function _inferModulesFromHeaders(headers) {
|
|
495
|
-
const modules = new Set();
|
|
496
|
-
for (const h of headers) {
|
|
497
|
-
const t = h.trim();
|
|
498
|
-
let m;
|
|
499
|
-
m = t.match(/^#import\s+<([^/> ]+)/);
|
|
500
|
-
if (m) {
|
|
501
|
-
modules.add(m[1]);
|
|
502
|
-
continue;
|
|
503
|
-
}
|
|
504
|
-
m = t.match(/^@import\s+(\w+)/);
|
|
505
|
-
if (m) {
|
|
506
|
-
modules.add(m[1]);
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
m = t.match(/^import\s+(\w+)/);
|
|
510
|
-
if (m && !['class', 'struct', 'enum', 'protocol'].includes(m[1])) {
|
|
511
|
-
modules.add(m[1]);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
return [...modules];
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// ═══════════════════════════════════════════════════════════════
|
|
518
|
-
// §6 SPM 依赖检查决策引擎
|
|
519
|
-
// ═══════════════════════════════════════════════════════════════
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* 将 SpmService.ensureDependency 返回值映射为三种动作:
|
|
523
|
-
* continue — 依赖已存在
|
|
524
|
-
* block — 循环/反向依赖,禁止插入
|
|
525
|
-
* review — 依赖缺失但可添加,需用户确认
|
|
526
|
-
*/
|
|
527
|
-
function _evaluateDepResult(ensureResult, from, to) {
|
|
528
|
-
if (ensureResult.exists) {
|
|
529
|
-
return { action: 'continue' };
|
|
530
|
-
}
|
|
531
|
-
if (!ensureResult.canAdd) {
|
|
532
|
-
return { action: 'block', reason: ensureResult.reason || 'cycleBlocked', from, to };
|
|
533
|
-
}
|
|
534
|
-
return { action: 'review', reason: ensureResult.reason || 'missingDependency', from, to };
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* 公共依赖审查弹窗逻辑(insertHeaders 和 _preflightDeps 共享)
|
|
539
|
-
*
|
|
540
|
-
* @param {object} ctx - { spmService, currentTarget, mod, ensureResult, NU, depWarnings, label }
|
|
541
|
-
* @returns {{ blocked: boolean }}
|
|
542
|
-
*/
|
|
543
|
-
function _handleDepReview(ctx) {
|
|
544
|
-
const { spmService, currentTarget, mod, ensureResult, NU, depWarnings, label = '' } = ctx;
|
|
545
|
-
|
|
546
|
-
const fixMode = spmService.getFixMode();
|
|
547
|
-
const buttons =
|
|
548
|
-
fixMode === 'fix'
|
|
549
|
-
? ['直接插入(信任架构)', '提示操作插入', '自动修复依赖', '取消操作']
|
|
550
|
-
: ['直接插入(信任架构)', '提示操作插入', '取消操作'];
|
|
551
|
-
|
|
552
|
-
const crossTag = ensureResult.crossPackage ? ' (跨包)' : '';
|
|
553
|
-
const prefix = label ? `[${label}] ` : '';
|
|
554
|
-
|
|
555
|
-
const userChoice = NU.promptWithButtons(
|
|
556
|
-
`检测到依赖缺失:${currentTarget} -> ${mod}${crossTag}\n\n请选择处理方式:`,
|
|
557
|
-
buttons,
|
|
558
|
-
'AutoSnippet SPM 依赖决策'
|
|
559
|
-
);
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
userChoice === '取消操作' ||
|
|
563
|
-
(!userChoice && !['直接插入(信任架构)', '提示操作插入', '自动修复依赖'].includes(userChoice))
|
|
564
|
-
) {
|
|
565
|
-
return { blocked: true };
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
if (userChoice === '提示操作插入') {
|
|
569
|
-
depWarnings.set(mod, `${currentTarget} -> ${mod}`);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
if (userChoice === '自动修复依赖') {
|
|
573
|
-
const fixResult = spmService.addDependency(currentTarget, mod);
|
|
574
|
-
if (fixResult.ok) {
|
|
575
|
-
NU.notify(`已补齐依赖:${currentTarget} -> ${mod}`, 'AutoSnippet SPM');
|
|
576
|
-
} else {
|
|
577
|
-
console.warn(` ⚠️ ${prefix}自动修复失败: ${fixResult.error},继续插入`);
|
|
578
|
-
depWarnings.set(mod, `${currentTarget} -> ${mod}`);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
return { blocked: false };
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// ═══════════════════════════════════════════════════════════════
|
|
586
|
-
// §7 Xcode osascript 单条 import 写入
|
|
587
|
-
// ═══════════════════════════════════════════════════════════════
|
|
588
|
-
|
|
589
|
-
/**
|
|
590
|
-
* 通过 Xcode 自动化插入一条 import,保持 Xcode Undo 可用。
|
|
591
|
-
*
|
|
592
|
-
* 流程:保存剪贴板 → 写入 import 内容 → osascript 跳转+粘贴 → 恢复剪贴板
|
|
593
|
-
*
|
|
594
|
-
* @param {string} importLine 完整的 import 文本
|
|
595
|
-
* @param {number} insertLine 1-based 行号
|
|
596
|
-
* @param {object} XA XcodeAutomation 模块
|
|
597
|
-
* @param {object} CM ClipboardManager 模块
|
|
598
|
-
* @returns {boolean}
|
|
599
|
-
*/
|
|
600
|
-
function _writeImportLineXcode(importLine, insertLine, XA, CM) {
|
|
601
|
-
if (!XA.isXcodeRunning()) {
|
|
602
|
-
return false;
|
|
603
|
-
}
|
|
604
|
-
try {
|
|
605
|
-
const contentToWrite = `${String(importLine).trim()}\n`;
|
|
606
|
-
const previousClipboard = CM.read();
|
|
607
|
-
|
|
608
|
-
CM.write(contentToWrite);
|
|
609
|
-
const ok = XA.insertAtLineStartInXcode(insertLine);
|
|
610
|
-
|
|
611
|
-
// 始终恢复剪贴板
|
|
612
|
-
if (typeof previousClipboard === 'string') {
|
|
613
|
-
CM.write(previousClipboard);
|
|
614
|
-
}
|
|
615
|
-
return ok;
|
|
616
|
-
} catch {
|
|
617
|
-
return false;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// ═══════════════════════════════════════════════════════════════
|
|
622
|
-
// §8 文件写入回退
|
|
623
|
-
// ═══════════════════════════════════════════════════════════════
|
|
624
|
-
|
|
625
|
-
/**
|
|
626
|
-
* 纯文件写入插入单条 import。
|
|
627
|
-
* Xcode 会因文件变更而自动 reload。
|
|
628
|
-
*/
|
|
629
|
-
function _writeImportLineFile(filePath, importLine, isSwift) {
|
|
630
|
-
try {
|
|
631
|
-
const content = readFileSync(filePath, 'utf8');
|
|
632
|
-
const lines = content.split('\n');
|
|
633
|
-
let lastImportIdx = -1;
|
|
634
|
-
for (let i = 0; i < lines.length; i++) {
|
|
635
|
-
const t = lines[i].trim();
|
|
636
|
-
if (isSwift) {
|
|
637
|
-
if (t.startsWith('import ') && !t.startsWith('import (')) {
|
|
638
|
-
lastImportIdx = i;
|
|
639
|
-
}
|
|
640
|
-
} else {
|
|
641
|
-
if (t.startsWith('#import ') || t.startsWith('#include ') || t.startsWith('@import ')) {
|
|
642
|
-
lastImportIdx = i;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
const insertAt = lastImportIdx >= 0 ? lastImportIdx + 1 : 0;
|
|
647
|
-
lines.splice(insertAt, 0, importLine);
|
|
648
|
-
const newContent = lines.join('\n');
|
|
649
|
-
saveEventFilter.markWrite(filePath, newContent);
|
|
650
|
-
writeFileSync(filePath, newContent, 'utf8');
|
|
651
|
-
return true;
|
|
652
|
-
} catch {
|
|
653
|
-
return false;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// ═══════════════════════════════════════════════════════════════
|
|
658
|
-
// §9 粘贴行号偏移计算
|
|
659
|
-
// ═══════════════════════════════════════════════════════════════
|
|
660
|
-
|
|
661
|
-
/**
|
|
662
|
-
* 查找文件中最后一个 import 行的行号(1-based,0 表示无 import)
|
|
663
|
-
*/
|
|
664
|
-
function _getLastImportLine(filePath) {
|
|
665
|
-
try {
|
|
666
|
-
if (!existsSync(filePath)) {
|
|
667
|
-
return 0;
|
|
668
|
-
}
|
|
669
|
-
const content = readFileSync(filePath, 'utf8');
|
|
670
|
-
const lines = content.split(/\r?\n/);
|
|
671
|
-
let lastIdx = -1;
|
|
672
|
-
for (let i = 0; i < lines.length; i++) {
|
|
673
|
-
const t = lines[i].trim();
|
|
674
|
-
if (
|
|
675
|
-
t.startsWith('#import ') ||
|
|
676
|
-
t.startsWith('@import ') ||
|
|
677
|
-
t.startsWith('#include ') ||
|
|
678
|
-
t.startsWith('import ')
|
|
679
|
-
) {
|
|
680
|
-
lastIdx = i;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
return lastIdx >= 0 ? lastIdx + 1 : 0;
|
|
684
|
-
} catch {
|
|
685
|
-
return 0;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
/**
|
|
690
|
-
* 计算代码粘贴行号
|
|
691
|
-
*
|
|
692
|
-
* 如果 headers 插入在 trigger 行之前(import 区),trigger 行号需要向下偏移。
|
|
693
|
-
*/
|
|
694
|
-
function _computePasteLineNumber(triggerLineNumber, headerInsertCount, filePath, options = {}) {
|
|
695
|
-
const expectedCount = Number.isFinite(options.expectedHeaderCount)
|
|
696
|
-
? options.expectedHeaderCount
|
|
697
|
-
: headerInsertCount;
|
|
698
|
-
if (expectedCount > 0) {
|
|
699
|
-
if (options.forceOffset) {
|
|
700
|
-
return triggerLineNumber + expectedCount;
|
|
701
|
-
}
|
|
702
|
-
const headerInsertPosition = _getLastImportLine(filePath);
|
|
703
|
-
if (headerInsertPosition > 0 && headerInsertPosition < triggerLineNumber) {
|
|
704
|
-
return triggerLineNumber + expectedCount;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
return triggerLineNumber;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
91
|
// ═══════════════════════════════════════════════════════════════
|
|
711
92
|
// §10 导出:insertHeaders
|
|
712
93
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -741,10 +122,10 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
|
|
|
741
122
|
const isSwift = opts.isSwift ?? fullPath.endsWith('.swift');
|
|
742
123
|
|
|
743
124
|
// ── Step 1: 收集已有 imports ──
|
|
744
|
-
const importArray =
|
|
125
|
+
const importArray = collectImportsFromFile(fullPath, isSwift);
|
|
745
126
|
// .m 文件还要收集对应 .h 的 imports
|
|
746
127
|
if (!isSwift && !fullPath.endsWith('.h')) {
|
|
747
|
-
|
|
128
|
+
collectImportsFromHeaderFile(fullPath, importArray);
|
|
748
129
|
}
|
|
749
130
|
|
|
750
131
|
// ── Step 2: SPM/模块 服务准备 ──
|
|
@@ -752,7 +133,7 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
|
|
|
752
133
|
let spmService = opts._spmService || null;
|
|
753
134
|
let currentTarget = opts._currentTarget || null;
|
|
754
135
|
if (!spmService && !opts.skipDepCheck) {
|
|
755
|
-
const inferredModules =
|
|
136
|
+
const inferredModules = inferModulesFromHeaders(headers);
|
|
756
137
|
if (opts.moduleName && !inferredModules.includes(opts.moduleName)) {
|
|
757
138
|
inferredModules.push(opts.moduleName);
|
|
758
139
|
}
|
|
@@ -802,17 +183,17 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
|
|
|
802
183
|
|
|
803
184
|
// ── 三级去重 ──
|
|
804
185
|
// 先按原始格式检查,再按解析后格式检查(同一 header 可能格式不同)
|
|
805
|
-
const preResolvedHeader =
|
|
186
|
+
const preResolvedHeader = resolveHeaderFormat(headerTrimmed, {
|
|
806
187
|
currentTarget,
|
|
807
188
|
headerModuleName: opts.moduleName || null,
|
|
808
189
|
isSwift,
|
|
809
190
|
fullPath,
|
|
810
191
|
projectRoot: watcher?.projectRoot || null,
|
|
811
192
|
});
|
|
812
|
-
const status =
|
|
193
|
+
const status = checkImportStatus(importArray, headerTrimmed, isSwift);
|
|
813
194
|
const statusResolved =
|
|
814
195
|
preResolvedHeader !== headerTrimmed
|
|
815
|
-
?
|
|
196
|
+
? checkImportStatus(importArray, preResolvedHeader, isSwift)
|
|
816
197
|
: status;
|
|
817
198
|
if (status.hasHeader || statusResolved.hasHeader) {
|
|
818
199
|
result.skipped.push(preResolvedHeader);
|
|
@@ -828,7 +209,7 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
|
|
|
828
209
|
}
|
|
829
210
|
|
|
830
211
|
// ── SPM 依赖检查 ──
|
|
831
|
-
const headerModules =
|
|
212
|
+
const headerModules = inferModulesFromHeaders([headerTrimmed]);
|
|
832
213
|
if (spmService && currentTarget && !opts.skipDepCheck) {
|
|
833
214
|
for (const mod of headerModules) {
|
|
834
215
|
if (_SYSTEM_FRAMEWORKS.has(mod) || mod === currentTarget) {
|
|
@@ -836,7 +217,7 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
|
|
|
836
217
|
}
|
|
837
218
|
|
|
838
219
|
const ensureResult = spmService.ensureDependency(currentTarget, mod);
|
|
839
|
-
const decision =
|
|
220
|
+
const decision = evaluateDepResult(ensureResult, currentTarget, mod);
|
|
840
221
|
|
|
841
222
|
if (decision.action === 'block') {
|
|
842
223
|
console.warn(` ⛔ 依赖被阻止: ${currentTarget} -> ${mod} (${decision.reason})`);
|
|
@@ -849,7 +230,7 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
|
|
|
849
230
|
}
|
|
850
231
|
|
|
851
232
|
if (decision.action === 'review') {
|
|
852
|
-
const reviewResult =
|
|
233
|
+
const reviewResult = handleDepReview({
|
|
853
234
|
spmService,
|
|
854
235
|
currentTarget,
|
|
855
236
|
mod,
|
|
@@ -870,22 +251,22 @@ export async function insertHeaders(watcher, fullPath, headers, opts = {}) {
|
|
|
870
251
|
const resolvedHeader = preResolvedHeader;
|
|
871
252
|
const depHint = headerModules.find((m) => depWarnings.has(m));
|
|
872
253
|
const importLine = depHint
|
|
873
|
-
? `${
|
|
874
|
-
:
|
|
254
|
+
? `${withAutoSnippetNote(resolvedHeader)} // ⚠️ 依赖缺失: ${depWarnings.get(depHint)},需手动补齐 Package.swift`
|
|
255
|
+
: withAutoSnippetNote(resolvedHeader);
|
|
875
256
|
|
|
876
257
|
// ── 写入:Xcode 自动化优先 → 文件写入回退 ──
|
|
877
258
|
let inserted = false;
|
|
878
259
|
|
|
879
260
|
if (xcodeReady && !fileWriteUsed) {
|
|
880
261
|
// 逐条 osascript 跳转 + 粘贴
|
|
881
|
-
inserted =
|
|
262
|
+
inserted = writeImportLineXcode(importLine, baseInsertLine + xcodeOffset, XA, CM);
|
|
882
263
|
if (inserted) {
|
|
883
264
|
xcodeOffset++;
|
|
884
265
|
}
|
|
885
266
|
}
|
|
886
267
|
|
|
887
268
|
if (!inserted) {
|
|
888
|
-
|
|
269
|
+
writeImportLineFile(fullPath, importLine, isSwift);
|
|
889
270
|
fileWriteUsed = true;
|
|
890
271
|
}
|
|
891
272
|
|
|
@@ -989,7 +370,7 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
|
|
|
989
370
|
skipDepCheck: true,
|
|
990
371
|
});
|
|
991
372
|
}
|
|
992
|
-
await
|
|
373
|
+
await sleep(300);
|
|
993
374
|
|
|
994
375
|
// ── Step 4: 构建带缩进的代码块 ──
|
|
995
376
|
const codeLines = code.split(/\r?\n/);
|
|
@@ -1020,7 +401,7 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
|
|
|
1020
401
|
// ── Step 6: 计算偏移后的粘贴行号 ──
|
|
1021
402
|
// 使用实际插入的 header 数量计算偏移,而非期望数量
|
|
1022
403
|
// 当 headers 全部重复被跳过时,headerInsertCount = 0,不应偏移
|
|
1023
|
-
const pasteLineNumber =
|
|
404
|
+
const pasteLineNumber = computePasteLineNumber(
|
|
1024
405
|
triggerLineNumber,
|
|
1025
406
|
headerInsertCount,
|
|
1026
407
|
fullPath,
|
|
@@ -1029,7 +410,7 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
|
|
|
1029
410
|
|
|
1030
411
|
// 如果 headers 通过文件写入,等待 Xcode reload
|
|
1031
412
|
if (headerInsertCount > 0) {
|
|
1032
|
-
await
|
|
413
|
+
await sleep(600);
|
|
1033
414
|
}
|
|
1034
415
|
|
|
1035
416
|
// ── Step 7: Jump + 选中行内容 + 粘贴替换 ──
|
|
@@ -1039,11 +420,11 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
|
|
|
1039
420
|
console.warn(` ⚠️ 剪贴板写入失败`);
|
|
1040
421
|
return;
|
|
1041
422
|
}
|
|
1042
|
-
await
|
|
423
|
+
await sleep(100);
|
|
1043
424
|
XA.jumpToLineInXcode(pasteLineNumber);
|
|
1044
|
-
await
|
|
425
|
+
await sleep(500);
|
|
1045
426
|
XA.selectAndPasteInXcode();
|
|
1046
|
-
await
|
|
427
|
+
await sleep(300);
|
|
1047
428
|
});
|
|
1048
429
|
NU.notify(`已插入「${selected.title || '代码片段'}」`, 'AutoSnippet');
|
|
1049
430
|
return;
|
|
@@ -1069,7 +450,7 @@ async function _preflightDeps(fullPath, headers, selected, NU) {
|
|
|
1069
450
|
const result = { blocked: false };
|
|
1070
451
|
|
|
1071
452
|
// 始终从所有 headers 推断模块(不仅依赖 selected.moduleName)
|
|
1072
|
-
const inferredModules =
|
|
453
|
+
const inferredModules = inferModulesFromHeaders(headers);
|
|
1073
454
|
if (selected.moduleName && !inferredModules.includes(selected.moduleName)) {
|
|
1074
455
|
inferredModules.push(selected.moduleName);
|
|
1075
456
|
}
|
|
@@ -1108,7 +489,7 @@ async function _preflightDeps(fullPath, headers, selected, NU) {
|
|
|
1108
489
|
}
|
|
1109
490
|
|
|
1110
491
|
const ensureResult = spmService.ensureDependency(currentTarget, mod);
|
|
1111
|
-
const decision =
|
|
492
|
+
const decision = evaluateDepResult(ensureResult, currentTarget, mod);
|
|
1112
493
|
|
|
1113
494
|
if (decision.action === 'block') {
|
|
1114
495
|
console.warn(
|
|
@@ -1126,7 +507,7 @@ async function _preflightDeps(fullPath, headers, selected, NU) {
|
|
|
1126
507
|
if (!result.depWarnings) {
|
|
1127
508
|
result.depWarnings = new Map();
|
|
1128
509
|
}
|
|
1129
|
-
const reviewResult =
|
|
510
|
+
const reviewResult = handleDepReview({
|
|
1130
511
|
spmService,
|
|
1131
512
|
currentTarget,
|
|
1132
513
|
mod,
|