autosnippet 1.1.16 → 1.1.18
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 +11 -4
- package/bin/findPath.js +137 -0
- package/bin/init.js +59 -34
- package/bin/install.js +8 -2
- package/package.json +1 -1
package/bin/asnip.js
CHANGED
|
@@ -176,10 +176,17 @@ commander
|
|
|
176
176
|
commander
|
|
177
177
|
.command('i')
|
|
178
178
|
.description('add the shared Snippet to the Xcode environment')
|
|
179
|
-
.action(() => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
.action(async () => {
|
|
180
|
+
// ✅ 使用异步版本查找配置文件
|
|
181
|
+
const specFile = await findPath.findASSpecPathAsync(CMD_PATH);
|
|
182
|
+
if (!specFile) {
|
|
183
|
+
console.error('未找到 AutoSnippet.boxspec.json 配置文件');
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// ✅ 先聚合子模块配置到主配置文件
|
|
187
|
+
await init.mergeSubSpecs(specFile);
|
|
188
|
+
// 然后安装 snippets
|
|
189
|
+
install.addCodeSnippets(specFile);
|
|
183
190
|
});
|
|
184
191
|
|
|
185
192
|
commander
|
package/bin/findPath.js
CHANGED
|
@@ -440,9 +440,146 @@ async function findSubASSpecPath(filePath) {
|
|
|
440
440
|
return resultArray;
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
+
/**
|
|
444
|
+
* 向上查找项目根目录(包含 .git、Package.swift 等的目录)
|
|
445
|
+
* @param {string} filePath - 起始文件路径
|
|
446
|
+
* @returns {Promise<string|null>} 项目根目录路径,如果找不到返回 null
|
|
447
|
+
*/
|
|
448
|
+
async function findProjectRoot(filePath) {
|
|
449
|
+
let currentPath = path.resolve(filePath);
|
|
450
|
+
const maxLevels = 20;
|
|
451
|
+
let levelsChecked = 0;
|
|
452
|
+
|
|
453
|
+
while (currentPath && levelsChecked < maxLevels) {
|
|
454
|
+
try {
|
|
455
|
+
const entries = await getDirectoryEntries(currentPath);
|
|
456
|
+
if (entries && isProjectRoot(currentPath, entries)) {
|
|
457
|
+
return currentPath;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const parentPath = path.dirname(currentPath);
|
|
461
|
+
if (parentPath === currentPath) {
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
currentPath = parentPath;
|
|
465
|
+
levelsChecked++;
|
|
466
|
+
} catch (err) {
|
|
467
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES') {
|
|
468
|
+
const parentPath = path.dirname(currentPath);
|
|
469
|
+
if (parentPath === currentPath) {
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
currentPath = parentPath;
|
|
473
|
+
levelsChecked++;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
throw err;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* 从项目根目录找到模块,并返回模块名和头文件名
|
|
485
|
+
* @param {string} projectRoot - 项目根目录
|
|
486
|
+
* @param {string} specName - 模块名(target 名称)
|
|
487
|
+
* @param {string} headName - 头文件相对路径(相对于模块根目录)
|
|
488
|
+
* @returns {Promise<{moduleName: string, headerFileName: string}|null>}
|
|
489
|
+
*/
|
|
490
|
+
async function findModuleHeaderFromRoot(projectRoot, specName, headName) {
|
|
491
|
+
if (!projectRoot || !specName || !headName) {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// 从项目根目录向下查找包含 specName 的目录(可能是模块目录)
|
|
496
|
+
// 例如:BDVideoPlayer、UI/BDVideoPlayer 等
|
|
497
|
+
const headerFileName = path.basename(headName);
|
|
498
|
+
|
|
499
|
+
// 尝试在项目根目录下查找模块目录
|
|
500
|
+
// 1. 直接查找 specName 目录
|
|
501
|
+
const directModulePath = path.join(projectRoot, specName);
|
|
502
|
+
try {
|
|
503
|
+
const entries = await getDirectoryEntries(directModulePath);
|
|
504
|
+
if (entries) {
|
|
505
|
+
// 检查是否有 include/ModuleName/ 目录
|
|
506
|
+
const includePath = path.join(directModulePath, 'include', specName, headerFileName);
|
|
507
|
+
try {
|
|
508
|
+
await fs.promises.access(includePath);
|
|
509
|
+
return {
|
|
510
|
+
moduleName: specName,
|
|
511
|
+
headerFileName: headerFileName
|
|
512
|
+
};
|
|
513
|
+
} catch (err) {
|
|
514
|
+
// 继续查找
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
} catch (err) {
|
|
518
|
+
// 继续查找
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// 2. 递归查找包含 specName 的目录
|
|
522
|
+
const foundModule = await findModuleDirectory(projectRoot, specName, headerFileName);
|
|
523
|
+
if (foundModule) {
|
|
524
|
+
return foundModule;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// 如果找不到,返回默认值(使用 specName 和 headerFileName)
|
|
528
|
+
return {
|
|
529
|
+
moduleName: specName,
|
|
530
|
+
headerFileName: headerFileName
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* 递归查找模块目录
|
|
536
|
+
*/
|
|
537
|
+
async function findModuleDirectory(dirPath, moduleName, headerFileName) {
|
|
538
|
+
try {
|
|
539
|
+
const entries = await getDirectoryEntries(dirPath);
|
|
540
|
+
if (!entries) {
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
for (const entry of entries) {
|
|
545
|
+
if (entry.isDirectory()) {
|
|
546
|
+
const dirName = entry.name;
|
|
547
|
+
// 跳过隐藏目录和常见目录
|
|
548
|
+
if (dirName.startsWith('.') || dirName === 'node_modules') {
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const fullPath = path.join(dirPath, dirName);
|
|
553
|
+
|
|
554
|
+
// 检查是否是模块目录(包含 include/ModuleName/ 结构)
|
|
555
|
+
const includePath = path.join(fullPath, 'include', moduleName, headerFileName);
|
|
556
|
+
try {
|
|
557
|
+
await fs.promises.access(includePath);
|
|
558
|
+
return {
|
|
559
|
+
moduleName: moduleName,
|
|
560
|
+
headerFileName: headerFileName
|
|
561
|
+
};
|
|
562
|
+
} catch (err) {
|
|
563
|
+
// 继续递归查找
|
|
564
|
+
const found = await findModuleDirectory(fullPath, moduleName, headerFileName);
|
|
565
|
+
if (found) {
|
|
566
|
+
return found;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
} catch (err) {
|
|
572
|
+
// 忽略错误
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
|
|
443
578
|
exports.findASSpecPath = findASSpecPath;
|
|
444
579
|
exports.findASSpecPathAsync = findASSpecPathAsync;
|
|
445
580
|
exports.findPackageSwiftPath = findPackageSwiftPath;
|
|
446
581
|
exports.parsePackageSwift = parsePackageSwift;
|
|
447
582
|
exports.findSubHeaderPath = findSubHeaderPath;
|
|
448
583
|
exports.findSubASSpecPath = findSubASSpecPath;
|
|
584
|
+
exports.findProjectRoot = findProjectRoot;
|
|
585
|
+
exports.findModuleHeaderFromRoot = findModuleHeaderFromRoot;
|
package/bin/init.js
CHANGED
|
@@ -5,31 +5,34 @@ const path = require('path');
|
|
|
5
5
|
const CMD_PATH = process.cwd();
|
|
6
6
|
const findPath = require('./findPath.js');
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* 聚合子模块的 AutoSnippet.boxspec.json 到主配置文件
|
|
10
|
+
* @param {string} mainSpecFile - 主配置文件路径
|
|
11
|
+
* @returns {Promise<void>}
|
|
12
|
+
*/
|
|
13
|
+
async function mergeSubSpecs(mainSpecFile) {
|
|
10
14
|
let idsArray = [];
|
|
11
15
|
let specObj = {
|
|
12
16
|
list: []
|
|
13
17
|
};
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
fs.writeFileSync(filePath, content, 'utf8');
|
|
20
|
-
}
|
|
19
|
+
// ✅ 从项目根目录开始查找,而不是从主配置文件所在目录
|
|
20
|
+
// 找到项目根目录(包含 .git、Package.swift 等的目录)
|
|
21
|
+
const projectRoot = await findPath.findProjectRoot(mainSpecFile);
|
|
22
|
+
const searchRoot = projectRoot || path.dirname(mainSpecFile);
|
|
21
23
|
|
|
22
|
-
const specSlashIndex =
|
|
23
|
-
const specFilePath =
|
|
24
|
+
const specSlashIndex = mainSpecFile.lastIndexOf('/');
|
|
25
|
+
const specFilePath = mainSpecFile.substring(0, specSlashIndex + 1);
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
// ✅ 从项目根目录向下查找子模块的 AutoSnippet.boxspec.json
|
|
28
|
+
const array = await findPath.findSubASSpecPath(searchRoot);
|
|
26
29
|
|
|
27
30
|
for (let i = 0; i < array.length; i++) {
|
|
28
31
|
const filename = array[i];
|
|
29
32
|
|
|
30
33
|
const slashIndex = filename.lastIndexOf('/');
|
|
31
34
|
let thePath = filename.substring(0, slashIndex + 1);
|
|
32
|
-
if (filename ===
|
|
35
|
+
if (filename === mainSpecFile) {
|
|
33
36
|
thePath = '';
|
|
34
37
|
} else {
|
|
35
38
|
thePath = thePath.replace(specFilePath, '');
|
|
@@ -38,36 +41,58 @@ async function initSpec() {
|
|
|
38
41
|
try {
|
|
39
42
|
// 读取AutoSnippet的占位配置
|
|
40
43
|
const data = fs.readFileSync(filename, 'utf8');
|
|
41
|
-
|
|
44
|
+
const config = JSON.parse(data);
|
|
42
45
|
if (config && config.list) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
46
|
+
const arr = config.list.filter(function (item, index, array) {
|
|
47
|
+
for (let i = 0; i < idsArray.length; i++) {
|
|
48
|
+
if (item['{identifier}'] === idsArray[i]) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
50
52
|
idsArray.push(item['{identifier}']);
|
|
51
|
-
// ✅ SPM 模块:headName
|
|
53
|
+
// ✅ SPM 模块:headName 已经是相对于模块根目录的相对路径
|
|
52
54
|
// headName 保持相对于各自模块根目录的路径(不需要转换)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
specObj.list = specObj.list.concat(arr);
|
|
55
|
+
return true;
|
|
56
|
+
});
|
|
57
|
+
specObj.list = specObj.list.concat(arr);
|
|
57
58
|
}
|
|
58
59
|
} catch (err) {
|
|
59
60
|
console.error(err);
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
try {
|
|
65
|
+
const content = JSON.stringify(specObj, null, 4);
|
|
66
|
+
if (content) {
|
|
67
|
+
fs.writeFileSync(mainSpecFile, content, 'utf8');
|
|
68
|
+
}
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error(err);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function initSpec() {
|
|
75
|
+
// ✅ 查找 Package.swift,在其同级目录创建 AutoSnippet.boxspec.json
|
|
76
|
+
let packagePath = await findPath.findPackageSwiftPath(CMD_PATH);
|
|
77
|
+
|
|
78
|
+
// 如果找不到 Package.swift,使用当前目录
|
|
79
|
+
const configDir = packagePath ? path.dirname(packagePath) : CMD_PATH;
|
|
80
|
+
const filePath = path.join(configDir, 'AutoSnippet.boxspec.json');
|
|
81
|
+
|
|
82
|
+
// 如果配置文件不存在,创建一个空的
|
|
83
|
+
try {
|
|
84
|
+
await fs.promises.access(filePath);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
const specObj = {
|
|
87
|
+
list: []
|
|
88
|
+
};
|
|
89
|
+
const content = JSON.stringify(specObj, null, 4);
|
|
90
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ✅ 聚合子模块配置到主配置文件
|
|
94
|
+
await mergeSubSpecs(filePath);
|
|
71
95
|
}
|
|
72
96
|
|
|
73
|
-
exports.initSpec = initSpec;
|
|
97
|
+
exports.initSpec = initSpec;
|
|
98
|
+
exports.mergeSubSpecs = mergeSubSpecs;
|
package/bin/install.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const cache = require('./cache.js');
|
|
6
6
|
const config = require('./config.js');
|
|
7
|
+
const findPath = require('./findPath.js');
|
|
7
8
|
// 全局路径
|
|
8
9
|
const HOLDER_KEYS = ['{identifier}', '{title}', '{completion}', '{summary}', '{content}', '{language}'];
|
|
9
10
|
|
|
@@ -25,7 +26,10 @@ function writeSingleSnippet(snippet, template) {
|
|
|
25
26
|
// 如果有头文件,添加 headerVersion
|
|
26
27
|
if (snippet['{headName}']) {
|
|
27
28
|
let extPlace = Object.assign({}, snippet);
|
|
28
|
-
|
|
29
|
+
// ✅ 从项目根目录找到模块,只使用模块名和头文件名
|
|
30
|
+
// 例如:<BDVideoPlayer/BDVideoCacheManager.h> 而不是 <BDVideoPlayer/UI/BDVideoPlayer/Code/BDVideoCacheManager.h>
|
|
31
|
+
const headerFileName = path.basename(extPlace['{headName}']);
|
|
32
|
+
let header = '<' + extPlace['{specName}'] + '/' + headerFileName + '>';
|
|
29
33
|
header = escapeString(header);
|
|
30
34
|
|
|
31
35
|
// swift只需要考虑工作空间是否引入
|
|
@@ -152,7 +156,9 @@ function addCodeSnippets(specFile, singleSnippet) {
|
|
|
152
156
|
|
|
153
157
|
if (placeVal['{headName}']) {
|
|
154
158
|
let extPlace = Object.assign({}, placeVal);
|
|
155
|
-
|
|
159
|
+
// ✅ 从项目根目录找到模块,只使用模块名和头文件名
|
|
160
|
+
const headerFileName = path.basename(extPlace['{headName}']);
|
|
161
|
+
let header = '<' + extPlace['{specName}'] + '/' + headerFileName + '>';
|
|
156
162
|
header = escapeString(header);
|
|
157
163
|
|
|
158
164
|
// swift只需要考虑工作空间是否引入
|