@edmund32/edx-cli 0.1.2

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 ADDED
@@ -0,0 +1,372 @@
1
+ # @edmund/edx-cli
2
+
3
+ 跨平台 Edmund 指令包。安装后会提供这些命令:
4
+
5
+ ```bash
6
+ edx
7
+ edx-app
8
+ edx-alias
9
+ edx-hw
10
+ edx-image
11
+ edx-md
12
+ ```
13
+
14
+ ## 安装
15
+
16
+ 从 npm 官方仓库安装:
17
+
18
+ ```bash
19
+ npm install -g @edmund/edx-cli
20
+ ```
21
+
22
+ `-g` 表示安装到 npm 全局目录。npm 会读取 `package.json` 里的 `bin` 字段,把 `edx`、`edx-image` 等命令链接到全局 bin 目录,所以安装后可以在任意目录直接使用。
23
+
24
+ 依赖会一起安装。比如本包依赖 `sharp`,执行 `npm install -g ...` 时 npm 会自动下载依赖,用户不需要再进入包目录单独 `npm install`。
25
+
26
+ 查看全局安装位置:
27
+
28
+ ```bash
29
+ npm root -g
30
+ npm prefix -g
31
+ ```
32
+
33
+ ### `npm install -g` 做了什么
34
+
35
+ 以这个命令为例:
36
+
37
+ ```bash
38
+ npm install -g @edmund/edx-cli
39
+ ```
40
+
41
+ npm 会做以下几步:
42
+
43
+ 1. 到 npm registry 查询 `@edmund/edx-cli` 的包信息,包括最新版本、tarball 下载地址、依赖列表和 `bin` 命令配置。
44
+ 2. 下载包的 `.tgz` 压缩包。npm 包本质上就是一个 tarball,例如 `edmund-edx-cli-0.1.0.tgz`。
45
+ 3. 解压 `.tgz` 到全局 `node_modules` 目录。
46
+ 4. 读取包里的 `package.json`,安装 `dependencies`。本包里的 `sharp` 会在这一步自动下载。
47
+ 5. 读取 `package.json` 的 `bin` 字段,生成全局命令入口。
48
+
49
+ 本包的 `bin` 字段类似:
50
+
51
+ ```json
52
+ {
53
+ "bin": {
54
+ "edx": "bin/edx.js",
55
+ "edx-app": "bin/edx-app.js",
56
+ "edx-alias": "bin/edx-alias.js",
57
+ "edx-hw": "bin/edx-hw.js",
58
+ "edx-image": "bin/edx-image.js",
59
+ "edx-md": "bin/edx-md.js"
60
+ }
61
+ }
62
+ ```
63
+
64
+ 所以 npm 会把 `edx`、`edx-alias`、`edx-image` 等命令链接到全局 bin 目录。这个目录通常在系统 `PATH` 环境变量中,因此可以在任意目录直接执行:
65
+
66
+ ```bash
67
+ edx-image mkdir demo
68
+ ```
69
+
70
+ 全局安装后的文件大致是:
71
+
72
+ ```text
73
+ <npm全局node_modules>/@edmund/edx-cli/
74
+ bin/
75
+ src/
76
+ vendor/
77
+ package.json
78
+ node_modules/
79
+ ```
80
+
81
+ 命令入口大致是:
82
+
83
+ ```text
84
+ <npm全局bin>/edx-image -> <npm全局node_modules>/@edmund/edx-cli/bin/edx-image.js
85
+ ```
86
+
87
+ Windows 上 npm 通常会生成 `.cmd` 和 `.ps1` 启动脚本,例如:
88
+
89
+ ```text
90
+ edx-image.cmd
91
+ edx-image.ps1
92
+ ```
93
+
94
+ 所以 PowerShell 和 CMD 也能直接执行:
95
+
96
+ ```powershell
97
+ edx-image mkdir demo
98
+ ```
99
+
100
+ 如果安装的是本地 tgz:
101
+
102
+ ```bash
103
+ npm install -g ./edmund-edx-cli-0.1.0.tgz
104
+ ```
105
+
106
+ 流程也基本一样,只是主包不是从 npm registry 下载,而是从本地 `.tgz` 读取;依赖仍会根据 `package.json` 自动安装。
107
+
108
+ ## 使用
109
+
110
+ 查看顶层入口:
111
+
112
+ ```bash
113
+ edx -a
114
+ ```
115
+
116
+ 创建图片项目目录:
117
+
118
+ ```bash
119
+ edx-image mkdir demo
120
+ ```
121
+
122
+ 按行列切分图片或目录:
123
+
124
+ ```bash
125
+ edx-image split grid ./target --rows 3 --cols 3
126
+ ```
127
+
128
+ 输出会生成在输入目录同级的 `target_output/` 中。单张 `A.png` 会输出到:
129
+
130
+ ```text
131
+ target_output/A/A_001.png
132
+ target_output/A/A_002.png
133
+ ...
134
+ ```
135
+
136
+ 递归目录会镜像为 `_output` 目录,例如 `target/A/*.png` 输出到:
137
+
138
+ ```text
139
+ target_output/A_output/
140
+ ```
141
+
142
+ 按比例坐标裁切区域:
143
+
144
+ ```bash
145
+ edx-image split region ./target --from 0,0 --to 0.5,0.5
146
+ ```
147
+
148
+ `region` 坐标使用 `0-1` 比例,原点在左下角。`--from` 是左下角,`--to` 是右上角。
149
+
150
+ 右半边裁切不需要单独命令,直接用:
151
+
152
+ ```bash
153
+ edx-image split region ./target --from 0.5,0 --to 1,1
154
+ ```
155
+
156
+ 拼接文件夹图片:
157
+
158
+ ```bash
159
+ edx-image merge ./target --rows 3
160
+ ```
161
+
162
+ 输出会生成在输入目录同级的 `target_merged/` 中。`merge` 会递归处理 `target` 下面每个“直接包含图片”的文件夹,但结果目录只保留一层:
163
+
164
+ ```text
165
+ target/001.png -> target_merged/target.png
166
+ target/A/001.png -> target_merged/A.png
167
+ target/B/001.png -> target_merged/B.png
168
+ ```
169
+
170
+ 如果 `target` 根目录本身有图片,会单独拼成 `target_merged/target.png`。如果子文件夹也有图片,每个子文件夹会各自拼成一张图片并扁平放入 `target_merged/`。
171
+
172
+ 固定行列拼接:
173
+
174
+ ```bash
175
+ edx-image merge ./target --rows 3 --cols 3
176
+ ```
177
+
178
+ 图片按文件名排序,按行优先放置:第一行是第 1、2、3 张,第二行是第 4、5、6 张。图片少于固定格子数时用黑色背景占位,图片多于固定格子数时只取排序靠前的图片。
179
+
180
+ 指定每个格子的尺寸:
181
+
182
+ ```bash
183
+ edx-image merge ./target --rows 3 --cols 3 --cellw 512 --cellh 512
184
+ ```
185
+
186
+ 如果不传 `--cellw/--cellh`,每个文件夹会使用该文件夹第一张图片的尺寸作为格子尺寸;如果只传其中一个,另一个会使用相同数值。输出统一为 `png`。
187
+
188
+ 复制 Markdown 资源:
189
+
190
+ ```bash
191
+ edx-md copy old.md new.md
192
+ ```
193
+
194
+ 检查鸿蒙相关环境:
195
+
196
+ ```bash
197
+ edx-hw show
198
+ ```
199
+
200
+ 检查应用入口:
201
+
202
+ ```bash
203
+ edx-app show
204
+ edx-app ls
205
+ edx-app run "Visual Studio Code"
206
+ edx-app run code
207
+ edx-app run "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk"
208
+ ```
209
+
210
+ 注册并打开本机常用目录或文件:
211
+
212
+ ```bash
213
+ edx-alias add target /some/deep/path --type=dir
214
+ edx-alias update target /some/new/path --type=dir
215
+ edx-alias add note /some/deep/note.txt --type=file
216
+ edx-alias update note /some/new/note.txt --type=file --app TextEdit
217
+ edx-alias open target
218
+ edx-alias open note
219
+ edx-alias open note --app "Visual Studio Code"
220
+ edx-alias ls
221
+ edx-alias remove target
222
+ edx-app ls
223
+ ```
224
+
225
+ `edx-app ls` 会列出可直接启动的精确应用值:macOS 输出 `.app` 名称,Ubuntu/Linux 输出 `.desktop` 的可执行命令,Windows 输出开始菜单 `.lnk` 路径。拿到这些值后,可以用 `edx-app run <APP>` 直接打开应用,也可以把同样的值传给 `edx-alias open --app`。
226
+
227
+ `edx-alias` 不内置任何固定快捷项。用户全局安装 npm 包后,自己把本机目录或文件注册到 `~/.edx/aliases.json`。添加和更新记录时必须带 `--type=dir` 或 `--type=file`;文件快捷项可以在注册时通过 `--app` 绑定默认打开应用,也可以在 `open` 时用 `--app` 临时覆盖。`edx-alias ls` 会按 `target / path / type` 三列展示记录,并按类型给 `type` 上色。
228
+
229
+ ## 更新
230
+
231
+ 更新到远端最新版:
232
+
233
+ ```bash
234
+ npm install -g @edmund/edx-cli@latest
235
+ ```
236
+
237
+ 也可以使用:
238
+
239
+ ```bash
240
+ npm update -g @edmund/edx-cli
241
+ ```
242
+
243
+ 查看本机已安装版本:
244
+
245
+ ```bash
246
+ npm list -g @edmund/edx-cli
247
+ ```
248
+
249
+ 查看远端最新版本:
250
+
251
+ ```bash
252
+ npm view @edmund/edx-cli version
253
+ ```
254
+
255
+ 卸载:
256
+
257
+ ```bash
258
+ npm uninstall -g @edmund/edx-cli
259
+ ```
260
+
261
+ ## 开发
262
+
263
+ 安装开发依赖:
264
+
265
+ ```bash
266
+ npm install
267
+ ```
268
+
269
+ 运行测试:
270
+
271
+ ```bash
272
+ npm test
273
+ ```
274
+
275
+ 本地临时全局安装,方便在任意目录测试命令:
276
+
277
+ ```bash
278
+ npm link
279
+ ```
280
+
281
+ 取消本地链接:
282
+
283
+ ```bash
284
+ npm unlink -g @edmund/edx-cli
285
+ ```
286
+
287
+ ## 打包与发布
288
+
289
+ 本项目目前是纯 Node CLI,没有 TypeScript 编译、Vite 构建或前端产物,所以不需要 `npm run build`。
290
+
291
+ 发布前先检查测试:
292
+
293
+ ```bash
294
+ npm test
295
+ ```
296
+
297
+ 预览 npm 包会包含哪些文件:
298
+
299
+ ```bash
300
+ npm pack --dry-run
301
+ ```
302
+
303
+ `--dry-run` 的意思是“预演”。它会模拟执行打包流程,打印将进入 npm 包的文件列表,但不会真的生成 `.tgz` 文件,也不会上传任何东西。
304
+
305
+ 真正打出 npm tarball:
306
+
307
+ ```bash
308
+ npm pack
309
+ ```
310
+
311
+ 执行后会生成类似:
312
+
313
+ ```text
314
+ edmund-edx-cli-0.1.0.tgz
315
+ ```
316
+
317
+ 这个 `.tgz` 可以上传到 GitHub Release、Cloudflare R2、公司内网文件服务器,也可以直接在另一台电脑安装:
318
+
319
+ ```bash
320
+ npm install -g ./edmund-edx-cli-0.1.0.tgz
321
+ ```
322
+
323
+ 发布到 npm 官方仓库:
324
+
325
+ ```bash
326
+ npm login
327
+ npm publish --access public
328
+ ```
329
+
330
+ 每次发布新版本前都要升级版本号,因为 npm 不允许重复发布同一个包名和版本号:
331
+
332
+ ```bash
333
+ npm version patch
334
+ npm publish --access public
335
+ ```
336
+
337
+ 版本号常用规则:
338
+
339
+ ```text
340
+ patch: 0.1.0 -> 0.1.1,小修复
341
+ minor: 0.1.0 -> 0.2.0,新功能
342
+ major: 0.1.0 -> 1.0.0,破坏性变更
343
+ ```
344
+
345
+ ## npm pack 和 npm run build 的区别
346
+
347
+ `npm pack` 是 npm 自带命令,用来把当前包按 npm 发布规则打成 `.tgz`。
348
+
349
+ `npm run build` 不是 npm 内置打包规则,而是执行 `package.json` 里 `scripts.build` 配置的命令。比如 Vite 项目通常会写:
350
+
351
+ ```json
352
+ {
353
+ "scripts": {
354
+ "build": "vite build"
355
+ }
356
+ }
357
+ ```
358
+
359
+ 所以 Vite 的 `npm run build` 是生成前端 `dist/` 产物,不等于 `npm pack`,也不会自动封装 `npm pack`。如果一个项目想让 `npm run build` 后再打 npm 包,需要自己显式配置脚本。
360
+
361
+ 本项目没有配置 `build`,因为当前源码可以直接由 Node 运行。
362
+
363
+ ## 图片处理命令
364
+
365
+ `edx-image` 只保留通用图片处理入口:
366
+
367
+ ```bash
368
+ edx-image mkdir demo
369
+ edx-image split grid ./target --rows 3 --cols 3
370
+ edx-image split region ./target --from 0.5,0 --to 1,1
371
+ edx-image merge ./target --rows 3
372
+ ```
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../src/cli/run.mjs';
3
+ import { runAliasCommand } from '../src/commands/alias.mjs';
4
+
5
+ await runCli(runAliasCommand, process.argv.slice(2));
package/bin/edx-app.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../src/cli/run.mjs';
3
+ import { runAppCommand } from '../src/commands/app.mjs';
4
+
5
+ await runCli(runAppCommand, process.argv.slice(2));
package/bin/edx-hw.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../src/cli/run.mjs';
3
+ import { runHwCommand } from '../src/commands/hw.mjs';
4
+
5
+ await runCli(runHwCommand, process.argv.slice(2));
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../src/cli/run.mjs';
3
+ import { runImageCommand } from '../src/commands/image.mjs';
4
+
5
+ await runCli(runImageCommand, process.argv.slice(2));
package/bin/edx-md.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../src/cli/run.mjs';
3
+ import { runMdCommand } from '../src/commands/md.mjs';
4
+
5
+ await runCli(runMdCommand, process.argv.slice(2));
package/bin/edx.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../src/cli/run.mjs';
3
+ import { runEdxCommand } from '../src/cli/edx.mjs';
4
+
5
+ await runCli(runEdxCommand, process.argv.slice(2));
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@edmund32/edx-cli",
3
+ "version": "0.1.2",
4
+ "description": "Cross-platform Edmund command set",
5
+ "type": "module",
6
+ "files": [
7
+ "bin/",
8
+ "src/"
9
+ ],
10
+ "bin": {
11
+ "edx": "bin/edx.js",
12
+ "edx-app": "bin/edx-app.js",
13
+ "edx-alias": "bin/edx-alias.js",
14
+ "edx-hw": "bin/edx-hw.js",
15
+ "edx-image": "bin/edx-image.js",
16
+ "edx-md": "bin/edx-md.js"
17
+ },
18
+ "scripts": {
19
+ "test": "node --test test/*.test.mjs"
20
+ },
21
+ "dependencies": {
22
+ "sharp": "^0.34.5"
23
+ },
24
+ "engines": {
25
+ "node": ">=20"
26
+ }
27
+ }
@@ -0,0 +1,30 @@
1
+ import { ok, fail } from '../utils/result.mjs';
2
+
3
+ function help() {
4
+ return [
5
+ '欢迎使用 Edmund 的跨平台指令集',
6
+ '',
7
+ '顶层入口:',
8
+ ' edx-app',
9
+ ' edx-alias',
10
+ ' edx-hw',
11
+ ' edx-image',
12
+ ' edx-md',
13
+ ].join('\n');
14
+ }
15
+
16
+ function topLevelHint(name) {
17
+ const commands = new Set(['app', 'alias', 'hw', 'image', 'md']);
18
+ return commands.has(name) ? `\n请直接使用顶层入口: edx-${name}` : '';
19
+ }
20
+
21
+ export async function runEdxCommand(args, options = {}) {
22
+ void options;
23
+ const [name] = args;
24
+
25
+ if (!name || name === '-a' || name === '--help' || name === 'help') {
26
+ return ok(help());
27
+ }
28
+
29
+ return fail(`错误: 未知参数 '${name}'${topLevelHint(name)}\n${help()}`);
30
+ }
@@ -0,0 +1,17 @@
1
+ export async function runCli(handler, args) {
2
+ try {
3
+ const result = await handler(args);
4
+
5
+ if (result.stdout) {
6
+ process.stdout.write(result.stdout.endsWith('\n') ? result.stdout : `${result.stdout}\n`);
7
+ }
8
+ if (result.stderr) {
9
+ process.stderr.write(result.stderr.endsWith('\n') ? result.stderr : `${result.stderr}\n`);
10
+ }
11
+
12
+ process.exitCode = result.code;
13
+ } catch (error) {
14
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
15
+ process.exitCode = 1;
16
+ }
17
+ }