autosnippet 1.1.19 → 1.1.21
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/asnip.js +45 -4
- package/bin/create.js +139 -27
- package/bin/findPath.js +48 -8
- package/bin/init.js +82 -16
- package/bin/watch.js +5 -4
- package/package.json +1 -1
package/bin/asnip.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
4
5
|
// 读取输入命令
|
|
5
6
|
const inquirer = require('inquirer');
|
|
6
7
|
// 命令行工具
|
|
@@ -219,11 +220,51 @@ commander
|
|
|
219
220
|
commander
|
|
220
221
|
.command('w')
|
|
221
222
|
.description('recognize that Snippet automatically injects dependency header files')
|
|
223
|
+
.action(async () => {
|
|
224
|
+
// ✅ 从执行位置向上查找 AutoSnippetRoot.boxspec.json,找到根目录
|
|
225
|
+
const projectRoot = await findPath.findProjectRoot(CMD_PATH);
|
|
226
|
+
|
|
227
|
+
if (!projectRoot) {
|
|
228
|
+
console.error('未找到项目根目录(AutoSnippetRoot.boxspec.json)。');
|
|
229
|
+
console.error('请先使用 asd root 命令在项目根目录创建根目录标记文件。');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log(`[asd w] 项目根目录: ${projectRoot}`);
|
|
234
|
+
|
|
235
|
+
// ✅ 使用根目录的 AutoSnippetRoot.boxspec.json 作为配置文件
|
|
236
|
+
const rootSpecFile = path.join(projectRoot, findPath.ROOT_MARKER_NAME);
|
|
237
|
+
console.log(`[asd w] 使用配置文件: ${rootSpecFile}`);
|
|
238
|
+
|
|
239
|
+
// 先安装 snippets
|
|
240
|
+
install.addCodeSnippets(rootSpecFile);
|
|
241
|
+
// 在根目录启动监听
|
|
242
|
+
watch.watchFileChange(rootSpecFile, projectRoot);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
commander
|
|
246
|
+
.command('root')
|
|
247
|
+
.description('mark current directory as project root by creating AutoSnippetRoot.boxspec.json')
|
|
222
248
|
.action(() => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
249
|
+
const rootMarkerPath = path.join(CMD_PATH, 'AutoSnippetRoot.boxspec.json');
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
// 检查文件是否已存在
|
|
253
|
+
fs.accessSync(rootMarkerPath, fs.constants.F_OK);
|
|
254
|
+
console.log(`根目录标记文件已存在: ${rootMarkerPath}`);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
// 文件不存在,创建它
|
|
257
|
+
try {
|
|
258
|
+
// 创建一个空的标记文件
|
|
259
|
+
fs.writeFileSync(rootMarkerPath, JSON.stringify({
|
|
260
|
+
root: true,
|
|
261
|
+
description: 'This file marks the project root directory for AutoSnippet'
|
|
262
|
+
}, null, 2), 'utf8');
|
|
263
|
+
console.log(`已创建根目录标记文件: ${rootMarkerPath}`);
|
|
264
|
+
} catch (writeErr) {
|
|
265
|
+
console.error(`创建根目录标记文件失败: ${writeErr.message}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
227
268
|
});
|
|
228
269
|
|
|
229
270
|
commander.parse(process.argv);
|
package/bin/create.js
CHANGED
|
@@ -41,6 +41,51 @@ function determineModuleName(filePath, packageInfo) {
|
|
|
41
41
|
return packageInfo.name;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* 从文件路径确定 target 的根目录(包含 Code 或 Sources 的目录)
|
|
46
|
+
* @param {string} filePath - 文件路径
|
|
47
|
+
* @returns {Promise<string|null>} target 根目录路径,如果找不到返回 null
|
|
48
|
+
*/
|
|
49
|
+
async function findTargetRootDir(filePath) {
|
|
50
|
+
const fs = require('fs');
|
|
51
|
+
let currentPath = path.dirname(path.resolve(filePath));
|
|
52
|
+
const maxLevels = 10;
|
|
53
|
+
let levelsChecked = 0;
|
|
54
|
+
|
|
55
|
+
// ✅ 向上查找包含 Code 或 Sources 目录的目录(target 根目录)
|
|
56
|
+
// 例如:BDNetworkAPI/Code/xxx.m -> BDNetworkAPI/
|
|
57
|
+
while (currentPath && levelsChecked < maxLevels) {
|
|
58
|
+
try {
|
|
59
|
+
const entries = await fs.promises.readdir(currentPath, { withFileTypes: true });
|
|
60
|
+
|
|
61
|
+
// 检查当前目录是否包含 Code 或 Sources 目录
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
if (entry.isDirectory() && (entry.name === 'Code' || entry.name === 'Sources')) {
|
|
64
|
+
// 找到包含 Code 或 Sources 的目录,这就是 target 根目录
|
|
65
|
+
console.log(`[findTargetRootDir] 找到 target 根目录: ${currentPath} (包含 ${entry.name})`);
|
|
66
|
+
return currentPath;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 继续向上查找
|
|
71
|
+
const parentPath = path.dirname(currentPath);
|
|
72
|
+
if (parentPath === currentPath) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
currentPath = parentPath;
|
|
76
|
+
levelsChecked++;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES') {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log(`[findTargetRootDir] 未找到 target 根目录(Code 或 Sources)`);
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
44
89
|
function updateCodeSnippets(specFile, word, key, value) {
|
|
45
90
|
if (key && key !== 'title' && key !== 'link' && key !== 'summary') {
|
|
46
91
|
console.log('此项属性不存在或不可修改。');
|
|
@@ -224,7 +269,7 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
|
|
|
224
269
|
}
|
|
225
270
|
});
|
|
226
271
|
|
|
227
|
-
rl.on('close', function () {
|
|
272
|
+
rl.on('close', async function () {
|
|
228
273
|
if (codeList.length > 1) {
|
|
229
274
|
codeList.pop();
|
|
230
275
|
|
|
@@ -243,9 +288,14 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
|
|
|
243
288
|
if (!packagePath) {
|
|
244
289
|
console.log('未找到 Package.swift 文件,请检查路径。');
|
|
245
290
|
snippet['{content}'] = codeList;
|
|
246
|
-
// ✅
|
|
247
|
-
|
|
248
|
-
|
|
291
|
+
// ✅ 查找 target 根目录(Code 或 Sources 的父目录)
|
|
292
|
+
let targetRootDir = await findTargetRootDir(filePath);
|
|
293
|
+
if (!targetRootDir) {
|
|
294
|
+
// 如果找不到,使用文件所在目录的父目录作为后备
|
|
295
|
+
targetRootDir = path.dirname(path.dirname(filePath));
|
|
296
|
+
}
|
|
297
|
+
const moduleSpecFile = path.join(targetRootDir, 'AutoSnippet.boxspec.json');
|
|
298
|
+
await saveFromFile(moduleSpecFile, snippet);
|
|
249
299
|
return;
|
|
250
300
|
}
|
|
251
301
|
|
|
@@ -253,8 +303,14 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
|
|
|
253
303
|
const packageInfo = await findPath.parsePackageSwift(packagePath);
|
|
254
304
|
if (!packageInfo) {
|
|
255
305
|
snippet['{content}'] = codeList;
|
|
256
|
-
|
|
257
|
-
|
|
306
|
+
// ✅ 查找 target 根目录(Code 或 Sources 的父目录)
|
|
307
|
+
let targetRootDir = await findTargetRootDir(filePath);
|
|
308
|
+
if (!targetRootDir) {
|
|
309
|
+
// 如果找不到,使用 Package.swift 所在目录作为后备
|
|
310
|
+
targetRootDir = path.dirname(packagePath);
|
|
311
|
+
}
|
|
312
|
+
const moduleSpecFile = path.join(targetRootDir, 'AutoSnippet.boxspec.json');
|
|
313
|
+
await saveFromFile(moduleSpecFile, snippet);
|
|
258
314
|
return;
|
|
259
315
|
}
|
|
260
316
|
|
|
@@ -262,50 +318,54 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
|
|
|
262
318
|
const moduleName = determineModuleName(filePath, packageInfo);
|
|
263
319
|
const headerNameWithoutExt = fileName.substring(0, fileName.length - 2); // 移除 .h
|
|
264
320
|
|
|
265
|
-
// ✅
|
|
266
|
-
|
|
267
|
-
|
|
321
|
+
// ✅ 查找 target 根目录(Code 或 Sources 的父目录)
|
|
322
|
+
let targetRootDir = await findTargetRootDir(filePath);
|
|
323
|
+
if (!targetRootDir) {
|
|
324
|
+
// 如果找不到 target 根目录,使用 Package.swift 所在目录作为后备
|
|
325
|
+
targetRootDir = packageInfo.path;
|
|
326
|
+
}
|
|
327
|
+
console.log(`[createCodeSnippets] target 根目录: ${targetRootDir}`);
|
|
328
|
+
const moduleRoot = targetRootDir;
|
|
268
329
|
|
|
269
330
|
// ✅ 查找头文件(适配 SPM 的 include/ModuleName/ 结构)
|
|
270
|
-
// 在
|
|
271
|
-
const headerPath = await findPath.findSubHeaderPath(
|
|
331
|
+
// 在 target 根目录下查找(可能包含 Code/ 或 Sources/ 这样的结构)
|
|
332
|
+
const headerPath = await findPath.findSubHeaderPath(targetRootDir, headerNameWithoutExt, moduleName);
|
|
272
333
|
|
|
273
334
|
snippet['{content}'] = codeList;
|
|
274
335
|
snippet['{specName}'] = moduleName; // ✅ specName 是 target 名称(如 BDNetworkAPI),不是包名(如 Business)
|
|
275
336
|
|
|
276
337
|
if (headerPath) {
|
|
277
|
-
// ✅ headName 存储相对于
|
|
278
|
-
//
|
|
279
|
-
// 则 headName = "
|
|
280
|
-
const headerRelativePath = path.relative(
|
|
338
|
+
// ✅ headName 存储相对于 target 根目录的相对路径
|
|
339
|
+
// 例如:target 根目录在 BDNetworkAPI/,头文件在 BDNetworkAPI/Code/xxx.h
|
|
340
|
+
// 则 headName = "Code/xxx.h"
|
|
341
|
+
const headerRelativePath = path.relative(targetRootDir, headerPath);
|
|
281
342
|
snippet['{headName}'] = headerRelativePath;
|
|
282
343
|
} else {
|
|
283
344
|
// 如果找不到头文件,使用文件名
|
|
284
345
|
snippet['{headName}'] = fileName;
|
|
285
346
|
}
|
|
286
347
|
|
|
287
|
-
// 查找 README.md
|
|
348
|
+
// 查找 README.md(在 target 根目录)
|
|
288
349
|
try {
|
|
289
|
-
const readmePath = path.join(
|
|
350
|
+
const readmePath = path.join(targetRootDir, README_NAME);
|
|
290
351
|
await fs.promises.access(readmePath);
|
|
291
|
-
const
|
|
292
|
-
const readmeRelativePath = path.relative(moduleRoot, readmePath);
|
|
352
|
+
const readmeRelativePath = path.relative(targetRootDir, readmePath);
|
|
293
353
|
snippet['{readme}'] = encodeURI(readmeRelativePath);
|
|
294
354
|
} catch {
|
|
295
355
|
// README.md 不存在,跳过
|
|
296
356
|
}
|
|
297
357
|
|
|
298
|
-
// ✅ SPM 模块:.boxspec
|
|
299
|
-
const moduleSpecFile = path.join(
|
|
300
|
-
saveFromFile(moduleSpecFile, snippet);
|
|
301
|
-
}).catch(function (err) {
|
|
358
|
+
// ✅ SPM 模块:.boxspec 文件位置在 target 根目录(Code 或 Sources 的父目录)
|
|
359
|
+
const moduleSpecFile = path.join(targetRootDir, 'AutoSnippet.boxspec.json');
|
|
360
|
+
await saveFromFile(moduleSpecFile, snippet);
|
|
361
|
+
}).catch(async function (err) {
|
|
302
362
|
console.error('Error finding Package.swift:', err);
|
|
303
363
|
snippet['{content}'] = codeList;
|
|
304
|
-
saveFromFile(specFile, snippet);
|
|
364
|
+
await saveFromFile(specFile, snippet);
|
|
305
365
|
});
|
|
306
366
|
} else {
|
|
307
367
|
snippet['{content}'] = codeList;
|
|
308
|
-
saveFromFile(specFile, snippet);
|
|
368
|
+
await saveFromFile(specFile, snippet);
|
|
309
369
|
}
|
|
310
370
|
// 移除ACode标识
|
|
311
371
|
removeAcodeMark(filePath, positionList);
|
|
@@ -315,7 +375,8 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
|
|
|
315
375
|
});
|
|
316
376
|
}
|
|
317
377
|
|
|
318
|
-
function saveFromFile(specFile, snippet) {
|
|
378
|
+
async function saveFromFile(specFile, snippet) {
|
|
379
|
+
const findPath = require('./findPath.js');
|
|
319
380
|
let placeholder = null;
|
|
320
381
|
|
|
321
382
|
try {
|
|
@@ -370,7 +431,58 @@ function saveFromFile(specFile, snippet) {
|
|
|
370
431
|
console.log(err);
|
|
371
432
|
}
|
|
372
433
|
cache.updateCache(specFile, content);
|
|
373
|
-
|
|
434
|
+
|
|
435
|
+
// ✅ 同步到根目录的 AutoSnippetRoot.boxspec.json
|
|
436
|
+
try {
|
|
437
|
+
const rootSpecFile = await findPath.getRootSpecFilePath(specFile);
|
|
438
|
+
if (rootSpecFile) {
|
|
439
|
+
// 读取根配置文件
|
|
440
|
+
let rootPlaceholder = null;
|
|
441
|
+
try {
|
|
442
|
+
const rootData = fs.readFileSync(rootSpecFile, 'utf8');
|
|
443
|
+
if (rootData) {
|
|
444
|
+
rootPlaceholder = JSON.parse(rootData);
|
|
445
|
+
}
|
|
446
|
+
} catch (err) {
|
|
447
|
+
if (err.code === 'ENOENT') {
|
|
448
|
+
rootPlaceholder = { list: [] };
|
|
449
|
+
} else {
|
|
450
|
+
console.error(`[saveFromFile] 读取根配置文件失败: ${err.message}`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (rootPlaceholder != null) {
|
|
455
|
+
// 检查是否已存在相同的 identifier
|
|
456
|
+
let exists = false;
|
|
457
|
+
if (rootPlaceholder.list) {
|
|
458
|
+
for (let i = 0; i < rootPlaceholder.list.length; i++) {
|
|
459
|
+
if (rootPlaceholder.list[i]['{identifier}'] === snippet['{identifier}']) {
|
|
460
|
+
// 已存在,更新它
|
|
461
|
+
rootPlaceholder.list[i] = snippet;
|
|
462
|
+
exists = true;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
rootPlaceholder.list = [];
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// 如果不存在,添加到列表
|
|
471
|
+
if (!exists) {
|
|
472
|
+
rootPlaceholder.list.push(snippet);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// 写入根配置文件
|
|
476
|
+
const rootContent = JSON.stringify(rootPlaceholder, null, 4);
|
|
477
|
+
fs.writeFileSync(rootSpecFile, rootContent, 'utf8');
|
|
478
|
+
console.log(`[saveFromFile] 已同步 snippet 到根目录配置文件: ${rootSpecFile}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
} catch (err) {
|
|
482
|
+
console.error(`[saveFromFile] 同步到根配置文件失败: ${err.message}`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// ✅ 只写入刚创建的单个代码片段
|
|
374
486
|
install.addCodeSnippets(specFile, snippet);
|
|
375
487
|
}
|
|
376
488
|
}
|
package/bin/findPath.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
// 全局常量
|
|
6
6
|
const HOLDER_NAME = 'AutoSnippet.boxspec.json';
|
|
7
|
+
const ROOT_MARKER_NAME = 'AutoSnippetRoot.boxspec.json'; // 项目根目录标记文件
|
|
7
8
|
const PACKAGE_SWIFT = 'Package.swift';
|
|
8
9
|
const README_NAME = 'readme.md';
|
|
9
10
|
|
|
@@ -503,13 +504,13 @@ async function findSubASSpecPath(filePath) {
|
|
|
503
504
|
}
|
|
504
505
|
|
|
505
506
|
/**
|
|
506
|
-
*
|
|
507
|
+
* 向上查找项目根目录(查找 AutoSnippetRoot.boxspec.json 文件)
|
|
507
508
|
* @param {string} filePath - 起始文件路径或目录路径
|
|
508
|
-
* @returns {Promise<string|null>}
|
|
509
|
+
* @returns {Promise<string|null>} 项目根目录路径(AutoSnippetRoot.boxspec.json 所在目录),如果找不到返回 null
|
|
509
510
|
*/
|
|
510
511
|
async function findProjectRoot(filePath) {
|
|
511
512
|
console.log(`[findProjectRoot] 开始查找,传入路径: ${filePath}`);
|
|
512
|
-
// ✅
|
|
513
|
+
// ✅ 简化逻辑:向上查找 AutoSnippetRoot.boxspec.json 文件
|
|
513
514
|
let currentPath = path.resolve(filePath);
|
|
514
515
|
|
|
515
516
|
// 检查路径是文件还是目录
|
|
@@ -537,9 +538,16 @@ async function findProjectRoot(filePath) {
|
|
|
537
538
|
|
|
538
539
|
while (currentPath && levelsChecked < maxLevels) {
|
|
539
540
|
try {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
541
|
+
// ✅ 查找 AutoSnippetRoot.boxspec.json 文件
|
|
542
|
+
const rootMarkerPath = path.join(currentPath, ROOT_MARKER_NAME);
|
|
543
|
+
try {
|
|
544
|
+
const stats = await fs.promises.stat(rootMarkerPath);
|
|
545
|
+
if (stats.isFile()) {
|
|
546
|
+
console.log(`[findProjectRoot] 找到根目录标记文件: ${rootMarkerPath}`);
|
|
547
|
+
return currentPath;
|
|
548
|
+
}
|
|
549
|
+
} catch (err) {
|
|
550
|
+
// 文件不存在,继续向上查找
|
|
543
551
|
}
|
|
544
552
|
|
|
545
553
|
const parentPath = path.dirname(currentPath);
|
|
@@ -549,7 +557,7 @@ async function findProjectRoot(filePath) {
|
|
|
549
557
|
currentPath = parentPath;
|
|
550
558
|
levelsChecked++;
|
|
551
559
|
} catch (err) {
|
|
552
|
-
// ✅
|
|
560
|
+
// ✅ 处理各种文件系统错误
|
|
553
561
|
if (err.code === 'ENOENT' || err.code === 'EACCES' || err.code === 'ENOTDIR') {
|
|
554
562
|
const parentPath = path.dirname(currentPath);
|
|
555
563
|
if (parentPath === currentPath) {
|
|
@@ -563,6 +571,7 @@ async function findProjectRoot(filePath) {
|
|
|
563
571
|
}
|
|
564
572
|
}
|
|
565
573
|
|
|
574
|
+
console.log(`[findProjectRoot] 未找到根目录标记文件 ${ROOT_MARKER_NAME}`);
|
|
566
575
|
return null;
|
|
567
576
|
}
|
|
568
577
|
|
|
@@ -661,6 +670,35 @@ async function findModuleDirectory(dirPath, moduleName, headerFileName) {
|
|
|
661
670
|
return null;
|
|
662
671
|
}
|
|
663
672
|
|
|
673
|
+
/**
|
|
674
|
+
* 获取根目录的 AutoSnippetRoot.boxspec.json 文件路径
|
|
675
|
+
* @param {string} filePath - 起始文件路径或目录路径
|
|
676
|
+
* @returns {Promise<string|null>} 根目录的 AutoSnippetRoot.boxspec.json 文件路径,如果找不到返回 null
|
|
677
|
+
*/
|
|
678
|
+
async function getRootSpecFilePath(filePath) {
|
|
679
|
+
const projectRoot = await findProjectRoot(filePath);
|
|
680
|
+
if (!projectRoot) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
const rootSpecFile = path.join(projectRoot, ROOT_MARKER_NAME);
|
|
684
|
+
|
|
685
|
+
// 检查文件是否存在,如果不存在则创建
|
|
686
|
+
try {
|
|
687
|
+
await fs.promises.access(rootSpecFile);
|
|
688
|
+
} catch (err) {
|
|
689
|
+
if (err.code === 'ENOENT') {
|
|
690
|
+
// 文件不存在,创建它
|
|
691
|
+
const specObj = {
|
|
692
|
+
list: []
|
|
693
|
+
};
|
|
694
|
+
const content = JSON.stringify(specObj, null, 4);
|
|
695
|
+
fs.writeFileSync(rootSpecFile, content, 'utf8');
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return rootSpecFile;
|
|
700
|
+
}
|
|
701
|
+
|
|
664
702
|
exports.findASSpecPath = findASSpecPath;
|
|
665
703
|
exports.findASSpecPathAsync = findASSpecPathAsync;
|
|
666
704
|
exports.findPackageSwiftPath = findPackageSwiftPath;
|
|
@@ -668,4 +706,6 @@ exports.parsePackageSwift = parsePackageSwift;
|
|
|
668
706
|
exports.findSubHeaderPath = findSubHeaderPath;
|
|
669
707
|
exports.findSubASSpecPath = findSubASSpecPath;
|
|
670
708
|
exports.findProjectRoot = findProjectRoot;
|
|
671
|
-
exports.findModuleHeaderFromRoot = findModuleHeaderFromRoot;
|
|
709
|
+
exports.findModuleHeaderFromRoot = findModuleHeaderFromRoot;
|
|
710
|
+
exports.getRootSpecFilePath = getRootSpecFilePath;
|
|
711
|
+
exports.ROOT_MARKER_NAME = ROOT_MARKER_NAME;
|
package/bin/init.js
CHANGED
|
@@ -16,16 +16,22 @@ async function mergeSubSpecs(mainSpecFile) {
|
|
|
16
16
|
list: []
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
// ✅
|
|
20
|
-
// 找到项目根目录(包含 .git、Package.swift 等的目录)
|
|
19
|
+
// ✅ 找到项目根目录的 AutoSnippetRoot.boxspec.json 文件
|
|
21
20
|
console.log(`[mergeSubSpecs] 主配置文件路径: ${mainSpecFile}`);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const rootSpecFile = await findPath.getRootSpecFilePath(mainSpecFile);
|
|
22
|
+
if (!rootSpecFile) {
|
|
23
|
+
console.error(`[mergeSubSpecs] 未找到项目根目录,无法聚合配置`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
console.log(`[mergeSubSpecs] 根目录配置文件: ${rootSpecFile}`);
|
|
27
|
+
|
|
28
|
+
const projectRoot = path.dirname(rootSpecFile);
|
|
29
|
+
const searchRoot = projectRoot;
|
|
30
|
+
console.log(`[mergeSubSpecs] 项目根目录: ${projectRoot}`);
|
|
25
31
|
console.log(`[mergeSubSpecs] 搜索根目录: ${searchRoot}`);
|
|
26
32
|
|
|
27
|
-
const specSlashIndex =
|
|
28
|
-
const specFilePath =
|
|
33
|
+
const specSlashIndex = rootSpecFile.lastIndexOf('/');
|
|
34
|
+
const specFilePath = rootSpecFile.substring(0, specSlashIndex + 1);
|
|
29
35
|
|
|
30
36
|
// ✅ 从项目根目录向下查找子模块的 AutoSnippet.boxspec.json
|
|
31
37
|
console.log(`[mergeSubSpecs] 开始查找子模块配置,搜索根目录: ${searchRoot}`);
|
|
@@ -37,8 +43,9 @@ async function mergeSubSpecs(mainSpecFile) {
|
|
|
37
43
|
|
|
38
44
|
const slashIndex = filename.lastIndexOf('/');
|
|
39
45
|
let thePath = filename.substring(0, slashIndex + 1);
|
|
40
|
-
if (filename ===
|
|
41
|
-
|
|
46
|
+
if (filename === rootSpecFile) {
|
|
47
|
+
// 跳过根目录的 AutoSnippetRoot.boxspec.json,避免循环引用
|
|
48
|
+
continue;
|
|
42
49
|
} else {
|
|
43
50
|
thePath = thePath.replace(specFilePath, '');
|
|
44
51
|
}
|
|
@@ -69,33 +76,92 @@ async function mergeSubSpecs(mainSpecFile) {
|
|
|
69
76
|
try {
|
|
70
77
|
const content = JSON.stringify(specObj, null, 4);
|
|
71
78
|
if (content) {
|
|
72
|
-
|
|
79
|
+
// ✅ 写入根目录的 AutoSnippetRoot.boxspec.json
|
|
80
|
+
fs.writeFileSync(rootSpecFile, content, 'utf8');
|
|
81
|
+
console.log(`[mergeSubSpecs] 已聚合 ${specObj.list.length} 个 snippet 到根目录配置文件`);
|
|
73
82
|
}
|
|
74
83
|
} catch (err) {
|
|
75
84
|
console.error(err);
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
/**
|
|
89
|
+
* 从目录路径确定 target 的根目录(包含 Code 或 Sources 的目录)
|
|
90
|
+
* @param {string} dirPath - 目录路径
|
|
91
|
+
* @returns {Promise<string|null>} target 根目录路径,如果找不到返回 null
|
|
92
|
+
*/
|
|
93
|
+
async function findTargetRootDirFromPath(dirPath) {
|
|
94
|
+
let currentPath = path.resolve(dirPath);
|
|
95
|
+
const maxLevels = 10;
|
|
96
|
+
let levelsChecked = 0;
|
|
97
|
+
|
|
98
|
+
// ✅ 向上查找包含 Code 或 Sources 目录的目录(target 根目录)
|
|
99
|
+
while (currentPath && levelsChecked < maxLevels) {
|
|
100
|
+
try {
|
|
101
|
+
const entries = await fs.promises.readdir(currentPath, { withFileTypes: true });
|
|
102
|
+
|
|
103
|
+
// 检查当前目录是否包含 Code 或 Sources 目录
|
|
104
|
+
for (const entry of entries) {
|
|
105
|
+
if (entry.isDirectory() && (entry.name === 'Code' || entry.name === 'Sources')) {
|
|
106
|
+
// 找到包含 Code 或 Sources 的目录,这就是 target 根目录
|
|
107
|
+
console.log(`[findTargetRootDirFromPath] 找到 target 根目录: ${currentPath} (包含 ${entry.name})`);
|
|
108
|
+
return currentPath;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 继续向上查找
|
|
113
|
+
const parentPath = path.dirname(currentPath);
|
|
114
|
+
if (parentPath === currentPath) {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
currentPath = parentPath;
|
|
118
|
+
levelsChecked++;
|
|
119
|
+
} catch (err) {
|
|
120
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES') {
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log(`[findTargetRootDirFromPath] 未找到 target 根目录(Code 或 Sources)`);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
79
131
|
async function initSpec() {
|
|
80
|
-
// ✅ 查找
|
|
81
|
-
let
|
|
132
|
+
// ✅ 查找 target 根目录(包含 Code 或 Sources 的目录),在该目录创建 AutoSnippet.boxspec.json
|
|
133
|
+
let targetRootDir = await findTargetRootDirFromPath(CMD_PATH);
|
|
134
|
+
|
|
135
|
+
// 如果找不到 target 根目录,尝试查找 Package.swift 作为后备
|
|
136
|
+
if (!targetRootDir) {
|
|
137
|
+
let packagePath = await findPath.findPackageSwiftPath(CMD_PATH);
|
|
138
|
+
if (packagePath) {
|
|
139
|
+
targetRootDir = path.dirname(packagePath);
|
|
140
|
+
console.log(`[initSpec] 使用 Package.swift 所在目录作为 target 根目录: ${targetRootDir}`);
|
|
141
|
+
} else {
|
|
142
|
+
// 如果都找不到,使用当前目录
|
|
143
|
+
targetRootDir = CMD_PATH;
|
|
144
|
+
console.log(`[initSpec] 使用当前目录作为 target 根目录: ${targetRootDir}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
82
147
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const filePath = path.join(configDir, 'AutoSnippet.boxspec.json');
|
|
148
|
+
const filePath = path.join(targetRootDir, 'AutoSnippet.boxspec.json');
|
|
149
|
+
console.log(`[initSpec] 将在 target 根目录创建配置文件: ${filePath}`);
|
|
86
150
|
|
|
87
151
|
// 如果配置文件不存在,创建一个空的
|
|
88
152
|
try {
|
|
89
153
|
await fs.promises.access(filePath);
|
|
154
|
+
console.log(`[initSpec] 配置文件已存在: ${filePath}`);
|
|
90
155
|
} catch (error) {
|
|
91
156
|
const specObj = {
|
|
92
157
|
list: []
|
|
93
158
|
};
|
|
94
159
|
const content = JSON.stringify(specObj, null, 4);
|
|
95
160
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
161
|
+
console.log(`[initSpec] 已创建配置文件: ${filePath}`);
|
|
96
162
|
}
|
|
97
163
|
|
|
98
|
-
// ✅
|
|
164
|
+
// ✅ 聚合子模块配置到根目录的 AutoSnippetRoot.boxspec.json
|
|
99
165
|
await mergeSubSpecs(filePath);
|
|
100
166
|
}
|
|
101
167
|
|
package/bin/watch.js
CHANGED
|
@@ -22,10 +22,11 @@ const importSwiftReg = /^import\s*\w+$/;
|
|
|
22
22
|
let timeoutLink = null;
|
|
23
23
|
let timeoutHead = null;
|
|
24
24
|
|
|
25
|
-
function watchFileChange(specFile) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
function watchFileChange(specFile, watchRootPath) {
|
|
26
|
+
// ✅ 如果指定了监听根目录,使用它;否则使用当前工作目录
|
|
27
|
+
const filePath = watchRootPath || CMD_PATH;
|
|
28
|
+
console.log(`[watchFileChange] 监听目录: ${filePath}`);
|
|
29
|
+
console.log(`[watchFileChange] 配置文件: ${specFile}`);
|
|
29
30
|
let isReading = false;
|
|
30
31
|
|
|
31
32
|
fs.watch(filePath, {recursive: true}, (event, filename) => {
|