autosnippet 1.1.14 → 1.1.16

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 CHANGED
@@ -17,18 +17,9 @@ const share = require('./share.js');
17
17
  const init = require('./init.js');
18
18
  const config = require('./config.js');
19
19
 
20
- // 获取配置文件路径(测试模式优先使用 BiliDemo 配置)
20
+ // 获取配置文件路径
21
21
  function getSpecFile(callback) {
22
- // 测试模式下,优先使用 BiliDemo 的配置文件
23
- if (config.isTestMode()) {
24
- const configPath = config.getConfigPath();
25
- if (configPath && fs.existsSync(configPath)) {
26
- callback(configPath);
27
- return;
28
- }
29
- }
30
-
31
- // 否则使用原有的查找逻辑
22
+ // 向上查找 AutoSnippet.boxspec.json 配置文件
32
23
  findPath.findASSpecPath(CMD_PATH, callback);
33
24
  }
34
25
 
package/bin/cache.js CHANGED
@@ -41,11 +41,12 @@ async function updateCache(specFile, content) {
41
41
  linkCache[key] = element['{readme}'];
42
42
  }
43
43
 
44
+ // ✅ SPM 模块:headName 已经包含相对路径,不需要 specHeadPath
44
45
  if (element['{headName}']
45
- && element['{specHeadPath}']
46
46
  && element['{language}'] !== 'Xcode.SourceCodeLanguage.Swift') {
47
-
48
- headCache[element['{headName}']] = element['{specHeadPath}'];
47
+ // headName 本身已经是相对路径,直接使用
48
+ const headerFileName = path.basename(element['{headName}']);
49
+ headCache[headerFileName] = element['{headName}'];
49
50
  }
50
51
  }
51
52
  });
package/bin/config.js CHANGED
@@ -3,87 +3,37 @@
3
3
  const path = require('path');
4
4
  const fs = require('fs');
5
5
 
6
- // 检测是否为测试模式
7
- // 通过环境变量 AUTOSNIPPET_TEST_MODE 或检测 BiliDemo 目录
8
- function isTestMode() {
9
- // 优先检查环境变量
10
- if (process.env.AUTOSNIPPET_TEST_MODE === 'true') {
11
- return true;
12
- }
13
-
14
- // 检查当前工作目录是否在 BiliDemo 项目内
15
- const cwd = process.cwd();
16
- if (cwd.includes('BiliDemo')) {
17
- return true;
18
- }
19
-
20
- return false;
21
- }
22
-
23
- // 获取 BiliDemo 项目根目录
24
- function getBiliDemoRoot() {
25
- const cwd = process.cwd();
26
- const parts = cwd.split(path.sep);
6
+ // 获取代码片段输出路径(写入 Xcode CodeSnippets 目录)
7
+ function getSnippetsPath() {
8
+ const USER_HOME = process.env.HOME || process.env.USERPROFILE;
9
+ const snippetsPath = path.join(USER_HOME, 'Library/Developer/Xcode/UserData/CodeSnippets');
27
10
 
28
- for (let i = parts.length; i > 0; i--) {
29
- const testPath = parts.slice(0, i).join(path.sep);
30
- if (fs.existsSync(path.join(testPath, 'BiliDili', 'AutoSnippet.boxspec.json'))) {
31
- return testPath;
32
- }
11
+ // 确保目录存在
12
+ try {
13
+ fs.accessSync(snippetsPath, fs.constants.F_OK);
14
+ } catch (err) {
15
+ fs.mkdirSync(snippetsPath, { recursive: true });
33
16
  }
34
17
 
35
- return null;
18
+ return snippetsPath;
36
19
  }
37
20
 
38
- // 获取配置文件路径
39
- function getConfigPath() {
40
- if (isTestMode()) {
41
- const biliDemoRoot = getBiliDemoRoot();
42
- if (biliDemoRoot) {
43
- return path.join(biliDemoRoot, 'BiliDili', 'AutoSnippet.boxspec.json');
44
- }
45
- }
46
- return null;
47
- }
48
-
49
- // 获取代码片段输出路径(测试模式写入 AutoSnippetCache,生产模式写入 Xcode)
50
- function getSnippetsPath() {
51
- if (isTestMode()) {
52
- // ✅ 测试模式:写入 AutoSnippetCache/Snippets/
53
- // __dirname: AutoSnippet/bin
54
- // 目标路径: AutoSnippet/AutoSnippetCache/Snippets
55
- const cachePath = path.join(__dirname, '../../AutoSnippetCache/Snippets');
56
- try {
57
- fs.accessSync(cachePath, fs.constants.F_OK);
58
- } catch (err) {
59
- fs.mkdirSync(cachePath, { recursive: true });
60
- }
61
- return cachePath;
62
- } else {
63
- // 生产模式:写入 Xcode CodeSnippets 目录
64
- const USER_HOME = process.env.HOME || process.env.USERPROFILE;
65
- return path.join(USER_HOME, 'Library/Developer/Xcode/UserData/CodeSnippets');
66
- }
67
- }
68
-
69
- // 获取缓存目录路径
21
+ // 获取缓存目录路径(使用用户 home 目录下的缓存)
70
22
  function getCachePath() {
71
- // 缓存始终在 AutoSnippetCache 目录
72
- // __dirname: AutoSnippet/bin
73
- // 目标路径: AutoSnippet/AutoSnippetCache
74
- const cachePath = path.join(__dirname, '../../AutoSnippetCache');
23
+ const USER_HOME = process.env.HOME || process.env.USERPROFILE;
24
+ const cachePath = path.join(USER_HOME, '.autosnippet', 'cache');
25
+
26
+ // 确保目录存在
75
27
  try {
76
28
  fs.accessSync(cachePath, fs.constants.F_OK);
77
29
  } catch (err) {
78
30
  fs.mkdirSync(cachePath, { recursive: true });
79
31
  }
32
+
80
33
  return cachePath;
81
34
  }
82
35
 
83
36
  module.exports = {
84
- isTestMode,
85
- getBiliDemoRoot,
86
- getConfigPath,
87
37
  getSnippetsPath,
88
38
  getCachePath
89
39
  };
package/bin/create.js CHANGED
@@ -13,23 +13,32 @@ const CMD_PATH = process.cwd();
13
13
 
14
14
  /**
15
15
  * 根据文件路径确定模块名(SPM)
16
+ * 返回路径中最近的(最深的)target 名称
16
17
  */
17
18
  function determineModuleName(filePath, packageInfo) {
18
19
  // 从路径中提取模块名
19
- // 例如:Services/Services/BDNetworkControl/Code/... -> BDNetworkControl
20
+ // 例如:Business/BDNetworkAPI/Code/... -> BDNetworkAPI(不是 Business)
20
21
  const packagePath = packageInfo.path;
21
22
  const relativePath = path.relative(packagePath, filePath);
22
23
  const segments = relativePath.split(path.sep);
23
24
 
24
- // 查找匹配的 target
25
- for (const segment of segments) {
25
+ // 从后向前查找匹配的 target(优先匹配路径中更深的 target)
26
+ // 这样可以避免匹配到聚合 target(如 Business),而匹配到实际的 target(如 BDNetworkAPI)
27
+ for (let i = segments.length - 1; i >= 0; i--) {
28
+ const segment = segments[i];
26
29
  if (packageInfo.targets.includes(segment)) {
27
30
  return segment;
28
31
  }
29
32
  }
30
33
 
31
- // 如果找不到,使用第一个 target
32
- return packageInfo.targets[0] || packageInfo.name;
34
+ // 如果找不到,使用第一个 target(排除包名,如果包名也是 target)
35
+ const firstTarget = packageInfo.targets[0];
36
+ if (firstTarget && firstTarget !== packageInfo.name) {
37
+ return firstTarget;
38
+ }
39
+
40
+ // 最后回退到包名
41
+ return packageInfo.name;
33
42
  }
34
43
 
35
44
  function updateCodeSnippets(specFile, word, key, value) {
@@ -229,15 +238,14 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
229
238
  const fileName = filePath.substring(slashIndex + 1, dotIndex + 1) + 'h';
230
239
  const thePath = filePath.substring(0, slashIndex + 1);
231
240
 
232
- const specSlashIndex = specFile.lastIndexOf('/');
233
- const specFilePath = specFile.substring(0, specSlashIndex + 1);
234
-
235
241
  // ✅ 使用 SPM 的 Package.swift 查找(替代 .boxspec)
236
242
  findPath.findPackageSwiftPath(thePath).then(async function (packagePath) {
237
243
  if (!packagePath) {
238
244
  console.log('未找到 Package.swift 文件,请检查路径。');
239
245
  snippet['{content}'] = codeList;
240
- saveFromFile(specFile, snippet);
246
+ // ✅ SPM 模块:.boxspec 文件位置在模块根目录(与 Package.swift 同级)
247
+ const moduleSpecFile = path.join(path.dirname(packagePath), 'AutoSnippet.boxspec.json');
248
+ saveFromFile(moduleSpecFile, snippet);
241
249
  return;
242
250
  }
243
251
 
@@ -245,35 +253,51 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
245
253
  const packageInfo = await findPath.parsePackageSwift(packagePath);
246
254
  if (!packageInfo) {
247
255
  snippet['{content}'] = codeList;
248
- saveFromFile(specFile, snippet);
256
+ const moduleSpecFile = path.join(path.dirname(packagePath), 'AutoSnippet.boxspec.json');
257
+ saveFromFile(moduleSpecFile, snippet);
249
258
  return;
250
259
  }
251
260
 
252
261
  // 根据当前文件路径确定模块名
253
262
  const moduleName = determineModuleName(filePath, packageInfo);
254
- const headerName = fileName.substring(0, fileName.length - 2); // 移除 .h
263
+ const headerNameWithoutExt = fileName.substring(0, fileName.length - 2); // 移除 .h
264
+
265
+ // ✅ 模块根目录就是 Package.swift 所在目录(packageInfo.path)
266
+ // .boxspec.json 文件将创建在这个目录下(与 Package.swift 同级)
267
+ const moduleRoot = packageInfo.path;
255
268
 
256
269
  // ✅ 查找头文件(适配 SPM 的 include/ModuleName/ 结构)
257
- const headerPath = await findPath.findSubHeaderPath(packageInfo.path, headerName, moduleName);
270
+ // Package.swift 所在目录下查找(可能包含 Services/Services/... 这样的结构)
271
+ const headerPath = await findPath.findSubHeaderPath(moduleRoot, headerNameWithoutExt, moduleName);
258
272
 
259
273
  snippet['{content}'] = codeList;
260
- snippet['{specName}'] = moduleName;
261
- snippet['{headName}'] = fileName;
274
+ snippet['{specName}'] = moduleName; // ✅ specName 是 target 名称(如 BDNetworkAPI),不是包名(如 Business)
262
275
 
263
276
  if (headerPath) {
264
- snippet['{specHeadPath}'] = encodeURI(headerPath.replace(specFilePath, ''));
277
+ // headName 存储相对于 Package.swift 所在目录的相对路径
278
+ // 例如:如果 Package.swift 在 Business/,头文件在 Business/Business/BDNetworkAPI/Code/xxx.h
279
+ // 则 headName = "Business/BDNetworkAPI/Code/xxx.h"
280
+ const headerRelativePath = path.relative(moduleRoot, headerPath);
281
+ snippet['{headName}'] = headerRelativePath;
282
+ } else {
283
+ // 如果找不到头文件,使用文件名
284
+ snippet['{headName}'] = fileName;
265
285
  }
266
286
 
267
287
  // 查找 README.md
268
288
  try {
269
289
  const readmePath = path.join(packageInfo.path, README_NAME);
270
290
  await fs.promises.access(readmePath);
271
- snippet['{readme}'] = encodeURI(readmePath.replace(specFilePath, ''));
291
+ const moduleRoot = packageInfo.path;
292
+ const readmeRelativePath = path.relative(moduleRoot, readmePath);
293
+ snippet['{readme}'] = encodeURI(readmeRelativePath);
272
294
  } catch {
273
295
  // README.md 不存在,跳过
274
296
  }
275
297
 
276
- saveFromFile(specFile, snippet);
298
+ // ✅ SPM 模块:.boxspec 文件位置在模块根目录(与 Package.swift 同级)
299
+ const moduleSpecFile = path.join(packageInfo.path, 'AutoSnippet.boxspec.json');
300
+ saveFromFile(moduleSpecFile, snippet);
277
301
  }).catch(function (err) {
278
302
  console.error('Error finding Package.swift:', err);
279
303
  snippet['{content}'] = codeList;
@@ -301,7 +325,23 @@ function saveFromFile(specFile, snippet) {
301
325
  placeholder = JSON.parse(data);
302
326
  }
303
327
  } catch (err) {
304
- console.error(err);
328
+ // ✅ 文件不存在或读取失败,创建新的配置文件
329
+ if (err.code === 'ENOENT') {
330
+ // 确保目录存在
331
+ const specFileDir = path.dirname(specFile);
332
+ try {
333
+ fs.mkdirSync(specFileDir, { recursive: true });
334
+ } catch (mkdirErr) {
335
+ // 目录可能已存在,忽略错误
336
+ }
337
+ // 创建空的配置文件结构
338
+ placeholder = {
339
+ list: []
340
+ };
341
+ } else {
342
+ console.error(err);
343
+ return;
344
+ }
305
345
  }
306
346
 
307
347
  if (placeholder != null) {
package/bin/findPath.js CHANGED
@@ -51,46 +51,77 @@ async function getDirectoryEntries(dirPath) {
51
51
  }
52
52
  }
53
53
 
54
- // 向上查找AutoSnippet配置文件(优化:找到配置文件后,最多再向上查找两层停止)
54
+ /**
55
+ * 检测是否为工程根目录
56
+ * 通过查找常见的工程根目录标记来判断
57
+ */
58
+ function isProjectRoot(dirPath, entries) {
59
+ if (!entries) {
60
+ return false;
61
+ }
62
+
63
+ // 工程根目录标记(按优先级)
64
+ const rootMarkers = [
65
+ '.git', // Git 仓库根目录
66
+ PACKAGE_SWIFT, // SPM 项目根目录
67
+ '.xcodeproj', // Xcode 项目根目录
68
+ '.xcworkspace', // Xcode 工作空间根目录
69
+ 'Podfile', // CocoaPods 项目根目录
70
+ '.swiftpm', // Swift Package Manager 元数据目录
71
+ ];
72
+
73
+ for (const entry of entries) {
74
+ const name = entry.name;
75
+
76
+ // 检查文件标记
77
+ if (entry.isFile() && rootMarkers.includes(name)) {
78
+ return true;
79
+ }
80
+
81
+ // 检查目录标记(.git 可能是目录)
82
+ if (entry.isDirectory() && rootMarkers.includes(name)) {
83
+ return true;
84
+ }
85
+ }
86
+
87
+ return false;
88
+ }
89
+
90
+ // 向上查找AutoSnippet配置文件(优化:找到配置文件后,继续向上查找直到找到工程根目录)
55
91
  async function findASSpecPathAsync(filePath) {
56
92
  let configPath = null;
57
- let levelsUp = 0;
93
+ let configDir = null;
58
94
  let currentPath = path.resolve(filePath);
95
+ const maxLevels = 20; // 安全限制:最多向上查找 20 层
96
+ let levelsChecked = 0;
59
97
 
60
- while (currentPath) {
98
+ while (currentPath && levelsChecked < maxLevels) {
61
99
  try {
62
100
  const entries = await getDirectoryEntries(currentPath);
63
- if (!entries) {
64
- // 目录不存在,继续向上
65
- const parentPath = path.dirname(currentPath);
66
- if (parentPath === currentPath) {
67
- break; // 已到达根目录
68
- }
69
- currentPath = parentPath;
70
- continue;
71
- }
72
101
 
73
102
  // 查找 AutoSnippet.boxspec.json
74
- for (const entry of entries) {
75
- if (entry.isFile() && entry.name === HOLDER_NAME) {
76
- if (!configPath) {
77
- // 第一次找到配置文件
78
- configPath = path.join(currentPath, entry.name);
103
+ if (entries) {
104
+ for (const entry of entries) {
105
+ if (entry.isFile() && entry.name === HOLDER_NAME) {
106
+ if (!configPath) {
107
+ // 第一次找到配置文件
108
+ configPath = path.join(currentPath, entry.name);
109
+ configDir = currentPath;
110
+ }
79
111
  }
80
- // 找到后,标记为已找到,继续向上最多两层
81
112
  }
82
113
  }
83
114
 
84
- // 如果已经找到配置文件,检查是否已经向上查找了两层
115
+ // 如果已经找到配置文件,检查是否到达工程根目录
85
116
  if (configPath) {
86
- if (path.resolve(currentPath) !== path.dirname(configPath)) {
87
- // 当前目录不是配置文件所在目录,说明已经向上查找了
88
- levelsUp++;
117
+ // 如果在配置文件所在目录或以上发现了工程根目录标记,停止查找
118
+ if (isProjectRoot(currentPath, entries)) {
119
+ return configPath;
89
120
  }
90
121
 
91
- // 如果已经向上查找了两层,停止查找
92
- if (levelsUp >= 2) {
93
- return configPath;
122
+ // 如果已经向上查找了,但没有找到工程根目录标记,继续查找
123
+ if (path.resolve(currentPath) !== configDir) {
124
+ // 当前目录不是配置文件所在目录,继续向上查找
94
125
  }
95
126
  }
96
127
 
@@ -101,6 +132,7 @@ async function findASSpecPathAsync(filePath) {
101
132
  break;
102
133
  }
103
134
  currentPath = parentPath;
135
+ levelsChecked++;
104
136
 
105
137
  } catch (err) {
106
138
  // 错误处理:权限错误继续查找
@@ -110,6 +142,7 @@ async function findASSpecPathAsync(filePath) {
110
142
  break;
111
143
  }
112
144
  currentPath = parentPath;
145
+ levelsChecked++;
113
146
  continue;
114
147
  }
115
148
  // 其他错误,抛出
@@ -117,73 +150,134 @@ async function findASSpecPathAsync(filePath) {
117
150
  }
118
151
  }
119
152
 
153
+ // 如果找到配置文件,返回它(即使没有找到明确的工程根目录)
120
154
  return configPath || null;
121
155
  }
122
156
 
157
+ /**
158
+ * 同步版本:检测是否为工程根目录
159
+ */
160
+ function isProjectRootSync(dirPath, files) {
161
+ if (!files || files.length === 0) {
162
+ return false;
163
+ }
164
+
165
+ // 工程根目录标记(按优先级)
166
+ const rootMarkers = [
167
+ '.git', // Git 仓库根目录
168
+ PACKAGE_SWIFT, // SPM 项目根目录
169
+ '.xcodeproj', // Xcode 项目根目录
170
+ '.xcworkspace', // Xcode 工作空间根目录
171
+ 'Podfile', // CocoaPods 项目根目录
172
+ ];
173
+
174
+ // 检查文件名
175
+ for (const filename of files) {
176
+ if (rootMarkers.includes(filename)) {
177
+ // 检查是否为文件或目录
178
+ try {
179
+ const filePath = path.join(dirPath, filename);
180
+ const stats = fs.lstatSync(filePath);
181
+ if (stats.isFile() || stats.isDirectory()) {
182
+ return true;
183
+ }
184
+ } catch (err) {
185
+ // 忽略错误,继续检查
186
+ }
187
+ }
188
+ }
189
+
190
+ return false;
191
+ }
192
+
123
193
  // 向上查找AutoSnippet配置文件(保留回调版本,兼容现有代码)
124
- function findASSpecPath(filePath, callback, configPath, levelsUp) {
194
+ function findASSpecPath(filePath, callback, configPath, configDir) {
125
195
  // 初始化参数
126
196
  if (configPath === undefined) configPath = null;
127
- if (levelsUp === undefined) levelsUp = 0;
197
+ if (configDir === undefined) configDir = null;
128
198
 
129
- fs.readdir(filePath, function (err, files) {
130
- if (err) {
131
- console.log(err);
199
+ const maxLevels = 20; // 安全限制:最多向上查找 20 层
200
+ let levelsChecked = 0;
201
+
202
+ function search(currentPath, foundConfigPath, foundConfigDir, level) {
203
+ if (level >= maxLevels) {
204
+ if (foundConfigPath) {
205
+ callback(foundConfigPath);
206
+ } else {
207
+ console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
208
+ }
132
209
  return;
133
210
  }
134
-
135
- let isFound = false;
136
- let foundConfigPath = configPath;
137
- files.forEach(function (filename) {
138
- if (filename === HOLDER_NAME) {
139
- isFound = true;
140
- if (!foundConfigPath) {
141
- // 第一次找到配置文件,记录路径并调用回调
142
- foundConfigPath = path.resolve(filePath);
143
- callback(path.join(filePath, filename));
211
+
212
+ fs.readdir(currentPath, function (err, files) {
213
+ if (err) {
214
+ // 错误处理:权限错误继续查找
215
+ if (err.code === 'ENOENT' || err.code === 'EACCES') {
216
+ const parentPath = path.join(currentPath, '/..');
217
+ const parentResolvedPath = path.resolve(parentPath);
218
+
219
+ // 如果已经到达根目录,停止查找
220
+ if (parentResolvedPath === path.resolve(currentPath)) {
221
+ if (foundConfigPath) {
222
+ callback(foundConfigPath);
223
+ } else {
224
+ console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
225
+ }
226
+ return;
227
+ }
228
+
229
+ search(parentPath, foundConfigPath, foundConfigDir, level + 1);
230
+ } else {
231
+ console.log(err);
144
232
  }
145
- }
146
- });
147
-
148
- // 如果已经找到配置文件,检查是否已经向上查找了两层
149
- if (foundConfigPath) {
150
- if (!isFound) {
151
- // 当前目录不是配置文件所在目录,说明已经向上查找了
152
- levelsUp++;
153
- }
154
-
155
- // 如果已经向上查找了两层,停止查找
156
- if (levelsUp >= 2) {
157
233
  return;
158
234
  }
159
- }
160
235
 
161
- // 如果还没找到配置文件,继续向上查找
162
- if (!foundConfigPath) {
163
- // 继续向上查找
164
- const parentPath = path.join(filePath, '/..');
165
- const parentResolvedPath = path.resolve(parentPath);
236
+ let isFound = false;
237
+ let currentConfigPath = foundConfigPath;
238
+ let currentConfigDir = foundConfigDir;
166
239
 
167
- // 如果已经到达根目录,停止查找
168
- if (parentResolvedPath === path.resolve(filePath)) {
169
- console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
170
- return;
240
+ // 查找 AutoSnippet.boxspec.json
241
+ files.forEach(function (filename) {
242
+ if (filename === HOLDER_NAME) {
243
+ isFound = true;
244
+ if (!currentConfigPath) {
245
+ // 第一次找到配置文件,记录路径(不立即调用回调)
246
+ currentConfigPath = path.join(currentPath, filename);
247
+ currentConfigDir = path.resolve(currentPath);
248
+ }
249
+ }
250
+ });
251
+
252
+ // 如果已经找到配置文件,检查是否到达工程根目录
253
+ if (currentConfigPath) {
254
+ // 如果在配置文件所在目录或以上发现了工程根目录标记,停止查找并调用回调
255
+ if (isProjectRootSync(currentPath, files)) {
256
+ callback(currentConfigPath);
257
+ return;
258
+ }
171
259
  }
172
260
 
173
- findASSpecPath(parentPath, callback, foundConfigPath, 0);
174
- } else {
175
- // 已经找到配置文件,继续向上查找(最多两层)
176
- const parentPath = path.join(filePath, '/..');
261
+ // 继续向上查找
262
+ const parentPath = path.join(currentPath, '/..');
177
263
  const parentResolvedPath = path.resolve(parentPath);
178
264
 
179
265
  // 如果已经到达根目录,停止查找
180
- if (parentResolvedPath === path.resolve(filePath)) {
266
+ if (parentResolvedPath === path.resolve(currentPath)) {
267
+ // 如果找到了配置文件,即使没找到工程根目录标记,也调用回调
268
+ if (currentConfigPath) {
269
+ callback(currentConfigPath);
270
+ } else {
271
+ console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
272
+ }
181
273
  return;
182
274
  }
183
275
 
184
- findASSpecPath(parentPath, callback, foundConfigPath, levelsUp);
185
- }
186
- });
276
+ search(parentPath, currentConfigPath, currentConfigDir, level + 1);
277
+ });
278
+ }
279
+
280
+ search(filePath, configPath, configDir, 0);
187
281
  }
188
282
 
189
283
  // 向上查找 Package.swift 文件(SPM 模块规范文件)
package/bin/init.js CHANGED
@@ -47,11 +47,10 @@ async function initSpec() {
47
47
  return false;
48
48
  }
49
49
  }
50
- idsArray.push(item['{identifier}']);
51
- // 头文件相对路径需要补齐
52
- if (item['{specHeadPath}']) {
53
- item['{specHeadPath}'] = thePath + item['{specHeadPath}'];
54
- }
50
+ idsArray.push(item['{identifier}']);
51
+ // ✅ SPM 模块:headName 已经是相对于模块根目录的相对路径,如果子模块与主配置不在同一目录,需要调整
52
+ // headName 保持相对于各自模块根目录的路径(不需要转换)
53
+ // 去掉 specHeadPath 的处理逻辑
55
54
  return true;
56
55
  });
57
56
  specObj.list = specObj.list.concat(arr);
package/bin/injection.js CHANGED
@@ -58,33 +58,117 @@ function handleHeaderLineSwift(specFile, updateFile, headerLine, importArray) {
58
58
 
59
59
  // specFile实际上是获取缓存的key,用来获取Snippet的模块空间信息,没有路径意义
60
60
  // updateFile是当前修改文件路径,用来获取当前模块空间信息
61
- function handleHeaderLine(specFile, updateFile, headerLine, importArray, isSwift) {
61
+ async function handleHeaderLine(specFile, updateFile, headerLine, importArray, isSwift) {
62
62
  if (isSwift) {
63
63
  handleHeaderLineSwift(specFile, updateFile, headerLine, importArray);
64
64
  return;
65
65
  }
66
66
  const header = createHeader(headerLine);
67
67
 
68
- // 首先识别是否是组件内部修改,默认头文件间接等于模块名
69
- cache.getHeadCache(specFile).then(function (headCache) {
70
- if (headCache) {
71
- const headPath = headCache[header.headerName];
68
+ // ✅ 重新设计:区分当前模块内部和外部
69
+ // 1. 获取当前文件所在的模块(通过 Package.swift)
70
+ // 2. 获取头文件所在的模块(从头文件路径查找 Package.swift,确定最近的 target 模块名)
71
+ // 3. 比较模块名:
72
+ // - 相同模块 -> 使用相对路径 #import "Header.h"
73
+ // - 不同模块 -> 使用 #import <ModuleName/Header.h>
74
+
75
+ // 获取当前文件所在的模块
76
+ const updateFileDir = path.dirname(updateFile);
77
+ const currentPackagePath = await findPath.findPackageSwiftPath(updateFileDir);
78
+
79
+ if (!currentPackagePath) {
80
+ // 找不到当前模块的 Package.swift,默认使用 <> 格式
81
+ handleModuleHeader(specFile, updateFile, header, importArray, true);
82
+ return;
83
+ }
72
84
 
73
- const dotIndex = headPath.lastIndexOf('.');
74
- const slashIndex = headPath.lastIndexOf('/');
75
- const currModuleName = headPath.substring(slashIndex + 1, dotIndex);
76
- if (currModuleName === header.moduleName) {
77
- handleModuleHeader(specFile, updateFile, header, importArray, false);
78
- } else {
79
- handleModuleHeader(specFile, updateFile, header, importArray, true);
80
- }
85
+ const currentPackageInfo = await findPath.parsePackageSwift(currentPackagePath);
86
+ if (!currentPackageInfo) {
87
+ handleModuleHeader(specFile, updateFile, header, importArray, true);
88
+ return;
89
+ }
90
+
91
+ // 获取当前文件所在的模块名(通过路径判断)
92
+ const currentModuleName = determineCurrentModuleName(updateFile, currentPackageInfo);
93
+
94
+ // ✅ 获取头文件缓存,然后确定头文件所在的模块名
95
+ const headCache = await cache.getHeadCache(specFile);
96
+ const headerModuleName = await determineHeaderModuleName(specFile, header, headCache);
97
+
98
+ // ✅ 更新 header 中的模块名和 specName,确保使用正确的模块名(最近的 target 模块名)
99
+ header.moduleName = headerModuleName;
100
+ header.specName = '<' + headerModuleName + '/' + header.headerName + '>';
101
+
102
+ // ✅ 判断是否为同一模块
103
+ const isSameModule = currentModuleName === headerModuleName;
104
+
105
+ // 如果是同一模块,使用相对路径;否则使用 <> 格式
106
+ handleModuleHeader(specFile, updateFile, header, importArray, !isSameModule);
107
+ }
108
+
109
+ /**
110
+ * 根据文件路径确定当前模块名(SPM)
111
+ */
112
+ function determineCurrentModuleName(filePath, packageInfo) {
113
+ const relativePath = path.relative(packageInfo.path, filePath);
114
+ const segments = relativePath.split(path.sep);
115
+
116
+ // 查找匹配的 target(最近的 target)
117
+ for (const segment of segments) {
118
+ if (packageInfo.targets.includes(segment)) {
119
+ return segment;
81
120
  }
82
- });
121
+ }
122
+
123
+ // 如果找不到,使用第一个 target
124
+ return packageInfo.targets[0] || packageInfo.name;
125
+ }
126
+
127
+ /**
128
+ * 确定头文件所在的模块名(SPM)
129
+ * 从头文件路径查找 Package.swift,确定最近的 target 模块名
130
+ */
131
+ async function determineHeaderModuleName(specFile, header, headCache) {
132
+ if (!headCache) {
133
+ // 没有缓存,使用从 headerLine 解析的模块名(可能不准确)
134
+ return header.moduleName;
135
+ }
136
+
137
+ // 从头缓存中获取头文件的相对路径
138
+ const headRelativePath = headCache[header.headerName];
139
+ if (!headRelativePath) {
140
+ // 找不到头文件路径,使用从 headerLine 解析的模块名
141
+ return header.moduleName;
142
+ }
143
+
144
+ // ✅ specFile 位于模块根目录(如 ModuleRoot/AutoSnippet.boxspec.json)
145
+ const moduleRootDir = path.dirname(specFile);
146
+ const headPath = path.join(moduleRootDir, headRelativePath);
147
+
148
+ // 从头文件路径向上查找 Package.swift
149
+ const headerPackagePath = await findPath.findPackageSwiftPath(headPath);
150
+ if (!headerPackagePath) {
151
+ // 找不到 Package.swift,使用从 headerLine 解析的模块名
152
+ return header.moduleName;
153
+ }
154
+
155
+ // 解析 Package.swift 获取模块信息
156
+ const headerPackageInfo = await findPath.parsePackageSwift(headerPackagePath);
157
+ if (!headerPackageInfo) {
158
+ return header.moduleName;
159
+ }
160
+
161
+ // ✅ 根据头文件路径确定最近的 target 模块名
162
+ return determineCurrentModuleName(headPath, headerPackageInfo);
83
163
  }
84
164
 
85
165
  // isOuter区分模块内部引用""格式和模块外部引用<>格式
166
+ // ✅ SPM 模块:
167
+ // - isOuter = false:同一模块内部,使用相对路径 #import "Header.h"
168
+ // - isOuter = true:不同模块之间,使用 #import <ModuleName/Header.h>
86
169
  function handleModuleHeader(specFile, updateFile, header, importArray, isOuter) {
87
- const headName = isOuter ? header.name : header.headerStrName;
170
+ // 根据 isOuter 选择不同的引入格式
171
+ const headName = isOuter ? header.name : header.headerStrName; // isOuter: <ModuleName/Header.h>, 否则: "Header.h"
88
172
  const moduleName = isOuter ? header.specName : header.moduleStrName;
89
173
 
90
174
  // 检查是否已经引入头文件
@@ -93,20 +177,21 @@ function handleModuleHeader(specFile, updateFile, header, importArray, isOuter)
93
177
 
94
178
  if (importHeader === headName) {
95
179
  // 已经引入头文件
96
- handelAddHeaderStatus(specFile, updateFile, header, true, false);
180
+ handelAddHeaderStatus(specFile, updateFile, header, true, false, isOuter);
97
181
  return;
98
182
  } else if (importHeader === moduleName) {
99
- // 已经引入spec头文件
100
- handelAddHeaderStatus(specFile, updateFile, header, false, true);
183
+ // 已经引入模块头文件(如 <ModuleName/ModuleName.h>)
184
+ handelAddHeaderStatus(specFile, updateFile, header, false, true, isOuter);
101
185
  return;
102
186
  }
103
187
  }
104
188
 
105
189
  // 没有找到已引入的头文件,直接添加
106
- addHeaderToFile(updateFile, header);
190
+ // addHeaderToFile 会根据 isOuter 使用不同的格式
191
+ addHeaderToFile(updateFile, header, isOuter);
107
192
  }
108
193
 
109
- function handelAddHeaderStatus(specFile, updateFile, header, isAddedHeader, isAddedSpecHeader) {
194
+ function handelAddHeaderStatus(specFile, updateFile, header, isAddedHeader, isAddedSpecHeader, isOuter) {
110
195
  if (isAddedHeader) {
111
196
  // 已经引入头文件
112
197
  removeMarkFromFile(updateFile, header, '依赖头文件已存在,不需要额外引入。');
@@ -117,21 +202,32 @@ function handelAddHeaderStatus(specFile, updateFile, header, isAddedHeader, isAd
117
202
  // spec header足够了,不需要添加头文件
118
203
  removeMarkFromFile(updateFile, header, '依赖模块头文件已存在,不需要额外引入。');
119
204
  } else {
120
- addHeaderToFile(updateFile, header);
205
+ // 使用传入的 isOuter 参数
206
+ addHeaderToFile(updateFile, header, isOuter);
121
207
  }
122
208
  });
123
209
  } else {
124
210
  // 都没找到,添加头文件
125
- addHeaderToFile(updateFile, header);
211
+ // 使用传入的 isOuter 参数
212
+ addHeaderToFile(updateFile, header, isOuter);
126
213
  }
127
214
  }
128
215
 
129
216
  function isAddedToSpecHeader(specFile, header, callback) {
130
217
  cache.getHeadCache(specFile).then(function (headCache) {
131
218
  if (headCache) {
132
- const specSlashIndex = specFile.lastIndexOf('/');
133
- const specFilePath = specFile.substring(0, specSlashIndex + 1);
134
- const headPath = specFilePath + headCache[header.headerName];
219
+ // SPM 模块:headName 已经是相对于模块根目录的相对路径
220
+ // specFile 位于模块根目录(如 ModuleRoot/AutoSnippet.boxspec.json)
221
+ const moduleRootDir = path.dirname(specFile);
222
+ const headRelativePath = headCache[header.headerName];
223
+
224
+ if (!headRelativePath) {
225
+ callback(false);
226
+ return;
227
+ }
228
+
229
+ // 拼接头文件的完整路径
230
+ const headPath = path.join(moduleRootDir, headRelativePath);
135
231
 
136
232
  try {
137
233
  // 读取当前头文件所在工作空间里默认暴露的头文件
@@ -168,8 +264,15 @@ function removeMarkFromFile(updateFile, header, string) {
168
264
  });
169
265
  }
170
266
 
171
- function addHeaderToFile(updateFile, header) {
172
- readStream(updateFile, importMark + ' ' + header.name, importMark);
267
+ function addHeaderToFile(updateFile, header, isOuter) {
268
+ // 根据 isOuter 选择不同的引入格式
269
+ // isOuter = true: 使用 <> 格式(#import <ModuleName/Header.h>)
270
+ // isOuter = false: 使用相对路径(#import "Header.h")
271
+ const importLine = isOuter
272
+ ? importMark + ' ' + header.name // <ModuleName/Header.h>
273
+ : importMark + ' ' + header.headerStrName; // "Header.h"
274
+
275
+ readStream(updateFile, importLine, importMark);
173
276
  checkDependency(updateFile, header.moduleName, '自动注入头文件完成。').catch(err => {
174
277
  console.error('Error checking dependency:', err);
175
278
  });
package/bin/install.js CHANGED
@@ -80,7 +80,7 @@ function writeSingleSnippet(snippet, template) {
80
80
  });
81
81
 
82
82
  if (identifier && content) {
83
- // ✅ 使用配置模块获取代码片段输出路径(测试模式写入 AutoSnippetCache
83
+ // ✅ 使用配置模块获取代码片段输出路径(写入 Xcode CodeSnippets
84
84
  const snippetsPath = config.getSnippetsPath();
85
85
 
86
86
  try {
@@ -208,7 +208,7 @@ function addCodeSnippets(specFile, singleSnippet) {
208
208
  });
209
209
 
210
210
  if (identifier && content) {
211
- // ✅ 使用配置模块获取代码片段输出路径(测试模式写入 AutoSnippetCache
211
+ // ✅ 使用配置模块获取代码片段输出路径(写入 Xcode CodeSnippets
212
212
  const snippetsPath = config.getSnippetsPath();
213
213
 
214
214
  try {
@@ -219,11 +219,6 @@ function addCodeSnippets(specFile, singleSnippet) {
219
219
  try {
220
220
  const snippetFile = path.join(snippetsPath, identifier + '.codesnippet');
221
221
  fs.writeFileSync(snippetFile, content);
222
-
223
- // 测试模式下显示提示信息
224
- if (config.isTestMode()) {
225
- console.log(`[测试模式] 代码片段已写入: ${snippetFile}`);
226
- }
227
222
  } catch (err) {
228
223
  console.log(err);
229
224
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
4
4
  "description": "A iOS module management tool.",
5
5
  "main": "index.js",
6
6
  "scripts": {