autosnippet 1.1.22 → 1.1.23

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/create.js CHANGED
@@ -61,8 +61,6 @@ async function findTargetRootDir(filePath) {
61
61
  // 检查当前目录是否包含 Code 或 Sources 目录
62
62
  for (const entry of entries) {
63
63
  if (entry.isDirectory() && (entry.name === 'Code' || entry.name === 'Sources')) {
64
- // 找到包含 Code 或 Sources 的目录,这就是 target 根目录
65
- console.log(`[findTargetRootDir] 找到 target 根目录: ${currentPath} (包含 ${entry.name})`);
66
64
  return currentPath;
67
65
  }
68
66
  }
@@ -82,7 +80,6 @@ async function findTargetRootDir(filePath) {
82
80
  }
83
81
  }
84
82
 
85
- console.log(`[findTargetRootDir] 未找到 target 根目录(Code 或 Sources)`);
86
83
  return null;
87
84
  }
88
85
 
@@ -324,7 +321,6 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
324
321
  // 如果找不到 target 根目录,使用 Package.swift 所在目录作为后备
325
322
  targetRootDir = packageInfo.path;
326
323
  }
327
- console.log(`[createCodeSnippets] target 根目录: ${targetRootDir}`);
328
324
  const moduleRoot = targetRootDir;
329
325
 
330
326
  // ✅ 查找头文件(适配 SPM 的 include/ModuleName/ 结构)
@@ -332,7 +328,7 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
332
328
  const headerPath = await findPath.findSubHeaderPath(targetRootDir, headerNameWithoutExt, moduleName);
333
329
 
334
330
  snippet['{content}'] = codeList;
335
- snippet['{specName}'] = moduleName; // ✅ specName 是 target 名称(如 BDNetworkAPI),不是包名(如 Business)
331
+ snippet['{specName}'] = moduleName;
336
332
 
337
333
  if (headerPath) {
338
334
  // ✅ headName 存储相对于 target 根目录的相对路径
@@ -447,7 +443,7 @@ async function saveFromFile(specFile, snippet) {
447
443
  if (err.code === 'ENOENT') {
448
444
  rootPlaceholder = { list: [] };
449
445
  } else {
450
- console.error(`[saveFromFile] 读取根配置文件失败: ${err.message}`);
446
+ // 忽略错误
451
447
  }
452
448
  }
453
449
 
@@ -475,11 +471,10 @@ async function saveFromFile(specFile, snippet) {
475
471
  // 写入根配置文件
476
472
  const rootContent = JSON.stringify(rootPlaceholder, null, 4);
477
473
  fs.writeFileSync(rootSpecFile, rootContent, 'utf8');
478
- console.log(`[saveFromFile] 已同步 snippet 到根目录配置文件: ${rootSpecFile}`);
479
474
  }
480
475
  }
481
476
  } catch (err) {
482
- console.error(`[saveFromFile] 同步到根配置文件失败: ${err.message}`);
477
+ // 忽略错误
483
478
  }
484
479
 
485
480
  // ✅ 只写入刚创建的单个代码片段
package/bin/findPath.js CHANGED
@@ -12,9 +12,6 @@ const README_NAME = 'readme.md';
12
12
  const directoryCache = new Map();
13
13
  const CACHE_TTL = 60000; // 缓存 60 秒
14
14
 
15
- /**
16
- * 获取目录内容(带缓存)
17
- */
18
15
  async function getDirectoryEntries(dirPath) {
19
16
  const cacheKey = path.resolve(dirPath);
20
17
 
@@ -23,15 +20,11 @@ async function getDirectoryEntries(dirPath) {
23
20
  try {
24
21
  const stats = await fs.promises.stat(dirPath);
25
22
  if (stats.isFile()) {
26
- // 如果是文件,清除可能的错误缓存,然后返回 null
27
- console.log(`[getDirectoryEntries] 路径是文件,返回 null: ${dirPath}`);
28
23
  directoryCache.delete(cacheKey);
29
24
  return null;
30
25
  }
31
26
  } catch (err) {
32
- // 如果 stat 失败,可能是路径不存在或其他错误,继续处理
33
- // 如果是 ENOTDIR,会在下面的 readdir 中被捕获
34
- console.log(`[getDirectoryEntries] stat 失败: ${dirPath}, 错误: ${err.code || err.message}`);
27
+ // 继续
35
28
  }
36
29
 
37
30
  const cached = directoryCache.get(cacheKey);
@@ -41,12 +34,10 @@ async function getDirectoryEntries(dirPath) {
41
34
  }
42
35
 
43
36
  try {
44
- console.log(`[getDirectoryEntries] 尝试读取目录: ${dirPath}`);
45
37
  const entries = await fs.promises.readdir(dirPath, {
46
- withFileTypes: true // 返回 Dirent 对象,减少 stat 调用
38
+ withFileTypes: true
47
39
  });
48
40
 
49
- // 清理过期缓存(保持缓存大小合理)
50
41
  if (directoryCache.size > 1000) {
51
42
  const now = Date.now();
52
43
  for (const [key, value] of directoryCache.entries()) {
@@ -63,19 +54,13 @@ async function getDirectoryEntries(dirPath) {
63
54
 
64
55
  return entries;
65
56
  } catch (err) {
66
- // ✅ 处理各种文件系统错误
67
- console.log(`[getDirectoryEntries] readdir 失败: ${dirPath}, 错误: ${err.code || err.message}`);
68
57
  if (err.code === 'ENOENT' || err.code === 'EACCES' || err.code === 'ENOTDIR') {
69
- return null; // 路径不存在、权限不足或不是目录
58
+ return null;
70
59
  }
71
60
  throw err;
72
61
  }
73
62
  }
74
63
 
75
- /**
76
- * 检测是否为工程根目录
77
- * 通过查找常见的工程根目录标记来判断
78
- */
79
64
  function isProjectRoot(dirPath, entries) {
80
65
  if (!entries) {
81
66
  return false;
@@ -83,24 +68,17 @@ function isProjectRoot(dirPath, entries) {
83
68
 
84
69
  // 工程根目录标记(按优先级)
85
70
  const rootMarkers = [
86
- '.git', // Git 仓库根目录
87
- PACKAGE_SWIFT, // SPM 项目根目录
88
- '.xcodeproj', // Xcode 项目根目录
89
- '.xcworkspace', // Xcode 工作空间根目录
90
- 'Podfile', // CocoaPods 项目根目录
91
- '.swiftpm', // Swift Package Manager 元数据目录
71
+ '.git',
72
+ PACKAGE_SWIFT,
73
+ '.xcodeproj',
74
+ '.xcworkspace',
75
+ 'Podfile',
76
+ '.swiftpm',
92
77
  ];
93
78
 
94
79
  for (const entry of entries) {
95
80
  const name = entry.name;
96
-
97
- // 检查文件标记
98
- if (entry.isFile() && rootMarkers.includes(name)) {
99
- return true;
100
- }
101
-
102
- // 检查目录标记(.git 可能是目录)
103
- if (entry.isDirectory() && rootMarkers.includes(name)) {
81
+ if (rootMarkers.includes(name) && (entry.isFile() || entry.isDirectory())) {
104
82
  return true;
105
83
  }
106
84
  }
@@ -108,55 +86,29 @@ function isProjectRoot(dirPath, entries) {
108
86
  return false;
109
87
  }
110
88
 
111
- // 向上查找AutoSnippet配置文件(优化:找到配置文件后,继续向上查找直到找到工程根目录)
112
- async function findASSpecPathAsync(filePath) {
113
- let configPath = null;
114
- let configDir = null;
89
+ async function searchUpwardForFile(filePath, fileName) {
115
90
  let currentPath = path.resolve(filePath);
116
- const maxLevels = 20; // 安全限制:最多向上查找 20 层
91
+ const maxLevels = 20;
117
92
  let levelsChecked = 0;
118
93
 
119
94
  while (currentPath && levelsChecked < maxLevels) {
120
95
  try {
121
96
  const entries = await getDirectoryEntries(currentPath);
122
-
123
- // 查找 AutoSnippet.boxspec.json
124
97
  if (entries) {
125
98
  for (const entry of entries) {
126
- if (entry.isFile() && entry.name === HOLDER_NAME) {
127
- if (!configPath) {
128
- // 第一次找到配置文件
129
- configPath = path.join(currentPath, entry.name);
130
- configDir = currentPath;
131
- }
99
+ if (entry.isFile() && entry.name === fileName) {
100
+ return path.join(currentPath, entry.name);
132
101
  }
133
102
  }
134
103
  }
135
104
 
136
- // 如果已经找到配置文件,检查是否到达工程根目录
137
- if (configPath) {
138
- // 如果在配置文件所在目录或以上发现了工程根目录标记,停止查找
139
- if (isProjectRoot(currentPath, entries)) {
140
- return configPath;
141
- }
142
-
143
- // 如果已经向上查找了,但没有找到工程根目录标记,继续查找
144
- if (path.resolve(currentPath) !== configDir) {
145
- // 当前目录不是配置文件所在目录,继续向上查找
146
- }
147
- }
148
-
149
- // 继续向上查找
150
105
  const parentPath = path.dirname(currentPath);
151
106
  if (parentPath === currentPath) {
152
- // 已到达根目录
153
107
  break;
154
108
  }
155
109
  currentPath = parentPath;
156
110
  levelsChecked++;
157
-
158
111
  } catch (err) {
159
- // 错误处理:权限错误继续查找
160
112
  if (err.code === 'ENOENT' || err.code === 'EACCES') {
161
113
  const parentPath = path.dirname(currentPath);
162
114
  if (parentPath === currentPath) {
@@ -166,18 +118,22 @@ async function findASSpecPathAsync(filePath) {
166
118
  levelsChecked++;
167
119
  continue;
168
120
  }
169
- // 其他错误,抛出
170
121
  throw err;
171
122
  }
172
123
  }
173
124
 
174
- // 如果找到配置文件,返回它(即使没有找到明确的工程根目录)
175
- return configPath || null;
125
+ return null;
126
+ }
127
+
128
+ async function findASSpecPathAsync(filePath) {
129
+ const holderPath = await searchUpwardForFile(filePath, HOLDER_NAME);
130
+ if (holderPath) {
131
+ return holderPath;
132
+ }
133
+
134
+ return await searchUpwardForFile(filePath, ROOT_MARKER_NAME);
176
135
  }
177
136
 
178
- /**
179
- * 同步版本:检测是否为工程根目录
180
- */
181
137
  function isProjectRootSync(dirPath, files) {
182
138
  if (!files || files.length === 0) {
183
139
  return false;
@@ -185,17 +141,15 @@ function isProjectRootSync(dirPath, files) {
185
141
 
186
142
  // 工程根目录标记(按优先级)
187
143
  const rootMarkers = [
188
- '.git', // Git 仓库根目录
189
- PACKAGE_SWIFT, // SPM 项目根目录
190
- '.xcodeproj', // Xcode 项目根目录
191
- '.xcworkspace', // Xcode 工作空间根目录
192
- 'Podfile', // CocoaPods 项目根目录
144
+ '.git',
145
+ PACKAGE_SWIFT,
146
+ '.xcodeproj',
147
+ '.xcworkspace',
148
+ 'Podfile',
193
149
  ];
194
150
 
195
- // 检查文件名
196
151
  for (const filename of files) {
197
152
  if (rootMarkers.includes(filename)) {
198
- // 检查是否为文件或目录
199
153
  try {
200
154
  const filePath = path.join(dirPath, filename);
201
155
  const stats = fs.lstatSync(filePath);
@@ -203,7 +157,7 @@ function isProjectRootSync(dirPath, files) {
203
157
  return true;
204
158
  }
205
159
  } catch (err) {
206
- // 忽略错误,继续检查
160
+ // 继续检查
207
161
  }
208
162
  }
209
163
  }
@@ -211,45 +165,35 @@ function isProjectRootSync(dirPath, files) {
211
165
  return false;
212
166
  }
213
167
 
214
- // 向上查找AutoSnippet配置文件(保留回调版本,兼容现有代码)
215
168
  function findASSpecPath(filePath, callback, configPath, configDir) {
216
- // 初始化参数
217
169
  if (configPath === undefined) configPath = null;
218
170
  if (configDir === undefined) configDir = null;
219
171
 
220
- const maxLevels = 20; // 安全限制:最多向上查找 20 层
172
+ const maxLevels = 20;
221
173
  let levelsChecked = 0;
222
174
 
223
175
  function search(currentPath, foundConfigPath, foundConfigDir, level) {
224
176
  if (level >= maxLevels) {
225
177
  if (foundConfigPath) {
226
178
  callback(foundConfigPath);
227
- } else {
228
- console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
229
179
  }
230
180
  return;
231
181
  }
232
182
 
233
183
  fs.readdir(currentPath, function (err, files) {
234
184
  if (err) {
235
- // 错误处理:权限错误继续查找
236
185
  if (err.code === 'ENOENT' || err.code === 'EACCES') {
237
186
  const parentPath = path.join(currentPath, '/..');
238
187
  const parentResolvedPath = path.resolve(parentPath);
239
188
 
240
- // 如果已经到达根目录,停止查找
241
189
  if (parentResolvedPath === path.resolve(currentPath)) {
242
190
  if (foundConfigPath) {
243
191
  callback(foundConfigPath);
244
- } else {
245
- console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
246
192
  }
247
193
  return;
248
194
  }
249
195
 
250
196
  search(parentPath, foundConfigPath, foundConfigDir, level + 1);
251
- } else {
252
- console.log(err);
253
197
  }
254
198
  return;
255
199
  }
@@ -258,41 +202,32 @@ function findASSpecPath(filePath, callback, configPath, configDir) {
258
202
  let currentConfigPath = foundConfigPath;
259
203
  let currentConfigDir = foundConfigDir;
260
204
 
261
- // 查找 AutoSnippet.boxspec.json
262
- files.forEach(function (filename) {
263
- if (filename === HOLDER_NAME) {
205
+ files.forEach(function (filename) {
206
+ if (filename === HOLDER_NAME) {
264
207
  isFound = true;
265
208
  if (!currentConfigPath) {
266
- // 第一次找到配置文件,记录路径(不立即调用回调)
267
209
  currentConfigPath = path.join(currentPath, filename);
268
210
  currentConfigDir = path.resolve(currentPath);
269
- }
270
- }
271
- });
211
+ }
212
+ }
213
+ });
272
214
 
273
- // 如果已经找到配置文件,检查是否到达工程根目录
274
215
  if (currentConfigPath) {
275
- // 如果在配置文件所在目录或以上发现了工程根目录标记,停止查找并调用回调
276
216
  if (isProjectRootSync(currentPath, files)) {
277
217
  callback(currentConfigPath);
278
- return;
279
- }
218
+ return;
219
+ }
280
220
  }
281
221
 
282
- // 继续向上查找
283
222
  const parentPath = path.join(currentPath, '/..');
284
223
  const parentResolvedPath = path.resolve(parentPath);
285
224
 
286
- // 如果已经到达根目录,停止查找
287
225
  if (parentResolvedPath === path.resolve(currentPath)) {
288
- // 如果找到了配置文件,即使没找到工程根目录标记,也调用回调
289
226
  if (currentConfigPath) {
290
227
  callback(currentConfigPath);
291
- } else {
292
- console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
293
228
  }
294
- return;
295
- }
229
+ return;
230
+ }
296
231
 
297
232
  search(parentPath, currentConfigPath, currentConfigDir, level + 1);
298
233
  });
@@ -301,7 +236,6 @@ function findASSpecPath(filePath, callback, configPath, configDir) {
301
236
  search(filePath, configPath, configDir, 0);
302
237
  }
303
238
 
304
- // 向上查找 Package.swift 文件(SPM 模块规范文件)
305
239
  async function findPackageSwiftPath(filePath) {
306
240
  let currentPath = path.resolve(filePath);
307
241
 
@@ -317,14 +251,12 @@ async function findPackageSwiftPath(filePath) {
317
251
  continue;
318
252
  }
319
253
 
320
- // 查找 Package.swift 文件
321
254
  for (const entry of entries) {
322
255
  if (entry.isFile() && entry.name === PACKAGE_SWIFT) {
323
256
  return path.join(currentPath, entry.name);
324
257
  }
325
258
  }
326
259
 
327
- // 继续向上查找
328
260
  const parentPath = path.dirname(currentPath);
329
261
  if (parentPath === currentPath) {
330
262
  break;
@@ -347,14 +279,9 @@ async function findPackageSwiftPath(filePath) {
347
279
  return null;
348
280
  }
349
281
 
350
- /**
351
- * 解析 Package.swift 文件,提取模块信息
352
- */
353
282
  async function parsePackageSwift(packagePath) {
354
283
  try {
355
284
  const content = await fs.promises.readFile(packagePath, 'utf8');
356
-
357
- // 简单解析 Package.swift(实际可以使用 Swift AST 解析器)
358
285
  const packageNameMatch = content.match(/name:\s*"([^"]+)"/);
359
286
  const targetsMatch = content.match(/\.target\s*\([^)]+name:\s*"([^"]+)"/g);
360
287
 
@@ -376,31 +303,24 @@ async function parsePackageSwift(packagePath) {
376
303
  path: path.dirname(packagePath)
377
304
  };
378
305
  } catch (err) {
379
- console.error('Error parsing Package.swift:', err);
380
306
  return null;
381
307
  }
382
308
  }
383
309
 
384
- // 向下查找模块头文件(优化:适配 SPM 结构,优先查找 include/ModuleName/ 目录)
385
310
  async function findSubHeaderPath(filePath, headerName, moduleName) {
386
- // ✅ 优先查找 Code/ 目录(实际源代码位置)
387
- // 按照用户期望的顺序:target根目录 → Code → 子文件夹/头文件
388
311
  const codePath = path.join(filePath, 'Code');
389
312
  try {
390
313
  const stats = await fs.promises.stat(codePath);
391
314
  if (stats.isDirectory()) {
392
- // 在 Code 目录中递归查找头文件
393
315
  const result = await findSubHeaderPath(codePath, headerName, null);
394
316
  if (result) {
395
317
  return result;
396
318
  }
397
319
  }
398
320
  } catch {
399
- // Code 目录不存在,继续查找
321
+ // 继续查找
400
322
  }
401
323
 
402
- // ✅ 降级:查找 include/ModuleName/ 目录(SPM 公共头文件结构)
403
- // 注意:include/ 目录通常是符号链接,但作为后备方案保留
404
324
  if (moduleName) {
405
325
  const includePath = path.join(filePath, 'include', moduleName);
406
326
  try {
@@ -411,15 +331,14 @@ async function findSubHeaderPath(filePath, headerName, moduleName) {
411
331
  await fs.promises.access(headerPath);
412
332
  return headerPath;
413
333
  } catch {
414
- // 头文件不存在,继续查找
334
+ // 继续查找
415
335
  }
416
336
  }
417
337
  } catch {
418
- // include 目录不存在,继续查找
338
+ // 继续查找
419
339
  }
420
340
  }
421
341
 
422
- // ✅ 最后:在整个模块目录中查找(深度限制)
423
342
  try {
424
343
  const entries = await getDirectoryEntries(filePath);
425
344
  if (!entries) {
@@ -430,7 +349,6 @@ async function findSubHeaderPath(filePath, headerName, moduleName) {
430
349
  if (entry.isFile() && entry.name === `${headerName}.h`) {
431
350
  return path.join(filePath, entry.name);
432
351
  } else if (entry.isDirectory()) {
433
- // 跳过 node_modules、.git 等目录
434
352
  if (entry.name.startsWith('.') || entry.name === 'node_modules') {
435
353
  continue;
436
354
  }
@@ -442,56 +360,36 @@ async function findSubHeaderPath(filePath, headerName, moduleName) {
442
360
  }
443
361
  }
444
362
  } catch (err) {
445
- console.error(err);
363
+ // 忽略错误
446
364
  }
447
365
 
448
366
  return null;
449
367
  }
450
368
 
451
- // 向下查找AutoSnippet配置文件(优化:异步 I/O)
452
369
  async function findSubASSpecPath(filePath) {
453
- console.log(`[findSubASSpecPath] 开始查找,传入路径: ${filePath}`);
454
370
  let resultArray = [];
455
371
 
456
372
  try {
457
- // ✅ 确保 filePath 是目录路径,而不是文件路径
458
- // 如果传入的是文件路径,获取其所在目录
459
373
  let dirPath = filePath;
460
374
 
461
- // 首先检查路径是否存在,并确定是文件还是目录
462
375
  try {
463
376
  const stats = await fs.promises.stat(filePath);
464
377
  if (stats.isFile()) {
465
- // 如果是文件,使用其所在目录
466
378
  dirPath = path.dirname(filePath);
467
- console.log(`[findSubASSpecPath] 检测到文件路径,转换为目录: ${filePath} -> ${dirPath}`);
468
379
  } else if (!stats.isDirectory()) {
469
- // 既不是文件也不是目录,直接返回空数组
470
- console.log(`[findSubASSpecPath] 路径既不是文件也不是目录,返回空数组: ${filePath}`);
471
380
  return resultArray;
472
381
  }
473
382
  } catch (err) {
474
- console.log(`[findSubASSpecPath] stat 失败: ${filePath}, 错误: ${err.code || err.message}`);
475
- // 如果 stat 失败(路径不存在、权限错误等),尝试使用 dirname
476
383
  if (err.code === 'ENOENT' || err.code === 'EACCES') {
477
- // 如果路径看起来像文件路径(包含文件名),尝试使用 dirname
478
384
  if (path.basename(filePath) === HOLDER_NAME || path.extname(filePath) !== '') {
479
385
  dirPath = path.dirname(filePath);
480
- console.log(`[findSubASSpecPath] 根据路径特征转换为目录: ${filePath} -> ${dirPath}`);
481
386
  } else {
482
- // 可能是目录路径,但不存在,直接返回空数组
483
- console.log(`[findSubASSpecPath] 路径不存在,返回空数组: ${filePath}`);
484
387
  return resultArray;
485
388
  }
486
389
  } else {
487
- // 其他错误,直接返回空数组
488
- console.log(`[findSubASSpecPath] stat 错误,返回空数组: ${filePath}, 错误: ${err.code || err.message}`);
489
390
  return resultArray;
490
391
  }
491
392
  }
492
-
493
- // ✅ 再次确认 dirPath 是目录(防止 ENOTDIR 错误)
494
- console.log(`[findSubASSpecPath] 调用 getDirectoryEntries: ${dirPath}`);
495
393
  const entries = await getDirectoryEntries(dirPath);
496
394
  if (!entries) {
497
395
  return resultArray;
@@ -501,7 +399,6 @@ async function findSubASSpecPath(filePath) {
501
399
  if (entry.isFile() && entry.name === HOLDER_NAME) {
502
400
  resultArray.push(path.join(dirPath, entry.name));
503
401
  } else if (entry.isDirectory()) {
504
- // 跳过 node_modules、.git 等目录
505
402
  if (entry.name.startsWith('.') || entry.name === 'node_modules') {
506
403
  continue;
507
404
  }
@@ -511,187 +408,34 @@ async function findSubASSpecPath(filePath) {
511
408
  }
512
409
  }
513
410
  } catch (err) {
514
- // ✅ 忽略 ENOTDIR 错误(传入的是文件路径)和其他文件系统错误
515
411
  if (err.code !== 'ENOTDIR') {
516
- console.log(err);
412
+ // 忽略错误
517
413
  }
518
414
  }
519
415
 
520
416
  return resultArray;
521
417
  }
522
418
 
523
- /**
524
- * 向上查找项目根目录(查找 AutoSnippetRoot.boxspec.json 文件)
525
- * @param {string} filePath - 起始文件路径或目录路径
526
- * @returns {Promise<string|null>} 项目根目录路径(AutoSnippetRoot.boxspec.json 所在目录),如果找不到返回 null
527
- */
528
419
  async function findProjectRoot(filePath) {
529
- console.log(`[findProjectRoot] 开始查找,传入路径: ${filePath}`);
530
- // ✅ 简化逻辑:向上查找 AutoSnippetRoot.boxspec.json 文件
531
- let currentPath = path.resolve(filePath);
420
+ let startPath = path.resolve(filePath);
532
421
 
533
- // 检查路径是文件还是目录
534
422
  try {
535
- const stats = await fs.promises.stat(currentPath);
423
+ const stats = await fs.promises.stat(startPath);
536
424
  if (stats.isFile()) {
537
- // 如果是文件,使用其所在目录
538
- currentPath = path.dirname(currentPath);
539
- console.log(`[findProjectRoot] 检测到文件路径,转换为目录: ${filePath} -> ${currentPath}`);
425
+ startPath = path.dirname(startPath);
540
426
  }
541
427
  } catch (err) {
542
- console.log(`[findProjectRoot] stat 失败: ${currentPath}, 错误: ${err.code || err.message}`);
543
- // 如果 stat 失败,假设是目录路径,或者使用 dirname 作为后备
544
428
  if (err.code === 'ENOENT' || err.code === 'EACCES') {
545
- // 如果路径看起来像文件路径,使用 dirname
546
429
  if (path.basename(filePath).includes('.') || path.extname(filePath) !== '') {
547
- currentPath = path.dirname(currentPath);
548
- }
549
- }
550
- // 其他错误继续处理,可能是目录路径
551
- }
552
-
553
- const maxLevels = 20;
554
- let levelsChecked = 0;
555
-
556
- while (currentPath && levelsChecked < maxLevels) {
557
- try {
558
- // ✅ 查找 AutoSnippetRoot.boxspec.json 文件
559
- const rootMarkerPath = path.join(currentPath, ROOT_MARKER_NAME);
560
- try {
561
- const stats = await fs.promises.stat(rootMarkerPath);
562
- if (stats.isFile()) {
563
- console.log(`[findProjectRoot] 找到根目录标记文件: ${rootMarkerPath}`);
564
- return currentPath;
565
- }
566
- } catch (err) {
567
- // 文件不存在,继续向上查找
568
- }
569
-
570
- const parentPath = path.dirname(currentPath);
571
- if (parentPath === currentPath) {
572
- break;
573
- }
574
- currentPath = parentPath;
575
- levelsChecked++;
576
- } catch (err) {
577
- // ✅ 处理各种文件系统错误
578
- if (err.code === 'ENOENT' || err.code === 'EACCES' || err.code === 'ENOTDIR') {
579
- const parentPath = path.dirname(currentPath);
580
- if (parentPath === currentPath) {
581
- break;
582
- }
583
- currentPath = parentPath;
584
- levelsChecked++;
585
- continue;
430
+ startPath = path.dirname(startPath);
586
431
  }
587
- throw err;
588
432
  }
589
433
  }
590
434
 
591
- console.log(`[findProjectRoot] 未找到根目录标记文件 ${ROOT_MARKER_NAME}`);
592
- return null;
593
- }
594
-
595
- /**
596
- * 从项目根目录找到模块,并返回模块名和头文件名
597
- * @param {string} projectRoot - 项目根目录
598
- * @param {string} specName - 模块名(target 名称)
599
- * @param {string} headName - 头文件相对路径(相对于模块根目录)
600
- * @returns {Promise<{moduleName: string, headerFileName: string}|null>}
601
- */
602
- async function findModuleHeaderFromRoot(projectRoot, specName, headName) {
603
- if (!projectRoot || !specName || !headName) {
604
- return null;
605
- }
606
-
607
- // 从项目根目录向下查找包含 specName 的目录(可能是模块目录)
608
- // 例如:BDVideoPlayer、UI/BDVideoPlayer 等
609
- const headerFileName = path.basename(headName);
610
-
611
- // 尝试在项目根目录下查找模块目录
612
- // 1. 直接查找 specName 目录
613
- const directModulePath = path.join(projectRoot, specName);
614
- try {
615
- const entries = await getDirectoryEntries(directModulePath);
616
- if (entries) {
617
- // 检查是否有 include/ModuleName/ 目录
618
- const includePath = path.join(directModulePath, 'include', specName, headerFileName);
619
- try {
620
- await fs.promises.access(includePath);
621
- return {
622
- moduleName: specName,
623
- headerFileName: headerFileName
624
- };
625
- } catch (err) {
626
- // 继续查找
627
- }
628
- }
629
- } catch (err) {
630
- // 继续查找
631
- }
632
-
633
- // 2. 递归查找包含 specName 的目录
634
- const foundModule = await findModuleDirectory(projectRoot, specName, headerFileName);
635
- if (foundModule) {
636
- return foundModule;
637
- }
638
-
639
- // 如果找不到,返回默认值(使用 specName 和 headerFileName)
640
- return {
641
- moduleName: specName,
642
- headerFileName: headerFileName
643
- };
644
- }
645
-
646
- /**
647
- * 递归查找模块目录
648
- */
649
- async function findModuleDirectory(dirPath, moduleName, headerFileName) {
650
- try {
651
- const entries = await getDirectoryEntries(dirPath);
652
- if (!entries) {
653
- return null;
654
- }
655
-
656
- for (const entry of entries) {
657
- if (entry.isDirectory()) {
658
- const dirName = entry.name;
659
- // 跳过隐藏目录和常见目录
660
- if (dirName.startsWith('.') || dirName === 'node_modules') {
661
- continue;
662
- }
663
-
664
- const fullPath = path.join(dirPath, dirName);
665
-
666
- // 检查是否是模块目录(包含 include/ModuleName/ 结构)
667
- const includePath = path.join(fullPath, 'include', moduleName, headerFileName);
668
- try {
669
- await fs.promises.access(includePath);
670
- return {
671
- moduleName: moduleName,
672
- headerFileName: headerFileName
673
- };
674
- } catch (err) {
675
- // 继续递归查找
676
- const found = await findModuleDirectory(fullPath, moduleName, headerFileName);
677
- if (found) {
678
- return found;
679
- }
680
- }
681
- }
682
- }
683
- } catch (err) {
684
- // 忽略错误
685
- }
686
-
687
- return null;
435
+ const rootMarkerPath = await searchUpwardForFile(startPath, ROOT_MARKER_NAME);
436
+ return rootMarkerPath ? path.dirname(rootMarkerPath) : null;
688
437
  }
689
438
 
690
- /**
691
- * 获取根目录的 AutoSnippetRoot.boxspec.json 文件路径
692
- * @param {string} filePath - 起始文件路径或目录路径
693
- * @returns {Promise<string|null>} 根目录的 AutoSnippetRoot.boxspec.json 文件路径,如果找不到返回 null
694
- */
695
439
  async function getRootSpecFilePath(filePath) {
696
440
  const projectRoot = await findProjectRoot(filePath);
697
441
  if (!projectRoot) {
@@ -699,12 +443,10 @@ async function getRootSpecFilePath(filePath) {
699
443
  }
700
444
  const rootSpecFile = path.join(projectRoot, ROOT_MARKER_NAME);
701
445
 
702
- // 检查文件是否存在,如果不存在则创建
703
446
  try {
704
447
  await fs.promises.access(rootSpecFile);
705
448
  } catch (err) {
706
449
  if (err.code === 'ENOENT') {
707
- // 文件不存在,创建它
708
450
  const specObj = {
709
451
  list: []
710
452
  };
@@ -723,6 +465,5 @@ exports.parsePackageSwift = parsePackageSwift;
723
465
  exports.findSubHeaderPath = findSubHeaderPath;
724
466
  exports.findSubASSpecPath = findSubASSpecPath;
725
467
  exports.findProjectRoot = findProjectRoot;
726
- exports.findModuleHeaderFromRoot = findModuleHeaderFromRoot;
727
468
  exports.getRootSpecFilePath = getRootSpecFilePath;
728
469
  exports.ROOT_MARKER_NAME = ROOT_MARKER_NAME;
package/bin/init.js CHANGED
@@ -16,27 +16,18 @@ async function mergeSubSpecs(mainSpecFile) {
16
16
  list: []
17
17
  };
18
18
 
19
- // ✅ 找到项目根目录的 AutoSnippetRoot.boxspec.json 文件
20
- console.log(`[mergeSubSpecs] 主配置文件路径: ${mainSpecFile}`);
21
19
  const rootSpecFile = await findPath.getRootSpecFilePath(mainSpecFile);
22
20
  if (!rootSpecFile) {
23
- console.error(`[mergeSubSpecs] 未找到项目根目录,无法聚合配置`);
24
21
  return;
25
22
  }
26
- console.log(`[mergeSubSpecs] 根目录配置文件: ${rootSpecFile}`);
27
23
 
28
24
  const projectRoot = path.dirname(rootSpecFile);
29
25
  const searchRoot = projectRoot;
30
- console.log(`[mergeSubSpecs] 项目根目录: ${projectRoot}`);
31
- console.log(`[mergeSubSpecs] 搜索根目录: ${searchRoot}`);
32
26
 
33
27
  const specSlashIndex = rootSpecFile.lastIndexOf('/');
34
28
  const specFilePath = rootSpecFile.substring(0, specSlashIndex + 1);
35
29
 
36
- // ✅ 从项目根目录向下查找子模块的 AutoSnippet.boxspec.json
37
- console.log(`[mergeSubSpecs] 开始查找子模块配置,搜索根目录: ${searchRoot}`);
38
30
  const array = await findPath.findSubASSpecPath(searchRoot);
39
- console.log(`[mergeSubSpecs] 找到 ${array.length} 个子模块配置文件`);
40
31
 
41
32
  for (let i = 0; i < array.length; i++) {
42
33
  const filename = array[i];
@@ -62,8 +53,6 @@ async function mergeSubSpecs(mainSpecFile) {
62
53
  }
63
54
  }
64
55
  idsArray.push(item['{identifier}']);
65
- // ✅ SPM 模块:headName 已经是相对于模块根目录的相对路径
66
- // headName 保持相对于各自模块根目录的路径(不需要转换)
67
56
  return true;
68
57
  });
69
58
  specObj.list = specObj.list.concat(arr);
@@ -76,12 +65,10 @@ async function mergeSubSpecs(mainSpecFile) {
76
65
  try {
77
66
  const content = JSON.stringify(specObj, null, 4);
78
67
  if (content) {
79
- // ✅ 写入根目录的 AutoSnippetRoot.boxspec.json
80
68
  fs.writeFileSync(rootSpecFile, content, 'utf8');
81
- console.log(`[mergeSubSpecs] 已聚合 ${specObj.list.length} 个 snippet 到根目录配置文件`);
82
69
  }
83
70
  } catch (err) {
84
- console.error(err);
71
+ // 忽略错误
85
72
  }
86
73
  }
87
74
 
@@ -104,7 +91,6 @@ async function findTargetRootDirFromPath(dirPath) {
104
91
  for (const entry of entries) {
105
92
  if (entry.isDirectory() && (entry.name === 'Code' || entry.name === 'Sources')) {
106
93
  // 找到包含 Code 或 Sources 的目录,这就是 target 根目录
107
- console.log(`[findTargetRootDirFromPath] 找到 target 根目录: ${currentPath} (包含 ${entry.name})`);
108
94
  return currentPath;
109
95
  }
110
96
  }
@@ -124,7 +110,6 @@ async function findTargetRootDirFromPath(dirPath) {
124
110
  }
125
111
  }
126
112
 
127
- console.log(`[findTargetRootDirFromPath] 未找到 target 根目录(Code 或 Sources)`);
128
113
  return null;
129
114
  }
130
115
 
@@ -137,31 +122,22 @@ async function initSpec() {
137
122
  let packagePath = await findPath.findPackageSwiftPath(CMD_PATH);
138
123
  if (packagePath) {
139
124
  targetRootDir = path.dirname(packagePath);
140
- console.log(`[initSpec] 使用 Package.swift 所在目录作为 target 根目录: ${targetRootDir}`);
141
125
  } else {
142
- // 如果都找不到,使用当前目录
143
126
  targetRootDir = CMD_PATH;
144
- console.log(`[initSpec] 使用当前目录作为 target 根目录: ${targetRootDir}`);
145
127
  }
146
128
  }
147
129
 
148
130
  const filePath = path.join(targetRootDir, 'AutoSnippet.boxspec.json');
149
- console.log(`[initSpec] 将在 target 根目录创建配置文件: ${filePath}`);
150
131
 
151
- // 如果配置文件不存在,创建一个空的
152
132
  try {
153
133
  await fs.promises.access(filePath);
154
- console.log(`[initSpec] 配置文件已存在: ${filePath}`);
155
134
  } catch (error) {
156
135
  const specObj = {
157
136
  list: []
158
137
  };
159
138
  const content = JSON.stringify(specObj, null, 4);
160
139
  fs.writeFileSync(filePath, content, 'utf8');
161
- console.log(`[initSpec] 已创建配置文件: ${filePath}`);
162
140
  }
163
-
164
- // ✅ 聚合子模块配置到根目录的 AutoSnippetRoot.boxspec.json
165
141
  await mergeSubSpecs(filePath);
166
142
  }
167
143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "1.1.22",
3
+ "version": "1.1.23",
4
4
  "description": "A iOS module management tool.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -31,5 +31,8 @@
31
31
  "devDependencies": {},
32
32
  "bugs": {
33
33
  "url": "https://github.com/GxFn/AutoSnippet/issues"
34
+ },
35
+ "directories": {
36
+ "doc": "docs"
34
37
  }
35
38
  }