autosnippet 1.2.15 → 1.2.17

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/.editorconfig ADDED
@@ -0,0 +1,7 @@
1
+ root = true
2
+
3
+ [*.js]
4
+ indent_style = tab
5
+ indent_size = 4
6
+ tab_width = 4
7
+
@@ -0,0 +1,305 @@
1
+ ## AutoSnippet 架构与调用流程(实现解析)
2
+
3
+ 本文档用于解释 AutoSnippet 的实现方式、核心调用链、以及每个文件/模块的职责边界。当前版本已统一使用 `autosnippet:*` 指令(不再兼容旧标记)。
4
+
5
+ ---
6
+
7
+ ## 总览:分层结构
8
+
9
+ AutoSnippet 采用“薄 `bin/` + 业务下沉 `lib/`”的结构:
10
+
11
+ - **`bin/`(CLI/兼容入口层)**
12
+ - 只负责 CLI 参数解析、拼装 options、调用 `lib/` 的能力(或 re-export)
13
+ - 便于保持可执行入口稳定(`package.json` 的 bin 指向不变)
14
+
15
+ - **`lib/`(可复用能力层)**
16
+ - `infra/`:路径与缓存
17
+ - `snippet/`:snippet 构建、spec 读写、写入 Xcode CodeSnippets
18
+ - `watch/`:watcher + debounce + 事件提取
19
+ - `injection/`:指令解析、模块推断、写入 import、串联依赖补齐
20
+ - `spm/`:Package.swift 解析/修复(target/product/package)
21
+
22
+ - **`scripts/`(测试/辅助)**
23
+ - 跨目录 E2E:验证全局 asd 在任意 Xcode 项目里可用,并覆盖 SPM 自动补齐
24
+
25
+ ---
26
+
27
+ ## 核心指令(Source Directive)
28
+
29
+ ### snippet 指令:`autosnippet:code`
30
+
31
+ 用于从源码中抽取 snippet 内容:
32
+
33
+ ```txt
34
+ // autosnippet:code
35
+ ... snippet content ...
36
+ // autosnippet:code
37
+ ```
38
+
39
+ ### 注入指令:`autosnippet:include` / `autosnippet:import`
40
+
41
+ - ObjC/C/C++:注入 `#import`
42
+
43
+ ```txt
44
+ // autosnippet:include <ModuleName/Header.h> [optional/relative/path/Header.h]
45
+ ```
46
+
47
+ - Swift:注入 `import`
48
+
49
+ ```txt
50
+ // autosnippet:import ModuleName
51
+ ```
52
+
53
+ > 重要:Swift 的 `import ModuleName` 同样需要 **target 依赖**(在 SPM 中就是 `Package.swift` 的 target `dependencies`),因此 Swift 注入路径也会触发依赖补齐(见下文)。
54
+
55
+ ---
56
+
57
+ ## 调用流程 1:创建 snippet(create)
58
+
59
+ 目标:把源码/剪贴板内容生成 snippet,写入 `AutoSnippet.boxspec.json` / `AutoSnippetRoot.boxspec.json`,并安装到 Xcode CodeSnippets。
60
+
61
+ ### 1) CLI 入口
62
+
63
+ - `bin/asnip.js`
64
+ - `asd create` / `asd c`
65
+ - 支持 `--preset` / `--yes`
66
+ - 支持 `--clipboard` / `--lang objc|swift`
67
+
68
+ ### 2) 从文件标记抽取
69
+
70
+ - `bin/create.js`
71
+ - 扫描目录下 `.m/.h/.swift`
72
+ - 识别 `// autosnippet:code` 开关区间
73
+ - 组装 snippet 对象(identifier/trigger/summary/link/languageShort/body)
74
+ - 交给 `lib/snippet/specRepository.js` 落盘并触发 install
75
+
76
+ ### 3) 从剪贴板创建(clipboard)
77
+
78
+ - `bin/asnip.js` 读取剪贴板文本(平台命令)
79
+ - `bin/create.js` → `lib/snippet/snippetFactory.js`
80
+ - `snippetFactory.fromText(answers, text, { language })`
81
+
82
+ ### 4) 落盘与安装
83
+
84
+ - `lib/snippet/specRepository.js`
85
+ - `readSpecFile` / `writeSpecFile`
86
+ - `saveSnippet(specFile, snippet, { syncRoot, installSingle })`
87
+ - 同步根配置(root spec)与更新缓存
88
+
89
+ - `lib/snippet/snippetInstaller.js`
90
+ - `addCodeSnippets(specFile)`
91
+ - 写入 `~/Library/Developer/Xcode/UserData/CodeSnippets/*.codesnippet`
92
+
93
+ ---
94
+
95
+ ## 调用流程 2:watch 注入(watch)
96
+
97
+ 目标:监听文件变更,识别 `autosnippet:include/import` 或 `@xxx#alink`,然后执行注入/跳转。
98
+
99
+ ### 1) CLI 入口
100
+
101
+ - `bin/asnip.js`
102
+ - `asd watch` / `asd w`
103
+ - 过滤选项:`--path/--file/--ext`
104
+ - 输出选项:`--quiet/--summary`
105
+ - 测试选项:`--duration/--once`
106
+ - 在启动 watch 前会先 `init.mergeSubSpecs(rootSpecFile)`,保证 root spec 结构可用
107
+
108
+ ### 2) 文件监听与事件提取
109
+
110
+ - `lib/watch/fileWatcher.js`
111
+ - `watchFileChange(rootSpecFile, projectRoot, options)`
112
+ - 基于 `chokidar` 监听 `m/h/swift`(或指定 exts)
113
+ - 读取文件内容后:
114
+ - 收集当前已有 `#import` / `import` 列表(用于“已存在则不重复插入”)
115
+ - 检测是否存在 `// autosnippet:include ...` 或 `// autosnippet:import ...`
116
+ - 检测是否存在 `@xxx#alink`
117
+ - 命中后触发:
118
+ - header:`checkAnotherFile(...)` → `injection.handleHeaderLine(...)`
119
+ - alink:`openLink(...)`
120
+
121
+ > `checkAnotherFile`:当变更文件是 `.m`,会优先对同名 `.h` 做注入(保持 ObjC 的头文件习惯)。
122
+
123
+ ### 3) 注入编排(ObjC/Swift 共用入口)
124
+
125
+ - `bin/injection.js`
126
+ - 薄入口:re-export 到 `lib/injection/injectionService.js`
127
+
128
+ - `lib/injection/injectionService.js`
129
+ - 主入口:`handleHeaderLine(specFile, updateFile, headerLine, importArray, isSwift)`
130
+ - 串联以下步骤(ObjC/Swift 路径略有差异):
131
+ - 解析指令
132
+ - 推断当前 target/module
133
+ - **(跨 module 时)调用 `deps.ensureDependency()` 补齐 SPM 依赖**
134
+ - 写入 `#import` / `import` 并移除指令行
135
+
136
+ ---
137
+
138
+ ## 调用流程 3:SPM 依赖自动补齐(deps.ensureDependency)
139
+
140
+ 目标:当发现 `fromTarget -> toTarget` 缺失依赖时,按模式:
141
+ - `off`:只提示
142
+ - `suggest`:给出可复制的 patch 建议
143
+ - `fix`:直接修改 `Package.swift`
144
+
145
+ 入口来源:
146
+ - ObjC 注入:解析出 header 的 moduleName 后触发补齐
147
+ - Swift 注入:`import ModuleName` 的 moduleName 即依赖目标,同样触发补齐
148
+
149
+ ### 1) 入口与模式
150
+
151
+ - `bin/deps.js`
152
+ - 薄入口:re-export 到 `lib/spm/spmDepsService.js`
153
+
154
+ - `lib/spm/spmDepsService.js`
155
+ - `getFixMode()`:读取 `ASD_FIX_SPM_DEPS_MODE`
156
+ - `ensureDependency(specFile, packageSwiftPath, fromTarget, toTarget)`
157
+ - 先判断是否已可达 / 已存在依赖
158
+ - 若缺失:
159
+ - 同包 target → 补齐 target dependencies(`.target` / `.byName` 或 `"B"` 等)
160
+ - 跨包 product → 补齐 `.product(name:package:)`
161
+ - 若映射里包含 packageRef → 补齐顶层 `.package(...)`(`dependencies:` 参数)
162
+
163
+ ### 2) Package.swift 修改策略(AST-lite)
164
+
165
+ 核心策略是“**轻量解析 + 结构化 patch**”,避免靠简单正则误插入:
166
+
167
+ - 抽取 `Package(...)` 的 `targets` 列表 block
168
+ - 精确定位某个 target 的 `.target(...)` block
169
+ - 在该 block 的 `dependencies:` 中插入 `.product(...)` / `.target(...)` 或字符串依赖
170
+ - 若需要 `.package(...)`,精确定位 `Package(...)` 的顶层 `dependencies:` 参数
171
+ - 如果不存在 `dependencies:` 参数,则在 `targets:` 参数之前插入
172
+
173
+ ### 3) 跨包映射文件:`AutoSnippet.spmmap.json`
174
+
175
+ 当 `toTarget` 属于外部包时,需要映射告诉工具:
176
+ - `toModule` 对应哪个 product
177
+ - product 属于哪个 package
178
+ - package 的引用方式(url/path)
179
+
180
+ 该文件用于避免“猜测式补齐”,保证补齐是可解释、可复现的。
181
+
182
+ ---
183
+
184
+ ## ALink 跳转(@xxx#alink)
185
+
186
+ - `lib/watch/fileWatcher.js`
187
+ - 捕获 `@xxx#alink` 行
188
+ - `openLink(...)`:打开 link 或 README(具体链接来源来自 spec)
189
+
190
+ ---
191
+
192
+ ## 各文件职责(按目录)
193
+
194
+ ### bin/
195
+
196
+ - `bin/asnip.js`
197
+ - CLI 主入口(commander)
198
+ - 负责:命令定义、参数解析、preset/yes 统一、调用下层服务
199
+
200
+ - `bin/init.js`
201
+ - 初始化 spec、合并子 spec 到 root spec(watch 启动前依赖它做“根配置兜底”)
202
+
203
+ - `bin/create.js`
204
+ - 从 `autosnippet:code` 抽取 snippet(文件模式)
205
+ - clipboard 模式下调用 `lib/snippet/snippetFactory.js`
206
+
207
+ - `bin/install.js`
208
+ - 薄入口:re-export `lib/snippet/snippetInstaller.js`
209
+
210
+ - `bin/share.js`
211
+ - 读取本地 Xcode CodeSnippets 并写入 spec(委托 `specRepository`)
212
+
213
+ (已移除薄入口)`watch/injection/deps/install/cache/config` 等能力已直接从 `lib/` 引用,`bin/` 保持只承载 CLI 与命令实现。
214
+
215
+ - `bin/findPath.js`
216
+ - 工程路径/Package.swift 定位与解析辅助(后续可继续下沉到 `lib/spm/`)
217
+
218
+ (已移除薄入口)路径与缓存能力:`lib/infra/paths.js`、`lib/infra/cacheStore.js`
219
+
220
+ ### lib/infra/
221
+
222
+ - `lib/infra/paths.js`
223
+ - `getSnippetsPath()` / `getCachePath()`
224
+
225
+ - `lib/infra/cacheStore.js`
226
+ - 缓存读写(keys/link/head 等)
227
+ - 提供 watch/injection/create 中所需的缓存支持
228
+
229
+ ### lib/snippet/
230
+
231
+ - `lib/snippet/snippetFactory.js`
232
+ - snippet 组装(目前以 `fromText` 为主)
233
+
234
+ - `lib/snippet/specRepository.js`
235
+ - spec 文件读写、upsert、同步 root、更新缓存
236
+
237
+ - `lib/snippet/snippetInstaller.js`
238
+ - 渲染/写入 `.codesnippet` 到 Xcode 目录
239
+
240
+ ### lib/watch/
241
+
242
+ - `lib/watch/fileWatcher.js`
243
+ - chokidar + 过滤 + debounce + summary
244
+ - 解析事件并调用 injection/openLink
245
+
246
+ ### lib/injection/
247
+
248
+ - `lib/injection/directiveParser.js`
249
+ - 解析 `autosnippet:include/import`
250
+ - ObjC include 解析为 `{moduleName, headerName, headRelativePathFromMark}` 等结构
251
+
252
+ - `lib/injection/moduleResolver.js`
253
+ - 根据 `Package.swift` 的 targets 以及路径推断当前 target/module
254
+ - 根据 headRelativePath 推断头文件所属 module(ObjC 路径使用)
255
+
256
+ - `lib/injection/importWriter.js`
257
+ - 负责写入 `#import` / `import`、移除指令行、提示通知
258
+ - 负责判断“已存在则不重复”
259
+
260
+ - `lib/injection/injectionService.js`
261
+ - orchestration:解析 → 推断 → deps.ensureDependency → 写入
262
+ - Swift/ObjC 保持“功能一致”,实现方式不同(Swift 无头文件路径)
263
+
264
+ ### lib/spm/
265
+
266
+ - `lib/spm/spmDepsService.js`
267
+ - 依赖图构建与缓存(DepGraphCache)
268
+ - manifest/targets 解析、reachability 判断
269
+ - patch:target dependency / product dependency / package dependency
270
+ - 统一输出 `ensureResult`(ok/changed/changes/suggestion/reason)
271
+
272
+ ### scripts/
273
+
274
+ - `scripts/test-cross-directory.js`
275
+ - 跨目录 E2E(验证全局 asd 在任意项目可用)
276
+ - 覆盖:
277
+ - init/root/create/install/update/share/watch
278
+ - SPM 同包 target 依赖补齐
279
+ - Swift import 依赖补齐
280
+ - 跨包 product/.package 依赖补齐
281
+ - 真实项目抽样修复(并清理痕迹)
282
+
283
+ ---
284
+
285
+ ## 常见“为什么这样设计”
286
+
287
+ ### 为什么 `bin/` 要薄
288
+
289
+ - CLI 稳定,内部可重构
290
+ - 单元测试/E2E 更容易直接调用 `lib/` 能力
291
+ - 避免循环依赖(create/install/share/watch/deps 等互相引用)
292
+
293
+ ### 为什么 SPM 解析不用完整 AST
294
+
295
+ - `Package.swift` 是 Swift 代码,完整 AST 解析成本高
296
+ - 采用“AST-lite”定位结构区域并 patch,结合 E2E fixture 保证可靠性
297
+
298
+ ---
299
+
300
+ ## 扩展建议(后续演进)
301
+
302
+ - ✅ 已完成:把 `Package.swift` 解析/定位能力抽到 `lib/spm/packageParser.js`,`lib/spm` 与 `lib/injection` 不再依赖 `bin/findPath.js`
303
+ - ✅ 已完成(通知层):新增 `lib/infra/notifier.js`,统一 macOS `osascript` 通知并修复换行/引号导致的脚本错误
304
+ - 🔜 待完善(日志层):统一日志层(quiet/json),把散落的 `console.*` 收敛到一个可配置 logger(CLI 透传 quiet/json)
305
+
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AutoSnippet
2
2
 
3
- 基于SPM的iOS模块管理工具。通过AutoSnippet可以将模块的使用示范写进Xcode的配置文件,支持分类查询和头文件引入。
3
+ 基于 SPM iOS 模块 Snippet 工具。通过 AutoSnippet 可以把模块的使用示范写进 Xcode 的 CodeSnippets,并支持分类检索、链接跳转与(可选)依赖头文件注入。
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/autosnippet.svg?style=flat-square)](https://www.npmjs.com/package/autosnippet)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/autosnippet.svg?style=flat-square)](https://www.npmjs.com/package/autosnippet)
@@ -29,7 +29,34 @@ Toast模块添加配置时可以选择分类,使用者可以通过`@view`或
29
29
  $ npm install -g autosnippet
30
30
  ```
31
31
 
32
- ## 命令选项
32
+ ## 快速开始
33
+
34
+ 建议在**项目根目录**执行(能找到 `AutoSnippetRoot.boxspec.json`)。
35
+
36
+ ```bash
37
+ # 1) 一键初始化(等价于 init + root)
38
+ asd setup
39
+
40
+ # 2) 在代码里用新标记圈出 snippet 内容,然后创建
41
+ asd create
42
+
43
+ # 3) 安装到 Xcode CodeSnippets
44
+ asd install
45
+
46
+ # 4) 开启监听(用于头文件注入/依赖补齐/ALink 跳转)
47
+ asd watch
48
+ ```
49
+
50
+ ## 全局选项(推荐)
51
+
52
+ - **`--preset <path>`**:指定预置输入(非交互/自动化最常用)。
53
+ - **`--yes`**:非交互模式;缺少必要输入会直接报错退出。
54
+
55
+ 也支持环境变量(方便 CI / 测试脚本):
56
+
57
+ - **`ASD_PRESET` / `ASD_TEST_PRESET`**:预置输入 json 路径(优先级低于 `--preset`)
58
+
59
+ ## 命令
33
60
 
34
61
  请在当前 Xcode 项目文件目录下使用以下所有命令。
35
62
 
@@ -51,20 +78,47 @@ $ asd root
51
78
  $ asd init
52
79
  ```
53
80
 
81
+ ### setup(推荐)
82
+
83
+ 初始化快捷命令,等价于 `asd init` + `asd root`:
84
+
85
+ ```bash
86
+ $ asd setup
87
+ ```
88
+
54
89
  ### create
55
90
 
56
- 创建 Xcode 代码片段的命令,在标记有 `// ACode` 代码的文件目录中:
91
+ 创建 Xcode 代码片段(支持从文件标记提取,或直接从剪贴板生成)。
57
92
 
58
93
  ```bash
94
+ $ asd create
95
+ # 或短别名
59
96
  $ asd c
60
97
  ```
61
98
 
62
- 代码示例:
99
+ #### 从文件标记提取
100
+
101
+ 在任意 `.m/.h/.swift` 文件中使用标记圈出代码块(推荐短写法):
63
102
 
64
103
  ```
65
- // ACode
104
+ // as:code
66
105
  UIView *view = [[UIView alloc] init];
67
- // ACode
106
+ // as:code
107
+ ```
108
+
109
+ 然后在该文件所在目录(或通过 preset 指定文件)执行 `asd create`。
110
+
111
+ #### 从剪贴板创建
112
+
113
+ ```bash
114
+ # 默认按 objc 处理
115
+ asd create --clipboard
116
+
117
+ # 短写法
118
+ asd c -p
119
+
120
+ # Swift
121
+ asd create --clipboard --lang swift
68
122
  ```
69
123
 
70
124
  ### install
@@ -72,6 +126,8 @@ UIView *view = [[UIView alloc] init];
72
126
  将共享的代码片段添加到 Xcode 环境:
73
127
 
74
128
  ```bash
129
+ $ asd install
130
+ # 或短别名
75
131
  $ asd i
76
132
  ```
77
133
 
@@ -87,17 +143,45 @@ $ asd i
87
143
  共享本地代码片段:
88
144
 
89
145
  ```bash
146
+ $ asd share
147
+ # 或短别名
90
148
  $ asd s
91
149
  ```
92
150
 
151
+ ### update
152
+
153
+ 更新已创建的 snippet(按 trigger 查找,例如 `cover` 或 `@cover`):
154
+
155
+ ```bash
156
+ asd update <word> [key] [value]
157
+ # 或短别名
158
+ asd u <word> [key] [value]
159
+ ```
160
+
93
161
  ### watch
94
162
 
95
- 在模块化项目中,识别代码片段并自动注入依赖头文件:
163
+ 在模块化项目中监听文件变更,识别 `autosnippet:*` 指令并执行:
164
+ - 头文件注入(ObjC `#import` / Swift `import`)
165
+ - ALink 跳转
166
+ -(可选)SPM 依赖自动补齐(见下文)
96
167
 
97
168
  ```bash
169
+ $ asd watch
170
+ # 或短别名
98
171
  $ asd w
99
172
  ```
100
173
 
174
+ 常用参数:
175
+
176
+ ```bash
177
+ # 只监听某个子目录/文件/后缀
178
+ asd watch --path Services/Services/ASNetworkCheck --ext m,h
179
+ asd watch --file ./Services/Services/ASNetworkCheck/Code/ASSimplePing.m
180
+
181
+ # 降噪/退出时打印汇总
182
+ asd watch --quiet --summary
183
+ ```
184
+
101
185
  #### 追加头文件
102
186
 
103
187
  开启监听后,如果想要追加头文件,请执行以下操作:
@@ -108,6 +192,20 @@ $ asd w
108
192
 
109
193
  在 1 秒内,头文件会自动添加到文件头部。
110
194
 
195
+ #### 新指令格式(重要)
196
+
197
+ - ObjC / C / C++:注入头文件
198
+
199
+ ```
200
+ // as:include <ModuleName/Header.h> [optional/relative/path/Header.h]
201
+ ```
202
+
203
+ - Swift:注入 import
204
+
205
+ ```
206
+ // as:import ModuleName
207
+ ```
208
+
111
209
  #### 浏览器查看
112
210
 
113
211
  开启监听后,如果想要在浏览器中查看模块的更多信息,请执行以下操作:
@@ -124,6 +222,18 @@ $ asd w
124
222
  @view#ALink
125
223
  ```
126
224
 
225
+ ## SPM 依赖自动补齐(可选)
226
+
227
+ 当 `watch` 触发跨 target 引用时,AutoSnippet 可以(按配置)检查/补齐 `Package.swift` 里的依赖关系(target / product / package)。
228
+
229
+ - 开关:通过环境变量控制
230
+ - **`ASD_FIX_SPM_DEPS_MODE=off`**:只提示(默认行为)
231
+ - **`ASD_FIX_SPM_DEPS_MODE=suggest`**:输出可复制的补丁建议
232
+ - **`ASD_FIX_SPM_DEPS_MODE=fix`**:直接修改 `Package.swift` 自动补齐
233
+
234
+ - 跨包 product/package 依赖需要映射文件(项目内维护):
235
+ - `AutoSnippet.spmmap.json`
236
+
127
237
  ## 其他
128
238
 
129
239
  ### 占位符快捷键