autosnippet 1.1.13 → 1.1.14

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 CHANGED
@@ -4,189 +4,351 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  // 全局常量
6
6
  const HOLDER_NAME = 'AutoSnippet.boxspec.json';
7
- const BOX_SPEC_NAME = '.boxspec';
8
- const POD_SPEC_NAME = '.podspec';
7
+ const PACKAGE_SWIFT = 'Package.swift';
9
8
  const README_NAME = 'readme.md';
10
- const PCH_NAME = '.pch';
11
9
 
12
- // 向上查找AutoSnippet配置文件
13
- function findASSpecPath(filePath, callback) {
14
- fs.readdir(filePath, function (err, files) {
15
- if (err) {
16
- console.log(err);
17
- return;
18
- }
19
-
20
- let isEnd = false;
21
- files.forEach(function (filename) {
10
+ // 目录缓存(优化:减少重复读取)
11
+ const directoryCache = new Map();
12
+ const CACHE_TTL = 60000; // 缓存 60 秒
22
13
 
23
- if (filename === HOLDER_NAME) {
24
- isEnd = true;
25
- callback(path.join(filePath, filename));
14
+ /**
15
+ * 获取目录内容(带缓存)
16
+ */
17
+ async function getDirectoryEntries(dirPath) {
18
+ const cacheKey = path.resolve(dirPath);
19
+ const cached = directoryCache.get(cacheKey);
20
+
21
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
22
+ return cached.entries;
23
+ }
24
+
25
+ try {
26
+ const entries = await fs.promises.readdir(dirPath, {
27
+ withFileTypes: true // 返回 Dirent 对象,减少 stat 调用
28
+ });
29
+
30
+ // 清理过期缓存(保持缓存大小合理)
31
+ if (directoryCache.size > 1000) {
32
+ const now = Date.now();
33
+ for (const [key, value] of directoryCache.entries()) {
34
+ if (now - value.timestamp >= CACHE_TTL) {
35
+ directoryCache.delete(key);
36
+ }
26
37
  }
27
- });
28
-
29
- if (isEnd) {
30
- return;
31
38
  }
32
-
33
- if (!isEnd && filePath !== '/' && filePath !== '/Users') {
34
- findASSpecPath(path.join(filePath, '/..'), callback);
35
- } else {
36
- console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
39
+
40
+ directoryCache.set(cacheKey, {
41
+ entries,
42
+ timestamp: Date.now()
43
+ });
44
+
45
+ return entries;
46
+ } catch (err) {
47
+ if (err.code === 'ENOENT' || err.code === 'EACCES') {
48
+ return null; // 路径不存在或权限不足
37
49
  }
38
- });
50
+ throw err;
51
+ }
39
52
  }
40
53
 
41
- // 向上查找.pch文件
42
- function findPCHPath(filePath, callback) {
43
- fs.readdir(filePath, function (err, files) {
44
- if (err) {
45
- console.log(err);
46
- return;
47
- }
48
-
49
- let isEnd = false;
50
- files.forEach(function (filename) {
51
-
52
- if (filename.endsWith(PCH_NAME)) {
53
- isEnd = true;
54
- callback(true, path.join(filePath, filename));
54
+ // 向上查找AutoSnippet配置文件(优化:找到配置文件后,最多再向上查找两层停止)
55
+ async function findASSpecPathAsync(filePath) {
56
+ let configPath = null;
57
+ let levelsUp = 0;
58
+ let currentPath = path.resolve(filePath);
59
+
60
+ while (currentPath) {
61
+ try {
62
+ 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;
55
71
  }
56
- });
57
-
58
- if (isEnd) {
59
- return;
60
- }
61
-
62
- if (!isEnd && filePath !== '/' && filePath !== '/Users') {
63
- findPCHPath(path.join(filePath, '/..'), callback);
64
- } else {
65
- callback(false, null);
72
+
73
+ // 查找 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);
79
+ }
80
+ // 找到后,标记为已找到,继续向上最多两层
81
+ }
82
+ }
83
+
84
+ // 如果已经找到配置文件,检查是否已经向上查找了两层
85
+ if (configPath) {
86
+ if (path.resolve(currentPath) !== path.dirname(configPath)) {
87
+ // 当前目录不是配置文件所在目录,说明已经向上查找了
88
+ levelsUp++;
89
+ }
90
+
91
+ // 如果已经向上查找了两层,停止查找
92
+ if (levelsUp >= 2) {
93
+ return configPath;
94
+ }
95
+ }
96
+
97
+ // 继续向上查找
98
+ const parentPath = path.dirname(currentPath);
99
+ if (parentPath === currentPath) {
100
+ // 已到达根目录
101
+ break;
102
+ }
103
+ currentPath = parentPath;
104
+
105
+ } catch (err) {
106
+ // 错误处理:权限错误继续查找
107
+ if (err.code === 'ENOENT' || err.code === 'EACCES') {
108
+ const parentPath = path.dirname(currentPath);
109
+ if (parentPath === currentPath) {
110
+ break;
111
+ }
112
+ currentPath = parentPath;
113
+ continue;
114
+ }
115
+ // 其他错误,抛出
116
+ throw err;
66
117
  }
67
- });
118
+ }
119
+
120
+ return configPath || null;
68
121
  }
69
122
 
70
- // 向上查找模块.spec文件
71
- function findBPSpacPath(filePath, callback) {
123
+ // 向上查找AutoSnippet配置文件(保留回调版本,兼容现有代码)
124
+ function findASSpecPath(filePath, callback, configPath, levelsUp) {
125
+ // 初始化参数
126
+ if (configPath === undefined) configPath = null;
127
+ if (levelsUp === undefined) levelsUp = 0;
128
+
72
129
  fs.readdir(filePath, function (err, files) {
73
130
  if (err) {
74
131
  console.log(err);
75
132
  return;
76
133
  }
77
134
 
78
- let isError = false;
79
- let isEnd = false;
80
- let readme = null;
81
- let specName = null;
82
-
135
+ let isFound = false;
136
+ let foundConfigPath = configPath;
83
137
  files.forEach(function (filename) {
84
138
  if (filename === HOLDER_NAME) {
85
- isError = true;
86
- }
87
- if (filename.toLowerCase() === README_NAME) {
88
- readme = filename;
89
- }
90
- if (filename.endsWith(BOX_SPEC_NAME) || filename.endsWith(POD_SPEC_NAME)) {
91
- const dotIndex = filename.lastIndexOf('.');
92
- const ext = filename.substr(dotIndex);
93
-
94
- if (ext === BOX_SPEC_NAME || ext === POD_SPEC_NAME) {
95
- specName = filename;
96
- isEnd = true;
97
- }
139
+ isFound = true;
140
+ if (!foundConfigPath) {
141
+ // 第一次找到配置文件,记录路径并调用回调
142
+ foundConfigPath = path.resolve(filePath);
143
+ callback(path.join(filePath, filename));
144
+ }
98
145
  }
99
146
  });
100
147
 
101
- if (isEnd && specName) {
102
- callback(filePath, specName, readme);
103
- return;
148
+ // 如果已经找到配置文件,检查是否已经向上查找了两层
149
+ if (foundConfigPath) {
150
+ if (!isFound) {
151
+ // 当前目录不是配置文件所在目录,说明已经向上查找了
152
+ levelsUp++;
153
+ }
154
+
155
+ // 如果已经向上查找了两层,停止查找
156
+ if (levelsUp >= 2) {
157
+ return;
158
+ }
104
159
  }
105
160
 
106
- if (isError) {
107
- console.log('先找到了 AutoSnippet.boxspec.json 文件,请检查路径。');
108
- return;
109
- }
161
+ // 如果还没找到配置文件,继续向上查找
162
+ if (!foundConfigPath) {
163
+ // 继续向上查找
164
+ const parentPath = path.join(filePath, '/..');
165
+ const parentResolvedPath = path.resolve(parentPath);
166
+
167
+ // 如果已经到达根目录,停止查找
168
+ if (parentResolvedPath === path.resolve(filePath)) {
169
+ console.log('未找到 AutoSnippet.boxspec.json 文件,请检查路径。');
170
+ return;
171
+ }
110
172
 
111
- if (!isEnd && filePath !== '/' && filePath !== '/Users' && filePath !== '/LocalModule') {
112
- findBPSpacPath(path.join(filePath, '/..'), callback);
173
+ findASSpecPath(parentPath, callback, foundConfigPath, 0);
113
174
  } else {
114
- console.log('未找到 .boxspec 文件,请检查路径。');
175
+ // 已经找到配置文件,继续向上查找(最多两层)
176
+ const parentPath = path.join(filePath, '/..');
177
+ const parentResolvedPath = path.resolve(parentPath);
178
+
179
+ // 如果已经到达根目录,停止查找
180
+ if (parentResolvedPath === path.resolve(filePath)) {
181
+ return;
182
+ }
183
+
184
+ findASSpecPath(parentPath, callback, foundConfigPath, levelsUp);
115
185
  }
116
186
  });
117
187
  }
118
188
 
119
- // 向下查找模块默认头文件
120
- async function findSubHeaderPath(filePath, headerName) {
121
- let resultPath = null;
189
+ // 向上查找 Package.swift 文件(SPM 模块规范文件)
190
+ async function findPackageSwiftPath(filePath) {
191
+ let currentPath = path.resolve(filePath);
192
+
193
+ while (currentPath) {
194
+ try {
195
+ const entries = await getDirectoryEntries(currentPath);
196
+ if (!entries) {
197
+ const parentPath = path.dirname(currentPath);
198
+ if (parentPath === currentPath) {
199
+ break;
200
+ }
201
+ currentPath = parentPath;
202
+ continue;
203
+ }
204
+
205
+ // 查找 Package.swift 文件
206
+ for (const entry of entries) {
207
+ if (entry.isFile() && entry.name === PACKAGE_SWIFT) {
208
+ return path.join(currentPath, entry.name);
209
+ }
210
+ }
211
+
212
+ // 继续向上查找
213
+ const parentPath = path.dirname(currentPath);
214
+ if (parentPath === currentPath) {
215
+ break;
216
+ }
217
+ currentPath = parentPath;
218
+
219
+ } catch (err) {
220
+ if (err.code === 'ENOENT' || err.code === 'EACCES') {
221
+ const parentPath = path.dirname(currentPath);
222
+ if (parentPath === currentPath) {
223
+ break;
224
+ }
225
+ currentPath = parentPath;
226
+ continue;
227
+ }
228
+ throw err;
229
+ }
230
+ }
231
+
232
+ return null;
233
+ }
122
234
 
235
+ /**
236
+ * 解析 Package.swift 文件,提取模块信息
237
+ */
238
+ async function parsePackageSwift(packagePath) {
123
239
  try {
124
- const files = fs.readdirSync(filePath);
125
-
126
- for (let i = 0; i < files.length; i++) {
127
- const filename = files[i];
128
- const filedir = path.join(filePath, filename);
240
+ const content = await fs.promises.readFile(packagePath, 'utf8');
241
+
242
+ // 简单解析 Package.swift(实际可以使用 Swift AST 解析器)
243
+ const packageNameMatch = content.match(/name:\s*"([^"]+)"/);
244
+ const targetsMatch = content.match(/\.target\s*\([^)]+name:\s*"([^"]+)"/g);
245
+
246
+ const packageName = packageNameMatch ? packageNameMatch[1] : null;
247
+ const targets = [];
248
+
249
+ if (targetsMatch) {
250
+ targetsMatch.forEach(match => {
251
+ const targetMatch = match.match(/name:\s*"([^"]+)"/);
252
+ if (targetMatch) {
253
+ targets.push(targetMatch[1]);
254
+ }
255
+ });
256
+ }
257
+
258
+ return {
259
+ name: packageName,
260
+ targets: targets,
261
+ path: path.dirname(packagePath)
262
+ };
263
+ } catch (err) {
264
+ console.error('Error parsing Package.swift:', err);
265
+ return null;
266
+ }
267
+ }
129
268
 
130
- if (filename === (headerName + '.h')) {
131
- resultPath = filedir;
132
- break;
133
- } else {
269
+ // 向下查找模块头文件(优化:适配 SPM 结构,优先查找 include/ModuleName/ 目录)
270
+ async function findSubHeaderPath(filePath, headerName, moduleName) {
271
+ // ✅ 优先查找 include/ModuleName/ 目录(SPM 结构)
272
+ if (moduleName) {
273
+ const includePath = path.join(filePath, 'include', moduleName);
274
+ try {
275
+ const stats = await fs.promises.stat(includePath);
276
+ if (stats.isDirectory()) {
277
+ const headerPath = path.join(includePath, `${headerName}.h`);
134
278
  try {
135
- // 读取路径是否为文件
136
- const stats = fs.lstatSync(filedir);
137
- const isDirectory = stats.isDirectory();
138
- if (isDirectory) {
139
- const result = await findSubHeaderPath(filedir, headerName);
140
- if (result) {
141
- resultPath = result;
142
- }
143
- }
144
- } catch (err) {
145
- console.error(err);
279
+ await fs.promises.access(headerPath);
280
+ return headerPath;
281
+ } catch {
282
+ // 头文件不存在,继续查找
283
+ }
284
+ }
285
+ } catch {
286
+ // include 目录不存在,继续查找
287
+ }
288
+ }
289
+
290
+ // ✅ 降级:在整个模块目录中查找(深度限制)
291
+ try {
292
+ const entries = await getDirectoryEntries(filePath);
293
+ if (!entries) {
294
+ return null;
295
+ }
296
+
297
+ for (const entry of entries) {
298
+ if (entry.isFile() && entry.name === `${headerName}.h`) {
299
+ return path.join(filePath, entry.name);
300
+ } else if (entry.isDirectory()) {
301
+ // 跳过 node_modules、.git 等目录
302
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') {
303
+ continue;
304
+ }
305
+
306
+ const result = await findSubHeaderPath(path.join(filePath, entry.name), headerName, null);
307
+ if (result) {
308
+ return result;
146
309
  }
147
310
  }
148
311
  }
149
312
  } catch (err) {
150
- console.log(err);
313
+ console.error(err);
151
314
  }
152
- return resultPath;
315
+
316
+ return null;
153
317
  }
154
318
 
155
- // 向下查找AutoSnippet配置文件
319
+ // 向下查找AutoSnippet配置文件(优化:异步 I/O)
156
320
  async function findSubASSpecPath(filePath) {
157
321
  let resultArray = [];
158
322
 
159
323
  try {
160
- const files = fs.readdirSync(filePath);
161
-
162
- for (let i = 0; i < files.length; i++) {
163
- const filename = files[i];
164
- const filedir = path.join(filePath, filename);
324
+ const entries = await getDirectoryEntries(filePath);
325
+ if (!entries) {
326
+ return resultArray;
327
+ }
165
328
 
166
- if (filename === HOLDER_NAME) {
167
- resultArray.push(filedir);
168
- } else {
169
- try {
170
- // 读取路径是否为文件
171
- const stats = fs.lstatSync(filedir);
172
- const isDirectory = stats.isDirectory();
173
- if (isDirectory) {
174
- const array = await findSubASSpecPath(filedir);
175
- resultArray = resultArray.concat(array);
176
- }
177
- } catch (err) {
178
- console.error(err);
329
+ for (const entry of entries) {
330
+ if (entry.isFile() && entry.name === HOLDER_NAME) {
331
+ resultArray.push(path.join(filePath, entry.name));
332
+ } else if (entry.isDirectory()) {
333
+ // 跳过 node_modules、.git 等目录
334
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') {
335
+ continue;
179
336
  }
337
+
338
+ const array = await findSubASSpecPath(path.join(filePath, entry.name));
339
+ resultArray = resultArray.concat(array);
180
340
  }
181
341
  }
182
342
  } catch (err) {
183
343
  console.log(err);
184
344
  }
345
+
185
346
  return resultArray;
186
347
  }
187
348
 
188
349
  exports.findASSpecPath = findASSpecPath;
189
- exports.findBPSpacPath = findBPSpacPath;
190
- exports.findPCHPath = findPCHPath;
350
+ exports.findASSpecPathAsync = findASSpecPathAsync;
351
+ exports.findPackageSwiftPath = findPackageSwiftPath;
352
+ exports.parsePackageSwift = parsePackageSwift;
191
353
  exports.findSubHeaderPath = findSubHeaderPath;
192
- exports.findSubASSpecPath = findSubASSpecPath;
354
+ exports.findSubASSpecPath = findSubASSpecPath;
package/bin/injection.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const fs = require('fs');
4
+ const fsPromises = require('fs').promises;
4
5
  const path = require('path');
5
6
  const readline = require('readline');
6
7
  const findPath = require('./findPath.js');
@@ -86,65 +87,23 @@ function handleModuleHeader(specFile, updateFile, header, importArray, isOuter)
86
87
  const headName = isOuter ? header.name : header.headerStrName;
87
88
  const moduleName = isOuter ? header.specName : header.moduleStrName;
88
89
 
89
- let isNeedFindPCH = true;
90
-
90
+ // 检查是否已经引入头文件
91
91
  for (let i = 0; i < importArray.length; i++) {
92
92
  const importHeader = importArray[i].split(importMark)[1].trim();
93
93
 
94
94
  if (importHeader === headName) {
95
95
  // 已经引入头文件
96
96
  handelAddHeaderStatus(specFile, updateFile, header, true, false);
97
- isNeedFindPCH = false;
98
- break;
97
+ return;
99
98
  } else if (importHeader === moduleName) {
100
99
  // 已经引入spec头文件
101
100
  handelAddHeaderStatus(specFile, updateFile, header, false, true);
102
- isNeedFindPCH = false;
103
- break;
101
+ return;
104
102
  }
105
103
  }
106
104
 
107
- if (isNeedFindPCH) {
108
- // 检查被修改文件的.pch文件是否引入了spec头文件
109
- const slashIndex = updateFile.lastIndexOf('/');
110
- const thePath = updateFile.substring(0, slashIndex + 1);
111
-
112
- findPath.findPCHPath(thePath, function (success, pchName) {
113
- if (success) {
114
- try {
115
- // 读取.pch文件
116
- const data = fs.readFileSync(pchName, 'utf8');
117
- const lineArray = data.split('\n');
118
-
119
- let isAddedHeader = false;
120
- let isAddedSpecHeader = false;
121
-
122
- lineArray.forEach(element => {
123
- const lineVal = element.trim();
124
-
125
- if (importReg.test(lineVal)) {
126
- const importHeader = lineVal.split(importMark)[1].trim();
127
-
128
- if (importHeader === header.name
129
- || importHeader === header.headerStrName) {
130
- isAddedHeader = true;
131
- } else if (importHeader === header.specName
132
- || importHeader === header.moduleStrName) {
133
- isAddedSpecHeader = true;
134
- }
135
- }
136
- });
137
-
138
- handelAddHeaderStatus(specFile, updateFile, header, isAddedHeader, isAddedSpecHeader);
139
- } catch (err) {
140
- console.error(err);
141
- }
142
- } else {
143
- // 没有找到pch文件
144
- addHeaderToFile(updateFile, header);
145
- }
146
- });
147
- }
105
+ // 没有找到已引入的头文件,直接添加
106
+ addHeaderToFile(updateFile, header);
148
107
  }
149
108
 
150
109
  function handelAddHeaderStatus(specFile, updateFile, header, isAddedHeader, isAddedSpecHeader) {
@@ -204,47 +163,57 @@ function isAddedToSpecHeader(specFile, header, callback) {
204
163
 
205
164
  function removeMarkFromFile(updateFile, header, string) {
206
165
  readStream(updateFile, null, importMark);
207
- checkDependency(updateFile, header.moduleName, string);
166
+ checkDependency(updateFile, header.moduleName, string).catch(err => {
167
+ console.error('Error checking dependency:', err);
168
+ });
208
169
  }
209
170
 
210
171
  function addHeaderToFile(updateFile, header) {
211
172
  readStream(updateFile, importMark + ' ' + header.name, importMark);
212
- checkDependency(updateFile, header.moduleName, '自动注入头文件完成。');
173
+ checkDependency(updateFile, header.moduleName, '自动注入头文件完成。').catch(err => {
174
+ console.error('Error checking dependency:', err);
175
+ });
213
176
  }
214
177
 
215
- function checkDependency(updateFile, moduleName, string) {
178
+ async function checkDependency(updateFile, moduleName, string) {
216
179
  const slashIndex = updateFile.lastIndexOf('/');
217
180
  const thePath = updateFile.substring(0, slashIndex + 1);
218
181
 
219
- findPath.findBPSpacPath(thePath, function (findFilePath, specName, readme) {
220
- const specPath = path.join(findFilePath, specName);
221
-
222
- try {
223
- // 读取当前工作空间的.spec文件
224
- const data = fs.readFileSync(specPath, 'utf8');
225
- const lineArray = data.split('\n');
226
-
227
- let isHaveDependency = false;
228
-
229
- lineArray.forEach(element => {
230
- const lineVal = element.trim();
182
+ try {
183
+ // 使用 SPM 的 Package.swift 查找(替代 .boxspec)
184
+ const packagePath = await findPath.findPackageSwiftPath(thePath);
185
+ if (!packagePath) {
186
+ displayNotification(string);
187
+ return;
188
+ }
231
189
 
232
- if (!lineVal.startsWith('#')
233
- && lineVal.includes('dependency')
234
- && lineVal.includes(moduleName)) {
235
- isHaveDependency = true;
236
- }
237
- });
190
+ // 解析 Package.swift 获取模块信息
191
+ const packageInfo = await findPath.parsePackageSwift(packagePath);
192
+ if (!packageInfo) {
193
+ displayNotification(string);
194
+ return;
195
+ }
238
196
 
239
- if (isHaveDependency) {
240
- displayNotification(string);
241
- } else {
242
- displayNotification(string + '\nspec文件未发现依赖项,请检查模块是否引入。');
243
- }
244
- } catch (err) {
245
- console.error(err);
197
+ // 检查依赖关系(在 Package.swift 的 dependencies 中查找)
198
+ const packageContent = await fs.promises.readFile(packagePath, 'utf8');
199
+
200
+ // 检查是否在 dependencies 中
201
+ const dependencyPattern = new RegExp(`\\.package\\([^)]*"([^"]*${moduleName}[^"]*)"`, 'g');
202
+ const targetDependencyPattern = new RegExp(`\\.product\\([^)]*name:\\s*"([^"]*${moduleName}[^"]*)"`, 'g');
203
+
204
+ const hasDependency = dependencyPattern.test(packageContent) ||
205
+ targetDependencyPattern.test(packageContent) ||
206
+ packageInfo.targets.includes(moduleName);
207
+
208
+ if (hasDependency) {
209
+ displayNotification(string);
210
+ } else {
211
+ displayNotification(string + '\nPackage.swift 未发现依赖项,请检查模块是否引入。');
246
212
  }
247
- });
213
+ } catch (err) {
214
+ console.error(err);
215
+ displayNotification(string);
216
+ }
248
217
  }
249
218
 
250
219
  function readStream(filePath, headerName, currImportMark) {