autosnippet 1.1.18 → 1.1.19
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/findPath.js +108 -22
- package/bin/init.js +5 -0
- package/package.json +1 -1
package/bin/findPath.js
CHANGED
|
@@ -16,6 +16,23 @@ const CACHE_TTL = 60000; // 缓存 60 秒
|
|
|
16
16
|
*/
|
|
17
17
|
async function getDirectoryEntries(dirPath) {
|
|
18
18
|
const cacheKey = path.resolve(dirPath);
|
|
19
|
+
|
|
20
|
+
// ✅ 首先检查路径是否是文件,如果是文件,立即返回 null(避免 ENOTDIR 错误)
|
|
21
|
+
// 注意:这个检查要在缓存检查之前,因为缓存可能包含错误的条目
|
|
22
|
+
try {
|
|
23
|
+
const stats = await fs.promises.stat(dirPath);
|
|
24
|
+
if (stats.isFile()) {
|
|
25
|
+
// 如果是文件,清除可能的错误缓存,然后返回 null
|
|
26
|
+
console.log(`[getDirectoryEntries] 路径是文件,返回 null: ${dirPath}`);
|
|
27
|
+
directoryCache.delete(cacheKey);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
} catch (err) {
|
|
31
|
+
// 如果 stat 失败,可能是路径不存在或其他错误,继续处理
|
|
32
|
+
// 如果是 ENOTDIR,会在下面的 readdir 中被捕获
|
|
33
|
+
console.log(`[getDirectoryEntries] stat 失败: ${dirPath}, 错误: ${err.code || err.message}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
19
36
|
const cached = directoryCache.get(cacheKey);
|
|
20
37
|
|
|
21
38
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
@@ -23,6 +40,7 @@ async function getDirectoryEntries(dirPath) {
|
|
|
23
40
|
}
|
|
24
41
|
|
|
25
42
|
try {
|
|
43
|
+
console.log(`[getDirectoryEntries] 尝试读取目录: ${dirPath}`);
|
|
26
44
|
const entries = await fs.promises.readdir(dirPath, {
|
|
27
45
|
withFileTypes: true // 返回 Dirent 对象,减少 stat 调用
|
|
28
46
|
});
|
|
@@ -44,8 +62,10 @@ async function getDirectoryEntries(dirPath) {
|
|
|
44
62
|
|
|
45
63
|
return entries;
|
|
46
64
|
} catch (err) {
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
// ✅ 处理各种文件系统错误
|
|
66
|
+
console.log(`[getDirectoryEntries] readdir 失败: ${dirPath}, 错误: ${err.code || err.message}`);
|
|
67
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES' || err.code === 'ENOTDIR') {
|
|
68
|
+
return null; // 路径不存在、权限不足或不是目录
|
|
49
69
|
}
|
|
50
70
|
throw err;
|
|
51
71
|
}
|
|
@@ -208,7 +228,7 @@ function findASSpecPath(filePath, callback, configPath, configDir) {
|
|
|
208
228
|
}
|
|
209
229
|
return;
|
|
210
230
|
}
|
|
211
|
-
|
|
231
|
+
|
|
212
232
|
fs.readdir(currentPath, function (err, files) {
|
|
213
233
|
if (err) {
|
|
214
234
|
// 错误处理:权限错误继续查找
|
|
@@ -220,7 +240,7 @@ function findASSpecPath(filePath, callback, configPath, configDir) {
|
|
|
220
240
|
if (parentResolvedPath === path.resolve(currentPath)) {
|
|
221
241
|
if (foundConfigPath) {
|
|
222
242
|
callback(foundConfigPath);
|
|
223
|
-
|
|
243
|
+
} else {
|
|
224
244
|
console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
|
|
225
245
|
}
|
|
226
246
|
return;
|
|
@@ -228,34 +248,34 @@ function findASSpecPath(filePath, callback, configPath, configDir) {
|
|
|
228
248
|
|
|
229
249
|
search(parentPath, foundConfigPath, foundConfigDir, level + 1);
|
|
230
250
|
} else {
|
|
231
|
-
|
|
251
|
+
console.log(err);
|
|
232
252
|
}
|
|
233
|
-
|
|
234
|
-
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
235
255
|
|
|
236
256
|
let isFound = false;
|
|
237
257
|
let currentConfigPath = foundConfigPath;
|
|
238
258
|
let currentConfigDir = foundConfigDir;
|
|
239
259
|
|
|
240
260
|
// 查找 AutoSnippet.boxspec.json
|
|
241
|
-
|
|
242
|
-
|
|
261
|
+
files.forEach(function (filename) {
|
|
262
|
+
if (filename === HOLDER_NAME) {
|
|
243
263
|
isFound = true;
|
|
244
264
|
if (!currentConfigPath) {
|
|
245
265
|
// 第一次找到配置文件,记录路径(不立即调用回调)
|
|
246
266
|
currentConfigPath = path.join(currentPath, filename);
|
|
247
267
|
currentConfigDir = path.resolve(currentPath);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
251
271
|
|
|
252
272
|
// 如果已经找到配置文件,检查是否到达工程根目录
|
|
253
273
|
if (currentConfigPath) {
|
|
254
274
|
// 如果在配置文件所在目录或以上发现了工程根目录标记,停止查找并调用回调
|
|
255
275
|
if (isProjectRootSync(currentPath, files)) {
|
|
256
276
|
callback(currentConfigPath);
|
|
257
|
-
|
|
258
|
-
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
259
279
|
}
|
|
260
280
|
|
|
261
281
|
// 继续向上查找
|
|
@@ -270,8 +290,8 @@ function findASSpecPath(filePath, callback, configPath, configDir) {
|
|
|
270
290
|
} else {
|
|
271
291
|
console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
|
|
272
292
|
}
|
|
273
|
-
|
|
274
|
-
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
275
295
|
|
|
276
296
|
search(parentPath, currentConfigPath, currentConfigDir, level + 1);
|
|
277
297
|
});
|
|
@@ -412,29 +432,71 @@ async function findSubHeaderPath(filePath, headerName, moduleName) {
|
|
|
412
432
|
|
|
413
433
|
// 向下查找AutoSnippet配置文件(优化:异步 I/O)
|
|
414
434
|
async function findSubASSpecPath(filePath) {
|
|
435
|
+
console.log(`[findSubASSpecPath] 开始查找,传入路径: ${filePath}`);
|
|
415
436
|
let resultArray = [];
|
|
416
437
|
|
|
417
438
|
try {
|
|
418
|
-
|
|
439
|
+
// ✅ 确保 filePath 是目录路径,而不是文件路径
|
|
440
|
+
// 如果传入的是文件路径,获取其所在目录
|
|
441
|
+
let dirPath = filePath;
|
|
442
|
+
|
|
443
|
+
// 首先检查路径是否存在,并确定是文件还是目录
|
|
444
|
+
try {
|
|
445
|
+
const stats = await fs.promises.stat(filePath);
|
|
446
|
+
if (stats.isFile()) {
|
|
447
|
+
// 如果是文件,使用其所在目录
|
|
448
|
+
dirPath = path.dirname(filePath);
|
|
449
|
+
console.log(`[findSubASSpecPath] 检测到文件路径,转换为目录: ${filePath} -> ${dirPath}`);
|
|
450
|
+
} else if (!stats.isDirectory()) {
|
|
451
|
+
// 既不是文件也不是目录,直接返回空数组
|
|
452
|
+
console.log(`[findSubASSpecPath] 路径既不是文件也不是目录,返回空数组: ${filePath}`);
|
|
453
|
+
return resultArray;
|
|
454
|
+
}
|
|
455
|
+
} catch (err) {
|
|
456
|
+
console.log(`[findSubASSpecPath] stat 失败: ${filePath}, 错误: ${err.code || err.message}`);
|
|
457
|
+
// 如果 stat 失败(路径不存在、权限错误等),尝试使用 dirname
|
|
458
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES') {
|
|
459
|
+
// 如果路径看起来像文件路径(包含文件名),尝试使用 dirname
|
|
460
|
+
if (path.basename(filePath) === HOLDER_NAME || path.extname(filePath) !== '') {
|
|
461
|
+
dirPath = path.dirname(filePath);
|
|
462
|
+
console.log(`[findSubASSpecPath] 根据路径特征转换为目录: ${filePath} -> ${dirPath}`);
|
|
463
|
+
} else {
|
|
464
|
+
// 可能是目录路径,但不存在,直接返回空数组
|
|
465
|
+
console.log(`[findSubASSpecPath] 路径不存在,返回空数组: ${filePath}`);
|
|
466
|
+
return resultArray;
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
// 其他错误,直接返回空数组
|
|
470
|
+
console.log(`[findSubASSpecPath] stat 错误,返回空数组: ${filePath}, 错误: ${err.code || err.message}`);
|
|
471
|
+
return resultArray;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// ✅ 再次确认 dirPath 是目录(防止 ENOTDIR 错误)
|
|
476
|
+
console.log(`[findSubASSpecPath] 调用 getDirectoryEntries: ${dirPath}`);
|
|
477
|
+
const entries = await getDirectoryEntries(dirPath);
|
|
419
478
|
if (!entries) {
|
|
420
479
|
return resultArray;
|
|
421
480
|
}
|
|
422
481
|
|
|
423
482
|
for (const entry of entries) {
|
|
424
483
|
if (entry.isFile() && entry.name === HOLDER_NAME) {
|
|
425
|
-
resultArray.push(path.join(
|
|
484
|
+
resultArray.push(path.join(dirPath, entry.name));
|
|
426
485
|
} else if (entry.isDirectory()) {
|
|
427
486
|
// 跳过 node_modules、.git 等目录
|
|
428
487
|
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
|
429
488
|
continue;
|
|
430
489
|
}
|
|
431
490
|
|
|
432
|
-
const array = await findSubASSpecPath(path.join(
|
|
491
|
+
const array = await findSubASSpecPath(path.join(dirPath, entry.name));
|
|
433
492
|
resultArray = resultArray.concat(array);
|
|
434
493
|
}
|
|
435
494
|
}
|
|
436
495
|
} catch (err) {
|
|
437
|
-
|
|
496
|
+
// ✅ 忽略 ENOTDIR 错误(传入的是文件路径)和其他文件系统错误
|
|
497
|
+
if (err.code !== 'ENOTDIR') {
|
|
498
|
+
console.log(err);
|
|
499
|
+
}
|
|
438
500
|
}
|
|
439
501
|
|
|
440
502
|
return resultArray;
|
|
@@ -442,11 +504,34 @@ async function findSubASSpecPath(filePath) {
|
|
|
442
504
|
|
|
443
505
|
/**
|
|
444
506
|
* 向上查找项目根目录(包含 .git、Package.swift 等的目录)
|
|
445
|
-
* @param {string} filePath -
|
|
507
|
+
* @param {string} filePath - 起始文件路径或目录路径
|
|
446
508
|
* @returns {Promise<string|null>} 项目根目录路径,如果找不到返回 null
|
|
447
509
|
*/
|
|
448
510
|
async function findProjectRoot(filePath) {
|
|
511
|
+
console.log(`[findProjectRoot] 开始查找,传入路径: ${filePath}`);
|
|
512
|
+
// ✅ 确保从目录路径开始查找,如果是文件路径,先转换为目录路径
|
|
449
513
|
let currentPath = path.resolve(filePath);
|
|
514
|
+
|
|
515
|
+
// 检查路径是文件还是目录
|
|
516
|
+
try {
|
|
517
|
+
const stats = await fs.promises.stat(currentPath);
|
|
518
|
+
if (stats.isFile()) {
|
|
519
|
+
// 如果是文件,使用其所在目录
|
|
520
|
+
currentPath = path.dirname(currentPath);
|
|
521
|
+
console.log(`[findProjectRoot] 检测到文件路径,转换为目录: ${filePath} -> ${currentPath}`);
|
|
522
|
+
}
|
|
523
|
+
} catch (err) {
|
|
524
|
+
console.log(`[findProjectRoot] stat 失败: ${currentPath}, 错误: ${err.code || err.message}`);
|
|
525
|
+
// 如果 stat 失败,假设是目录路径,或者使用 dirname 作为后备
|
|
526
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES') {
|
|
527
|
+
// 如果路径看起来像文件路径,使用 dirname
|
|
528
|
+
if (path.basename(filePath).includes('.') || path.extname(filePath) !== '') {
|
|
529
|
+
currentPath = path.dirname(currentPath);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
// 其他错误继续处理,可能是目录路径
|
|
533
|
+
}
|
|
534
|
+
|
|
450
535
|
const maxLevels = 20;
|
|
451
536
|
let levelsChecked = 0;
|
|
452
537
|
|
|
@@ -464,7 +549,8 @@ async function findProjectRoot(filePath) {
|
|
|
464
549
|
currentPath = parentPath;
|
|
465
550
|
levelsChecked++;
|
|
466
551
|
} catch (err) {
|
|
467
|
-
|
|
552
|
+
// ✅ 处理各种文件系统错误,包括 ENOTDIR
|
|
553
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES' || err.code === 'ENOTDIR') {
|
|
468
554
|
const parentPath = path.dirname(currentPath);
|
|
469
555
|
if (parentPath === currentPath) {
|
|
470
556
|
break;
|
package/bin/init.js
CHANGED
|
@@ -18,14 +18,19 @@ async function mergeSubSpecs(mainSpecFile) {
|
|
|
18
18
|
|
|
19
19
|
// ✅ 从项目根目录开始查找,而不是从主配置文件所在目录
|
|
20
20
|
// 找到项目根目录(包含 .git、Package.swift 等的目录)
|
|
21
|
+
console.log(`[mergeSubSpecs] 主配置文件路径: ${mainSpecFile}`);
|
|
21
22
|
const projectRoot = await findPath.findProjectRoot(mainSpecFile);
|
|
22
23
|
const searchRoot = projectRoot || path.dirname(mainSpecFile);
|
|
24
|
+
console.log(`[mergeSubSpecs] 项目根目录: ${projectRoot || 'null'}`);
|
|
25
|
+
console.log(`[mergeSubSpecs] 搜索根目录: ${searchRoot}`);
|
|
23
26
|
|
|
24
27
|
const specSlashIndex = mainSpecFile.lastIndexOf('/');
|
|
25
28
|
const specFilePath = mainSpecFile.substring(0, specSlashIndex + 1);
|
|
26
29
|
|
|
27
30
|
// ✅ 从项目根目录向下查找子模块的 AutoSnippet.boxspec.json
|
|
31
|
+
console.log(`[mergeSubSpecs] 开始查找子模块配置,搜索根目录: ${searchRoot}`);
|
|
28
32
|
const array = await findPath.findSubASSpecPath(searchRoot);
|
|
33
|
+
console.log(`[mergeSubSpecs] 找到 ${array.length} 个子模块配置文件`);
|
|
29
34
|
|
|
30
35
|
for (let i = 0; i < array.length; i++) {
|
|
31
36
|
const filename = array[i];
|