autosnippet 1.1.23 → 1.2.1

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/README.md CHANGED
@@ -12,16 +12,24 @@ $ npm install -g autosnippet
12
12
 
13
13
  请在当前 Xcode 项目文件目录下使用以下所有命令。
14
14
 
15
- ### init
15
+ ### root
16
16
 
17
17
  在 Xcode 项目的根目录执行此命令以创建工作空间:
18
18
 
19
19
  ```bash
20
- $ asd init
20
+ $ asd root
21
21
  ```
22
22
 
23
23
  创建工作空间时,会将子工作空间的 Snippet 配置信息收集到当前工作空间。
24
24
 
25
+ ### init
26
+
27
+ 在 Xcode 项目的spm模块目录执行此命令以创建模块工作空间:
28
+
29
+ ```bash
30
+ $ asd init
31
+ ```
32
+
25
33
  ### create
26
34
 
27
35
  创建 Xcode 代码片段的命令,在标记有 `// ACode` 代码的文件目录中:
@@ -117,6 +125,4 @@ Xcode 会检测 `<#` 和 `#>` 标记,并将它们之间的文本作为占位
117
125
 
118
126
  1. 选择一个占位符
119
127
  2. `⌥⌘E` 选择下一个占位符,`⌥⇧⌘E` 选择上一个占位符
120
- 3. 输入修改的内容,所有选中的占位符都会被修改
121
-
122
- 感谢使用。
128
+ 3. 输入修改的内容,所有选中的占位符都会被修改
package/bin/asnip.js CHANGED
@@ -181,13 +181,33 @@ commander
181
181
  // ✅ 使用异步版本查找配置文件
182
182
  const specFile = await findPath.findASSpecPathAsync(CMD_PATH);
183
183
  if (!specFile) {
184
- console.error('未找到 AutoSnippet.boxspec.json 配置文件');
184
+ console.error(' 安装失败:未找到 AutoSnippet.boxspec.json 配置文件');
185
+ console.error('请先执行 asd init 初始化工作空间');
185
186
  return;
186
187
  }
187
188
  // ✅ 先聚合子模块配置到主配置文件
188
189
  await init.mergeSubSpecs(specFile);
189
190
  // 然后安装 snippets
190
- install.addCodeSnippets(specFile);
191
+ const result = install.addCodeSnippets(specFile);
192
+
193
+ if (result && result.success) {
194
+ if (result.count) {
195
+ console.log(`✅ 安装成功:已安装 ${result.count} 个代码片段`);
196
+ } else if (result.successCount !== undefined) {
197
+ const total = result.total || 0;
198
+ const success = result.successCount || 0;
199
+ const error = result.errorCount || 0;
200
+ if (error === 0) {
201
+ console.log(`✅ 安装成功:已安装 ${success} 个代码片段`);
202
+ } else {
203
+ console.log(`⚠️ 安装完成:成功 ${success} 个,失败 ${error} 个,共 ${total} 个`);
204
+ }
205
+ } else {
206
+ console.log('✅ 安装成功');
207
+ }
208
+ } else {
209
+ console.error('❌ 安装失败:', result?.error || '未知错误');
210
+ }
191
211
  });
192
212
 
193
213
  commander
package/bin/create.js CHANGED
@@ -507,6 +507,8 @@ function removeAcodeMark(filePath, positionList) {
507
507
  }
508
508
 
509
509
  function escapeString(string) {
510
+ // 必须先转义 &,否则会把 &lt; 转成 &amp;lt;
511
+ string = string.replace(/&/g, '&amp;');
510
512
  string = string.replace(/</g, '&lt;');
511
513
  string = string.replace(/>/g, '&gt;');
512
514
  return string;
package/bin/install.js CHANGED
@@ -74,13 +74,18 @@ function writeSingleSnippet(snippet, template) {
74
74
  let turnValue = '';
75
75
 
76
76
  for (var index = 0; index < value.length; index++) {
77
+ // ✅ 对代码内容进行特殊字符转义
78
+ const escapedLine = escapeString(value[index]);
77
79
  if (index === 0) {
78
- turnValue += value[index] + '\n';
80
+ turnValue += escapedLine + '\n';
79
81
  } else {
80
- turnValue += '\t' + value[index] + '\n';
82
+ turnValue += '\t' + escapedLine + '\n';
81
83
  }
82
84
  }
83
85
  value = turnValue;
86
+ } else {
87
+ // ✅ 对非数组值也进行转义(如 summary)
88
+ value = escapeString(value);
84
89
  }
85
90
  tempVal = '\t<string>' + value + '</string>\n';
86
91
  }
@@ -118,8 +123,8 @@ function addCodeSnippets(specFile, singleSnippet) {
118
123
  template = JSON.parse(data);
119
124
  }
120
125
  } catch (err) {
121
- console.error(err);
122
- return;
126
+ console.error('安装失败:无法读取模板文件', err.message);
127
+ return { success: false, error: err.message };
123
128
  }
124
129
 
125
130
  // ✅ 如果指定了单个代码片段,只处理这个片段
@@ -134,7 +139,7 @@ function addCodeSnippets(specFile, singleSnippet) {
134
139
  } catch (err) {
135
140
  console.error(err);
136
141
  }
137
- return;
142
+ return { success: true, count: 1 };
138
143
  }
139
144
 
140
145
  // 原有逻辑:处理所有代码片段(用于 install 命令)
@@ -146,8 +151,8 @@ function addCodeSnippets(specFile, singleSnippet) {
146
151
  cache.updateCache(specFile, data);
147
152
  }
148
153
  } catch (err) {
149
- console.error(err);
150
- return;
154
+ console.error('安装失败:无法读取配置文件', err.message);
155
+ return { success: false, error: err.message };
151
156
  }
152
157
 
153
158
  // 拼装配置文件
@@ -155,6 +160,8 @@ function addCodeSnippets(specFile, singleSnippet) {
155
160
  let content = '';
156
161
  let identifier = '';
157
162
  let holderArr = [];
163
+ let successCount = 0;
164
+ let errorCount = 0;
158
165
 
159
166
  placeholder.list.forEach(function (placeVal) {
160
167
  holderArr.push(placeVal);
@@ -209,13 +216,18 @@ function addCodeSnippets(specFile, singleSnippet) {
209
216
  let turnValue = '';
210
217
 
211
218
  for (var index = 0; index < value.length; index++) {
219
+ // ✅ 对代码内容进行特殊字符转义
220
+ const escapedLine = escapeString(value[index]);
212
221
  if (index === 0) {
213
- turnValue += value[index] + '\n';
222
+ turnValue += escapedLine + '\n';
214
223
  } else {
215
- turnValue += '\t' + value[index] + '\n';
224
+ turnValue += '\t' + escapedLine + '\n';
216
225
  }
217
226
  }
218
227
  value = turnValue;
228
+ } else {
229
+ // ✅ 对非数组值也进行转义(如 summary)
230
+ value = escapeString(value);
219
231
  }
220
232
  tempVal = '\t<string>' + value + '</string>\n';
221
233
  }
@@ -235,15 +247,23 @@ function addCodeSnippets(specFile, singleSnippet) {
235
247
  try {
236
248
  const snippetFile = path.join(snippetsPath, identifier + '.codesnippet');
237
249
  fs.writeFileSync(snippetFile, content);
250
+ successCount++;
238
251
  } catch (err) {
239
- console.log(err);
252
+ console.error(`安装片段失败 [${identifier}]:`, err.message);
253
+ errorCount++;
240
254
  }
241
255
  }
242
256
  });
257
+
258
+ return { success: true, successCount, errorCount, total: holderArr.length };
243
259
  }
260
+
261
+ return { success: false, error: '配置文件格式错误' };
244
262
  }
245
263
 
246
264
  function escapeString(string) {
265
+ // 必须先转义 &,否则会把 &lt; 转成 &amp;lt;
266
+ string = string.replace(/&/g, '&amp;');
247
267
  string = string.replace(/</g, '&lt;');
248
268
  string = string.replace(/>/g, '&gt;');
249
269
  return string;
package/bin/watch.js CHANGED
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
3
+ const chokidar = require('chokidar');
4
4
  const path = require('path');
5
5
  const open = require('open');
6
6
  const injection = require('./injection.js');
7
7
 
8
- // 全局路径
9
8
  const CMD_PATH = process.cwd();
10
9
  const cache = require('./cache.js');
11
10
 
@@ -14,94 +13,151 @@ const alinkMark = 'alink';
14
13
  const wellMark = '#';
15
14
  const atMark = '@';
16
15
 
17
- // ✅ 更新正则以匹配包含相对路径的新格式:// ahead <Module/Header.h> relative/path/to/Header.h
18
16
  const headerReg = /^\/\/ ahead <\w+\/\w+.h>(\s+.+)?$/;
19
17
  const headerSwiftReg = /^\/\/ ahead \w+$/;
20
18
  const importReg = /^\#import\s*<\w+\/\w+.h>$/;
21
19
  const importSwiftReg = /^import\s*\w+$/;
22
20
 
21
+ const debounceTimers = new Map();
22
+ const DEBOUNCE_DELAY = 300;
23
+
23
24
  let timeoutLink = null;
24
25
  let timeoutHead = null;
25
26
 
26
27
  function watchFileChange(specFile, watchRootPath) {
27
- // ✅ 如果指定了监听根目录,使用它;否则使用当前工作目录
28
28
  const filePath = watchRootPath || CMD_PATH;
29
- console.log(`[watchFileChange] 监听目录: ${filePath}`);
30
- console.log(`[watchFileChange] 配置文件: ${specFile}`);
31
- let isReading = false;
32
-
33
- fs.watch(filePath, {recursive: true}, (event, filename) => {
34
- if (filename) {
35
- setTimeout(() => {
36
- if (!watchFileFilter(filePath, filename)) {
37
- return;
38
- }
39
- if (isReading) {
29
+
30
+ const ignored = [
31
+ '**/node_modules/**',
32
+ '**/.git/**',
33
+ '**/.mgit/**',
34
+ '**/.easybox/**',
35
+ '**/xcuserdata/**',
36
+ '**/.build/**',
37
+ '**/*.swp',
38
+ '**/*.tmp',
39
+ '**/*~.m',
40
+ '**/*~.h',
41
+ ];
42
+
43
+ const filePattern = ['**/*.m', '**/*.h', '**/*.swift'];
44
+
45
+ console.log(`✅ 文件监听已启动: ${filePath}`);
46
+
47
+ const watcher = chokidar.watch(filePattern, {
48
+ cwd: filePath,
49
+ ignored: ignored,
50
+ ignoreInitial: true,
51
+ persistent: true,
52
+ awaitWriteFinish: {
53
+ stabilityThreshold: 300,
54
+ pollInterval: 100
55
+ }
56
+ });
57
+
58
+ watcher.on('change', (relativePath) => {
59
+ const fullPath = path.join(filePath, relativePath);
60
+ handleFileChange(specFile, fullPath, relativePath);
61
+ });
62
+
63
+ watcher.on('add', (relativePath) => {
64
+ const fullPath = path.join(filePath, relativePath);
65
+ handleFileChange(specFile, fullPath, relativePath);
66
+ });
67
+
68
+ watcher.on('error', (error) => {
69
+ console.error('文件监听错误:', error.message);
70
+ });
71
+
72
+ watcher.on('ready', () => {
73
+ console.log('文件监听器已就绪,等待文件变更...');
74
+ });
75
+
76
+ return watcher;
77
+ }
78
+
79
+ function handleFileChange(specFile, fullPath, relativePath) {
80
+ const existingTimer = debounceTimers.get(fullPath);
81
+ if (existingTimer) {
82
+ clearTimeout(existingTimer);
83
+ }
84
+
85
+ const timer = setTimeout(() => {
86
+ debounceTimers.delete(fullPath);
87
+ processFileChange(specFile, fullPath, relativePath);
88
+ }, DEBOUNCE_DELAY);
89
+
90
+ debounceTimers.set(fullPath, timer);
91
+ }
92
+
93
+ function processFileChange(specFile, updateFile, relativePath) {
94
+ const fs = require('fs');
95
+
96
+ fs.access(updateFile, fs.constants.F_OK, (err) => {
97
+ if (err) {
98
+ return;
99
+ }
100
+
101
+ fs.stat(updateFile, (statErr, stats) => {
102
+ if (statErr || stats.isDirectory()) {
103
+ return;
104
+ }
105
+
106
+ fs.readFile(updateFile, 'utf8', (readErr, data) => {
107
+ if (readErr) {
40
108
  return;
41
109
  }
42
- isReading = true;
43
-
44
- let updateFile = path.join(filePath, filename);
45
- fs.readFile(updateFile, 'utf8', (err, data) => {
46
- isReading = false;
47
- if (err) {
48
- console.error(err);
49
- return;
50
- }
51
110
 
52
- const isSwift = filename.endsWith('.swift');
53
- const currImportReg = isSwift ? importSwiftReg : importReg;
54
- const currHeaderReg = isSwift ? headerSwiftReg : headerReg;
55
-
56
- let importArray = [];
57
- let headerLine = null;
58
- let alinkLine = null;
59
-
60
- const lineArray = data.split('\n');
61
- lineArray.forEach(element => {
62
- const lineVal = element.trim();
63
-
64
- if (currImportReg.test(lineVal)) {
65
- importArray.push(lineVal);
66
- }
67
- if (lineVal.startsWith(headerMark)) {
68
- headerLine = lineVal;
69
- console.log('headerLine: ' + headerLine);
70
- }
71
- if (lineVal.startsWith(atMark) && lineVal.endsWith(wellMark + alinkMark)) {
72
- alinkLine = lineVal;
73
- console.log('alinkLine: ' + alinkLine);
74
- }
75
- });
76
-
77
- if (alinkLine) {
78
-
79
- clearTimeout(timeoutLink);
80
- timeoutLink = setTimeout(() => {
81
- openLink(specFile, alinkLine);
82
- }, 300);
83
- }
111
+ const filename = path.basename(updateFile);
112
+ const isSwift = filename.endsWith('.swift');
113
+ const currImportReg = isSwift ? importSwiftReg : importReg;
114
+ const currHeaderReg = isSwift ? headerSwiftReg : headerReg;
115
+
116
+ let importArray = [];
117
+ let headerLine = null;
118
+ let alinkLine = null;
84
119
 
85
- if (headerLine && currHeaderReg.test(headerLine)) {
120
+ const lineArray = data.split('\n');
121
+ lineArray.forEach(element => {
122
+ const lineVal = element.trim();
86
123
 
87
- clearTimeout(timeoutHead);
88
- timeoutHead = setTimeout(() => {
89
- checkAnotherFile(specFile, updateFile, headerLine, importArray, isSwift);
90
- }, 300);
124
+ if (currImportReg.test(lineVal)) {
125
+ importArray.push(lineVal);
126
+ }
127
+ if (lineVal.startsWith(headerMark)) {
128
+ headerLine = lineVal;
129
+ }
130
+ if (lineVal.startsWith(atMark) && lineVal.endsWith(wellMark + alinkMark)) {
131
+ alinkLine = lineVal;
91
132
  }
92
133
  });
93
- }, 1000);
94
- }
134
+
135
+ if (alinkLine) {
136
+ clearTimeout(timeoutLink);
137
+ timeoutLink = setTimeout(() => {
138
+ openLink(specFile, alinkLine);
139
+ }, DEBOUNCE_DELAY);
140
+ }
141
+
142
+ if (headerLine && currHeaderReg.test(headerLine)) {
143
+ clearTimeout(timeoutHead);
144
+ timeoutHead = setTimeout(() => {
145
+ checkAnotherFile(specFile, updateFile, headerLine, importArray, isSwift);
146
+ }, DEBOUNCE_DELAY);
147
+ }
148
+ });
149
+ });
95
150
  });
96
151
  }
97
152
 
98
153
  function checkAnotherFile(specFile, updateFile, headerLine, importArray, isSwift) {
154
+ const fs = require('fs');
155
+
99
156
  if (isSwift || updateFile.endsWith('.h')) {
100
157
  injection.handleHeaderLine(specFile, updateFile, headerLine, importArray, isSwift);
101
158
  return;
102
159
  }
103
160
 
104
- // 识别.h文件的引入头文件
105
161
  const dotIndex = updateFile.lastIndexOf('.');
106
162
  const mainPathFile = updateFile.substring(0, dotIndex) + '.h';
107
163
 
@@ -119,7 +175,6 @@ function checkAnotherFile(specFile, updateFile, headerLine, importArray, isSwift
119
175
  const lineArray = data.split('\n');
120
176
  lineArray.forEach(element => {
121
177
  const lineVal = element.trim();
122
-
123
178
  if (importReg.test(lineVal)) {
124
179
  importArray.push(lineVal);
125
180
  }
@@ -155,24 +210,4 @@ function openLink(specFile, inputWord) {
155
210
  }
156
211
  }
157
212
 
158
- function watchFileFilter(filePath, filename) {
159
- let updateFile = path.join(filePath, filename);
160
- if (updateFile.includes('xcuserdata')
161
- || updateFile.includes('.git')
162
- || updateFile.includes('.mgit')
163
- || updateFile.includes('.easybox')) {
164
- return false;
165
- }
166
- if (filename.endsWith('~.m')
167
- || filename.endsWith('~.h')) {
168
- return false;
169
- }
170
- if (!filename.endsWith('.m')
171
- && !filename.endsWith('.h')
172
- && !filename.endsWith('.swift')) {
173
- return false;
174
- }
175
- return true;
176
- }
177
-
178
213
  exports.watchFileChange = watchFileChange;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "1.1.23",
3
+ "version": "1.2.1",
4
4
  "description": "A iOS module management tool.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,6 +22,7 @@
22
22
  "url": "git+https://github.com/GxFn/AutoSnippet.git"
23
23
  },
24
24
  "dependencies": {
25
+ "chokidar": "^3.6.0",
25
26
  "commander": "^7.2.0",
26
27
  "inquirer": "^8.0.0",
27
28
  "minimist": "^1.2.5",