@flun/node-mobile-app 4.0.9 → 4.0.10
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/CHANGELOG.md +22 -6
- package/README.md +40 -38
- package/bin/cli.js +42 -27
- package/mobileAppConfig.js +1 -0
- package/package.json +7 -8
- package/scripts/copy-files.js +11 -10
- /package/{Android → Android}/346/236/204/345/273/272/346/214/207/345/215/227.md" +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
# 变更日志
|
|
2
|
+
|
|
3
|
+
## [4.1.0] - 2026-11-06 10:10
|
|
4
|
+
### 新增
|
|
5
|
+
- 新增 `example` 子命令,重新生成 `mobileAppConfig.js` 到项目根目录;此前该文件只由 `postinstall` 自动复制,
|
|
6
|
+
误删后无法手动恢复;文件已存在时跳过,不覆盖用户的修改;
|
|
7
|
+
|
|
8
|
+
### 优化
|
|
9
|
+
- 命令行帮助改用分组结构,按「命令」「选项」分节,子说明以 `- ` 前缀对齐,便于区分每个命令的注释边界;
|
|
10
|
+
- 帮助与运行输出统一使用 chalk 按语义着色:`用法:` 用 `blueBright`,分组名用 `cyan`,
|
|
11
|
+
命令与选项名用 `cyanBright`,说明用 `gray`,成功用 `green` 与 `greenBright`,错误用 `red`;
|
|
12
|
+
- `scripts/copy-files.js` 的输出改用同套着色,移除 emoji 前缀;
|
|
13
|
+
- 未知命令分支由逗号链跨行改为分号结束的多行块,符合行宽与语句分隔规则;
|
|
14
|
+
|
|
15
|
+
### 修复
|
|
16
|
+
- 指南文件名由 `Android 构建指南.md` 改为 `Android构建指南.md`,去除空格;
|
|
17
|
+
README 中的链接同步去除 `%20` 编码,此前指向不存在的路径;
|
|
18
|
+
|
|
19
|
+
### 文档
|
|
20
|
+
- README 命令列表与 `mobileAppConfig.js` 的「常用命令」注释补 `example`;
|
|
21
|
+
- README 包目录结构按 `files` 字段顺序重排;
|
|
22
|
+
|
|
2
23
|
## [4.0.9] - 2026-11-01 22:23
|
|
3
24
|
### 优化
|
|
4
25
|
- 命令行帮助重写:按 `用法`、`命令`、`选项` 三层组织,`test` 与 `build` 下的 Android、iOS 目标同级列出;
|
|
@@ -12,9 +33,4 @@
|
|
|
12
33
|
## [4.0.8] - 2026-10-26 09:56
|
|
13
34
|
### 修复
|
|
14
35
|
- 修复 `src/copy.js` 中 `out.allowScripts` 赋值语句缺少等号导致的运行时报错 `out.allowScripts is not a function`;
|
|
15
|
-
- 该问题由 4.0.7 版本折行时断点落在等号处、丢失赋值运算符引起;
|
|
16
|
-
|
|
17
|
-
## [4.0.7] - 2026-10-25 15:16
|
|
18
|
-
### 优化
|
|
19
|
-
- 项目标点已改为以英文标点为主;
|
|
20
|
-
- 行宽改为以 123 为主,部分超宽表格改为了列表;
|
|
36
|
+
- 该问题由 4.0.7 版本折行时断点落在等号处、丢失赋值运算符引起;
|
package/README.md
CHANGED
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
**核心理念**:你的 Node 项目保持不变,CLI 负责搬运,编译,打包;所有定制集中在 `mobileAppConfig.js` 一个文件;
|
|
8
8
|
|
|
9
|
-
- GitHub:https://github.com/OpenFlun/node-mobile-app
|
|
10
|
-
- Gitee(中国):https://gitee.com/OpenFlun/node-mobile-app
|
|
11
|
-
|
|
12
9
|
---
|
|
13
10
|
|
|
14
11
|
## 特性
|
|
@@ -20,7 +17,7 @@
|
|
|
20
17
|
- **跨平台**:Android(Windows / macOS / Linux)+ iOS(仅 macOS)
|
|
21
18
|
- **离线可用**:`build` 打出的 APK / IPA 内置 JS,无需连电脑也能跑
|
|
22
19
|
- **node_modules 优化(默认开启)**:构建时先交叉编译原生模块,再自动执行六阶段优化--先清空 postinstall 资源(保留 js 供合并),
|
|
23
|
-
再合并 JS,清平台二进制,清开发文件,清中间产物,清冗余与空目录;编译源文件与 node-gyp 中间产物在编译完成后一并清理,仅保留
|
|
20
|
+
再合并 JS,清平台二进制,清开发文件,清中间产物,清冗余与空目录;编译源文件与 node-gyp 中间产物在编译完成后一并清理,仅保留
|
|
24
21
|
`.node`;大幅减少打包文件数与体积,加速启动,无需配置;
|
|
25
22
|
- **热更新开箱可用**:`test` 自动启动 Metro 并后台常驻,命令结束后仍可热更新;详见 [自动化机制](#自动化机制);
|
|
26
23
|
- **签名切换自动处理**:设备已装包与待装包签名不一致时,`test` 自动卸载重装,避免 `INSTALL_FAILED_UPDATE_INCOMPATIBLE`;
|
|
@@ -71,12 +68,14 @@
|
|
|
71
68
|
│ ├── Gemfile
|
|
72
69
|
│ └── .watchmanconfig # watchman 文件监控配置(详见下方说明)
|
|
73
70
|
│
|
|
74
|
-
├──
|
|
75
|
-
├── package.json
|
|
76
|
-
├── README.md # 本文件
|
|
77
|
-
├── Android构建指南.md # Android 环境配置与踩坑清单
|
|
71
|
+
├── 非导出略...
|
|
78
72
|
├── 自定义libnode指南.md # 自编译 libnode.so 的完整流程
|
|
79
|
-
|
|
73
|
+
├── Android构建指南.md # Android 环境配置与踩坑清单
|
|
74
|
+
├── CHANGELOG.md # 更新日志
|
|
75
|
+
├── README.md # 本文件
|
|
76
|
+
├── LICENSE # 许可证
|
|
77
|
+
├── mobileAppConfig.js # 你的配置模板(postinstall 时复制到你的项目根)
|
|
78
|
+
└── package.json # 包配置
|
|
80
79
|
```
|
|
81
80
|
|
|
82
81
|
**关键约定**:
|
|
@@ -124,7 +123,7 @@
|
|
|
124
123
|
|
|
125
124
|
### 允许安装脚本执行
|
|
126
125
|
|
|
127
|
-
本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了
|
|
126
|
+
本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了
|
|
128
127
|
`ignore-scripts=true`),可能会导致安装不完整或运行时异常;
|
|
129
128
|
|
|
130
129
|
推荐在项目根目录的 `package.json` 中添加 `allowScripts` 字段,显式放行本包及其依赖的脚本:
|
|
@@ -180,16 +179,18 @@ npx node-mobile-app build --release
|
|
|
180
179
|
- 打 IPA(仅 macOS)
|
|
181
180
|
- `npx node-mobile-app clean`
|
|
182
181
|
- 清理构建缓存
|
|
182
|
+
- `npx node-mobile-app example`
|
|
183
|
+
- 重新生成 `mobileAppConfig.js`;配置文件被误删时使用,已存在则跳过
|
|
183
184
|
|
|
184
185
|
### 常用选项
|
|
185
186
|
|
|
186
|
-
| 选项
|
|
187
|
-
|
|
|
188
|
-
| `--ios`
|
|
189
|
-
| `--release`
|
|
190
|
-
| `--device <id>` | 指定设备(`test`)
|
|
191
|
-
| `--arch <abi>`
|
|
192
|
-
| `--debug`
|
|
187
|
+
| 选项 | 用途 |
|
|
188
|
+
| --------------- | ----------------------------------------------- |
|
|
189
|
+
| `--ios` | 目标平台为 iOS(默认 Android) |
|
|
190
|
+
| `--release` | 打 release 包(仅 `build`,Android 与 iOS 均生效) |
|
|
191
|
+
| `--device <id>` | 指定设备(`test`) |
|
|
192
|
+
| `--arch <abi>` | 指定 ABI,逗号分隔(`build`,仅 Android) |
|
|
193
|
+
| `--debug` | 显示详细日志(优化各阶段明细,各命令完整输出) |
|
|
193
194
|
|
|
194
195
|
### 安装 APK 到手机
|
|
195
196
|
|
|
@@ -222,8 +223,8 @@ adb install -r "./out/myapp-1.0.0-release.apk"
|
|
|
222
223
|
|
|
223
224
|
**说明**:
|
|
224
225
|
- `-r` = 覆盖安装(保留应用数据)
|
|
225
|
-
- **签名不同的包不能互相覆盖**:`build` 的 release 包用 `android.signing` 的 keystore 签名,`test` / `build` 的 debug
|
|
226
|
-
包用模板自带 debug keystore 签名;两者包名相同但签名不同,直接 `install -r` 会报
|
|
226
|
+
- **签名不同的包不能互相覆盖**:`build` 的 release 包用 `android.signing` 的 keystore 签名,`test` / `build` 的 debug
|
|
227
|
+
包用模板自带 debug keystore 签名;两者包名相同但签名不同,直接 `install -r` 会报
|
|
227
228
|
`INSTALL_FAILED_UPDATE_INCOMPATIBLE: signatures do not match`;需先执行 `adb uninstall <appId>` 再安装(会清除应用数据)
|
|
228
229
|
- `adb` 找不到?在 `mobileAppConfig.js` 里配 `android.adbPath`
|
|
229
230
|
- `test` 命令已自动处理签名冲突并安装,无需手动
|
|
@@ -423,13 +424,13 @@ signing: {
|
|
|
423
424
|
**编译与优化顺序**:先交叉编译原生模块(见 `install.js`),再执行以下六阶段优化:
|
|
424
425
|
|
|
425
426
|
1. **清空资源**:扫描各包 `postinstall` 引用的目录/文件,保留目录结构,清空内容;js 文件一律保留,留待合并阶段处理;
|
|
426
|
-
2. **合并 JS**:按包入口 bundle,多个 JS 文件合并为单文件(`bundled/` 目录);合并后同步删除 `package.json` 的 `scripts`
|
|
427
|
+
2. **合并 JS**:按包入口 bundle,多个 JS 文件合并为单文件(`bundled/` 目录);合并后同步删除 `package.json` 的 `scripts`
|
|
427
428
|
字段(运行时无安装期,且 Gradle `npm rebuild` 会触发脚本导致失败);
|
|
428
429
|
3. **清平台二进制**:按目标平台 + `abiFilters` 判定;`prebuilds/<platform>-<arch>` 按目录整体处理--Android 只保留 `android`,
|
|
429
430
|
`linux` / `darwin` / `win32` 删除;iOS 只保留 `darwin`;其余二进制逐文件按文件头判定;
|
|
430
431
|
4. **清开发文件与编译源文件**:删除 `.md` / `.map` / `.d.ts` / `.h` / `.c` / `.cc` / `.cpp` / `.cxx` 等非运行文件;
|
|
431
432
|
原生模块已在优化前完成编译,编译源文件无需保留;
|
|
432
|
-
5. **清 node-gyp 中间产物**:删除 `build/Release/obj.target/` 下的中间副本,以及 `.o` / `.d` / `.mk` / `.stamp` / `Makefile`
|
|
433
|
+
5. **清 node-gyp 中间产物**:删除 `build/Release/obj.target/` 下的中间副本,以及 `.o` / `.d` / `.mk` / `.stamp` / `Makefile`
|
|
433
434
|
等编译中间文件;
|
|
434
435
|
6. **清不参与运行的文件和空目录**.
|
|
435
436
|
|
|
@@ -444,15 +445,15 @@ signing: {
|
|
|
444
445
|
|
|
445
446
|
## 自动化机制
|
|
446
447
|
|
|
447
|
-
- **快照双哈希**:`.deps-snapshot.json` 记录 `optimizeHash`(`optimize-node-modules.js` 内容哈希)与
|
|
448
|
+
- **快照双哈希**:`.deps-snapshot.json` 记录 `optimizeHash`(`optimize-node-modules.js` 内容哈希)与
|
|
448
449
|
`installerHash`(`install.js` 内容哈希);任一变化即触发重装、重编、重优化,避免流程代码更新后仍复用旧缓存;
|
|
449
|
-
- **bundle 变化失效 Gradle 缓存**:`test` 生成 JS bundle 后,与
|
|
450
|
-
`android/app/build/intermediates/assets/debug/mergeDebugAssets/` 中的合并结果比对 SHA256;不一致时删除合并目录,强制
|
|
450
|
+
- **bundle 变化失效 Gradle 缓存**:`test` 生成 JS bundle 后,与
|
|
451
|
+
`android/app/build/intermediates/assets/debug/mergeDebugAssets/` 中的合并结果比对 SHA256;不一致时删除合并目录,强制
|
|
451
452
|
Gradle 重新合并;一致时保持 UP-TO-DATE;
|
|
452
453
|
- **编译前执行 `patch-package.js`**:把原生模块 `package.json` 中的 `node-gyp-build` 替换为 `node-gyp-build-mobile`,
|
|
453
454
|
使编译走 `@flun/nodejs-mobile-gyp` 而非 npm 自带的 `node-gyp`;
|
|
454
|
-
- **Metro 生命周期**:`test` 执行前检测 `localhost:8081/status`;未运行则以 detached 方式启动 `react-native start`(日志写入
|
|
455
|
-
`node-mobile-app-build/.metro.log`),就绪后继续;`run-android` 加 `--no-packager`,命令结束后 Metro 保持运行;30
|
|
455
|
+
- **Metro 生命周期**:`test` 执行前检测 `localhost:8081/status`;未运行则以 detached 方式启动 `react-native start`(日志写入
|
|
456
|
+
`node-mobile-app-build/.metro.log`),就绪后继续;`run-android` 加 `--no-packager`,命令结束后 Metro 保持运行;30
|
|
456
457
|
秒未就绪打印警告并回落到 APK 内置 bundle;
|
|
457
458
|
- **输出分级**:步骤标题 0 空格,动作与状态 2 空格,结果与完成 4 空格,子项详情 8 空格;优化阶段作为步骤子项,阶段标题 4 空格、
|
|
458
459
|
阶段结果 6 空格;
|
|
@@ -460,7 +461,7 @@ signing: {
|
|
|
460
461
|
|
|
461
462
|
## 服务地址说明
|
|
462
463
|
|
|
463
|
-
CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Server.prototype.listen`,从 `server.address()` 取真实端口,从
|
|
464
|
+
CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Server.prototype.listen`,从 `server.address()` 取真实端口,从
|
|
464
465
|
`Server` 类型判断 `http` / `https`;
|
|
465
466
|
|
|
466
467
|
因此 `mobileAppConfig.js` **不需要**配置端口/协议;动态端口(`listen(0)`)会拿到系统分配的真实端口;
|
|
@@ -500,11 +501,11 @@ CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Ser
|
|
|
500
501
|
| 运行时版本 | 官方提供多个版本,可用 `NODE_MOBILE_PREBUILT_VERSION` 切换 | **当前为 v18.20.4**,更高版本后续逐步适配 |
|
|
501
502
|
| Express 版本 | 产物带 full-icu,4.x / 5.x 均可用 | 当前产物不带 full-icu,**必须用 4.x** |
|
|
502
503
|
| full-icu | 自 v22.23.2 产物起支持 | 暂无 |
|
|
503
|
-
| `test` 离线 | 有(内置 bundle)
|
|
504
|
+
| `test` 离线 | 有(内置 bundle) | 有(需 `FORCE_BUNDLING=1`) |
|
|
504
505
|
| 图标尺寸 | 5 个密度(48~192) | 9 个尺寸(40~1024) |
|
|
505
506
|
|
|
506
|
-
**iOS 运行时版本**:iOS 附件的实际运行时版本随适配进度变化,**当前对应 v18.20.4**;如需更高版本,可克隆 [nodejs-mobile
|
|
507
|
-
仓库](https://github.com/OpenFlun/nodejs-mobile) 自行编译 `NodeMobile.xcframework`,流程与 Android 自编译 `libnode.so`
|
|
507
|
+
**iOS 运行时版本**:iOS 附件的实际运行时版本随适配进度变化,**当前对应 v18.20.4**;如需更高版本,可克隆 [nodejs-mobile
|
|
508
|
+
仓库](https://github.com/OpenFlun/nodejs-mobile) 自行编译 `NodeMobile.xcframework`,流程与 Android 自编译 `libnode.so`
|
|
508
509
|
类似(见 `自定义libnode指南.md`);
|
|
509
510
|
|
|
510
511
|
---
|
|
@@ -539,7 +540,7 @@ CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Ser
|
|
|
539
540
|
|
|
540
541
|
## 相关文档
|
|
541
542
|
|
|
542
|
-
- [Android
|
|
543
|
+
- [Android构建指南.md](./Android构建指南.md) -- Android 环境配置,踩坑清单
|
|
543
544
|
- [自定义libnode指南.md](./自定义libnode指南.md) -- 自编译 `libnode.so` 的完整流程
|
|
544
545
|
|
|
545
546
|
---
|
|
@@ -548,8 +549,8 @@ CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Ser
|
|
|
548
549
|
|
|
549
550
|
### Q:`test` 拔掉 USB 后 App 加载慢?
|
|
550
551
|
|
|
551
|
-
`test` 打的是 debug 版,启动时优先连 Metro;CLI 会在 `[9/11]` 自动启动 Metro 并保持后台常驻,但设备通过 `adb reverse` 访问
|
|
552
|
-
`localhost:8081`,**拔掉 USB 后端口转发失效**,设备连不上 Metro,debug 包等待超时后才回落到 APK 内置的 bundle;这个"等超时"是
|
|
552
|
+
`test` 打的是 debug 版,启动时优先连 Metro;CLI 会在 `[9/11]` 自动启动 Metro 并保持后台常驻,但设备通过 `adb reverse` 访问
|
|
553
|
+
`localhost:8081`,**拔掉 USB 后端口转发失效**,设备连不上 Metro,debug 包等待超时后才回落到 APK 内置的 bundle;这个"等超时"是
|
|
553
554
|
RN debug 版的固有行为;
|
|
554
555
|
|
|
555
556
|
拔线后的启动速度(从快到慢):
|
|
@@ -558,12 +559,12 @@ RN debug 版的固有行为;
|
|
|
558
559
|
2. **`npx node-mobile-app build`** 打包后安装到手机--次之(debug 版,会先尝试连 Metro 再回退)
|
|
559
560
|
3. **`npx node-mobile-app test`** 后手动打开应用--最慢(同上,且 `test` 面向"连 USB 热更新"场景)
|
|
560
561
|
|
|
561
|
-
若需要**热更新**,保持 USB 连接(维持 `adb reverse` 的端口转发);不需要热更新时,用 `build --release` 打包可避免等待 Metro
|
|
562
|
+
若需要**热更新**,保持 USB 连接(维持 `adb reverse` 的端口转发);不需要热更新时,用 `build --release` 打包可避免等待 Metro
|
|
562
563
|
超时.
|
|
563
564
|
|
|
564
565
|
### Q:`test` 装到手机后拔 USB 就崩?
|
|
565
566
|
|
|
566
|
-
前提是 APK 里有内置 bundle;`test` 每次都会先 `react-native bundle` 打进
|
|
567
|
+
前提是 APK 里有内置 bundle;`test` 每次都会先 `react-native bundle` 打进
|
|
567
568
|
APK;**如果崩,检查 CLI 输出里 `[7/11] 生成 JS bundle(离线可用)` 是否成功**;
|
|
568
569
|
|
|
569
570
|
### Q:Node 进程启动失败怎么排查?
|
|
@@ -580,7 +581,7 @@ adb logcat | grep -E "NODEJS-MOBILE|ReactNativeJS"
|
|
|
580
581
|
```
|
|
581
582
|
|
|
582
583
|
### Q:`npm install` 时提示 `install-scripts not yet covered by allowScripts`?
|
|
583
|
-
这是 npm 的提醒,表示某些依赖带有 `postinstall` / `install` 脚本,尚未在 `allowScripts` 里显式放行;`@flun/node-mobile-app`
|
|
584
|
+
这是 npm 的提醒,表示某些依赖带有 `postinstall` / `install` 脚本,尚未在 `allowScripts` 里显式放行;`@flun/node-mobile-app`
|
|
584
585
|
及其依赖(`@flun/nodejs-mobile-react-native`、`esbuild` 等)需要执行这些脚本才能完整安装;
|
|
585
586
|
|
|
586
587
|
放行方式见 [允许安装脚本执行](#允许安装脚本执行):在项目根 `package.json` 中添加 `allowScripts` 字段,逐项设为 `true`;
|
|
@@ -607,7 +608,7 @@ Express 5 的路由语法与 4 不同(如 `app.get('*')` 要改成 `app.get('/{*
|
|
|
607
608
|
|
|
608
609
|
### Q:`libnode.so` 和 `NodeMobile.xcframework` 是怎么来的?
|
|
609
610
|
|
|
610
|
-
从 `@flun/nodejs-mobile-react-native` 1.0.1 起,两套预编译二进制**不在 npm 包里**,改为 postinstall 阶段自动从 Gitee /
|
|
611
|
+
从 `@flun/nodejs-mobile-react-native` 1.0.1 起,两套预编译二进制**不在 npm 包里**,改为 postinstall 阶段自动从 Gitee /
|
|
611
612
|
GitHub Release 下载:
|
|
612
613
|
|
|
613
614
|
| 资源 | 目标位置 |
|
|
@@ -643,7 +644,7 @@ NODE_MOBILE_PREBUILT_IOS_SKIP=1 # 只跳过 iOS
|
|
|
643
644
|
|
|
644
645
|
### Q:iOS 提示 `fallbackSourceURL does not override`?
|
|
645
646
|
|
|
646
|
-
RN 0.87 的 API 若变动,`AppDelegate.swift` 里的 fallback 可能不生效--不影响"连 Mac + Metro"正常开发,只是拔 Mac 后无法
|
|
647
|
+
RN 0.87 的 API 若变动,`AppDelegate.swift` 里的 fallback 可能不生效--不影响"连 Mac + Metro"正常开发,只是拔 Mac 后无法
|
|
647
648
|
fallback 到内置 bundle;届时反馈,我们更新适配;
|
|
648
649
|
|
|
649
650
|
---
|
|
@@ -657,5 +658,6 @@ ISC
|
|
|
657
658
|
## 贡献
|
|
658
659
|
|
|
659
660
|
- 源码:[GitHub](https://github.com/OpenFlun/node-mobile-app)
|
|
661
|
+
- 中国:[gitee](https://gitee.com/OpenFlun/node-mobile-app)
|
|
660
662
|
- 问题反馈:[Issues](https://github.com/OpenFlun/node-mobile-app/issues)
|
|
661
663
|
- 主页:[npm 包页面](https://www.npmjs.com/package/@flun/node-mobile-app#readme)
|
package/bin/cli.js
CHANGED
|
@@ -1,30 +1,41 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import { runTest } from '../src/commands/test.js';
|
|
3
4
|
import { runBuild } from '../src/commands/build.js';
|
|
4
5
|
import { runClean } from '../src/commands/clean.js';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
const HELP = `用法: node-mobile-app <命令> [选项]
|
|
8
|
-
|
|
9
|
-
命令:
|
|
10
|
-
test 编译并安装到设备(走 Metro 热更新)
|
|
11
|
-
Android 默认,需 adb 可连的设备(真机或模拟器),默认配置 'arm64-v8a'(真机)
|
|
12
|
-
iOS 加 --ios,仅 macOS
|
|
13
|
-
build 打包
|
|
14
|
-
Android 默认 debug APK,离线可用;加 --release 打 release APK
|
|
15
|
-
iOS 加 --ios,仅 macOS,打 IPA;加 --release 用 Release 配置与 ad-hoc 导出
|
|
16
|
-
clean 清理构建缓存(不限平台)
|
|
17
|
-
|
|
18
|
-
选项:
|
|
19
|
-
--ios 目标为 iOS;test 与 build 均可用,仅 macOS
|
|
20
|
-
--release 打 release 包;仅 build 可用,Android 与 iOS 均生效;
|
|
21
|
-
打包安卓包时,请根据使用场景配置签名(走默认/自签名/正式签名);
|
|
22
|
-
--device <id> 指定设备;仅 test
|
|
23
|
-
--arch <abi> 指定 ABI,逗号分隔;仅 build,Android
|
|
24
|
-
--debug 显示详细日志;所有命令通用
|
|
25
|
-
|
|
26
|
-
无参数、help、-h、--help 输出本帮助。`,
|
|
6
|
+
import { copyFile } from '../scripts/copy-files.js';
|
|
27
7
|
|
|
8
|
+
// 输出用法;
|
|
9
|
+
const printHelp = () => {
|
|
10
|
+
const cmd = (name, desc) => ` ${chalk.cyanBright(name.padEnd(20))}${chalk.gray(desc)}`,
|
|
11
|
+
grp = name => ` ${chalk.cyan(name)}`,
|
|
12
|
+
sub = text => ` - ${chalk.gray(text)}`;
|
|
13
|
+
console.log([
|
|
14
|
+
` ${chalk.blueBright('用法:')} node-mobile-app <命令> [选项]`, '',
|
|
15
|
+
grp('命令'),
|
|
16
|
+
cmd('test', '编译并安装到设备,走 Metro 热更新'),
|
|
17
|
+
sub('Android 默认,需 adb 可连的设备(真机或模拟器),默认配置 arm64-v8a(真机)'),
|
|
18
|
+
sub('iOS 加 --ios,仅 macOS'),
|
|
19
|
+
cmd('build', '打包'),
|
|
20
|
+
sub('Android 默认 debug APK,离线可用;加 --release 打 release APK'),
|
|
21
|
+
sub('iOS 加 --ios,仅 macOS,打 IPA;加 --release 用 Release 配置与 ad-hoc 导出'),
|
|
22
|
+
cmd('clean', '清理构建缓存,不限平台'),
|
|
23
|
+
cmd('example', '重新生成 mobileAppConfig.js;误删时使用'), '',
|
|
24
|
+
grp('选项'),
|
|
25
|
+
cmd('--ios', '目标为 iOS;test 与 build 均可用,仅 macOS'),
|
|
26
|
+
cmd('--release', '打 release 包;仅 build 可用,Android 与 iOS 均生效'),
|
|
27
|
+
sub('打包安卓包时,请根据使用场景配置签名:默认、自签名或正式签名'),
|
|
28
|
+
cmd('--device <id>', '指定设备;仅 test'),
|
|
29
|
+
cmd('--arch <abi>', '指定 ABI,逗号分隔;仅 build,Android'),
|
|
30
|
+
cmd('--debug', '显示详细日志;所有命令通用'), '',
|
|
31
|
+
` ${chalk.gray('无参数、help、-h、--help 输出本帮助;')}`
|
|
32
|
+
].join('\n'));
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* 解析 --key value 参数,重复键覆盖为后者;
|
|
36
|
+
* @param {string[]} args 子命令后的参数
|
|
37
|
+
* @returns {Object} 解析结果
|
|
38
|
+
*/
|
|
28
39
|
parseFlags = args => {
|
|
29
40
|
const f = {};
|
|
30
41
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -36,7 +47,7 @@ const HELP = `用法: node-mobile-app <命令> [选项]
|
|
|
36
47
|
}
|
|
37
48
|
return f;
|
|
38
49
|
},
|
|
39
|
-
|
|
50
|
+
// 命令分发入口;
|
|
40
51
|
main = async () => {
|
|
41
52
|
const [cmd, ...rest] = process.argv.slice(2), flags = parseFlags(rest);
|
|
42
53
|
if (flags.debug) process.env.OPTIMIZE_DEBUG = '1';
|
|
@@ -44,11 +55,15 @@ const HELP = `用法: node-mobile-app <命令> [选项]
|
|
|
44
55
|
if (cmd === 'test') await runTest(flags);
|
|
45
56
|
else if (cmd === 'build') await runBuild(flags);
|
|
46
57
|
else if (cmd === 'clean') await runClean(flags);
|
|
47
|
-
else if (
|
|
48
|
-
else
|
|
58
|
+
else if (cmd === 'example') return process.exit(copyFile() ? 0 : 1);
|
|
59
|
+
else if (!cmd || cmd === 'help' || cmd === '-h' || cmd === '--help') return printHelp(), process.exit(0);
|
|
60
|
+
else {
|
|
61
|
+
console.error(chalk.red('✗ 未知命令: ') + chalk.yellow(cmd)), console.log('');
|
|
62
|
+
printHelp(), process.exit(1);
|
|
63
|
+
}
|
|
49
64
|
} catch (e) {
|
|
50
|
-
console.error(chalk.red('
|
|
51
|
-
if (process.env.DEBUG) console.error(chalk.
|
|
65
|
+
console.error(chalk.red('✗ ') + e.message);
|
|
66
|
+
if (process.env.DEBUG) console.error(chalk.gray(e.stack));
|
|
52
67
|
process.exit(1);
|
|
53
68
|
}
|
|
54
69
|
};
|
package/mobileAppConfig.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* npx node-mobile-app build --release 打 release APK(默认 debug keystore 签名,可安装;上架需配 android.signing)
|
|
12
12
|
* npx node-mobile-app build --ios 打 IPA(仅 macOS)
|
|
13
13
|
* npx node-mobile-app clean 清理构建缓存
|
|
14
|
+
* npx node-mobile-app example 重新生成此配置文件;误删时使用
|
|
14
15
|
*
|
|
15
16
|
* 任意命令加 --debug 可显示详细日志(优化各阶段明细、各命令完整输出)
|
|
16
17
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flun/node-mobile-app",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.10",
|
|
4
4
|
"description": "将 Node.js 项目一键打包为 Android / iOS 移动应用,运行时由 @flun/nodejs-mobile-react-native 提供;",
|
|
5
5
|
"author": "flun <open@flun.top>",
|
|
6
6
|
"keywords": [
|
|
@@ -29,26 +29,25 @@
|
|
|
29
29
|
"src/",
|
|
30
30
|
"template/",
|
|
31
31
|
"自定义libnode指南.md",
|
|
32
|
-
"Android
|
|
32
|
+
"Android构建指南.md",
|
|
33
33
|
"CHANGELOG.md",
|
|
34
34
|
"README.md",
|
|
35
35
|
"LICENSE",
|
|
36
|
-
"mobileAppConfig.js"
|
|
37
|
-
"package.json"
|
|
36
|
+
"mobileAppConfig.js"
|
|
38
37
|
],
|
|
39
38
|
"scripts": {
|
|
40
39
|
"postinstall": "node scripts/copy-files.js"
|
|
41
40
|
},
|
|
42
41
|
"dependencies": {
|
|
43
|
-
"@babel/core": "^8.0
|
|
42
|
+
"@babel/core": "^8.1.0",
|
|
44
43
|
"@eslint-react/eslint-plugin": "^5.28.8",
|
|
45
44
|
"@eslint/js": "^10.0.1",
|
|
46
|
-
"@flun/nodejs-mobile-react-native": "^2.1.
|
|
47
|
-
"@react-native/babel-preset": "^0.88.1",
|
|
48
|
-
"@react-native/metro-config": "^0.88.1",
|
|
45
|
+
"@flun/nodejs-mobile-react-native": "^2.1.3",
|
|
49
46
|
"@react-native-community/cli": "^20.2.0",
|
|
50
47
|
"@react-native-community/cli-platform-android": "^20.2.0",
|
|
51
48
|
"@react-native-community/cli-platform-ios": "^20.2.0",
|
|
49
|
+
"@react-native/babel-preset": "^0.88.1",
|
|
50
|
+
"@react-native/metro-config": "^0.88.1",
|
|
52
51
|
"@types/react": "^19.3.0",
|
|
53
52
|
"@typescript-eslint/eslint-plugin": "^8.72.1",
|
|
54
53
|
"@typescript-eslint/parser": "^8.72.1",
|
package/scripts/copy-files.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
3
4
|
import { fileURLToPath } from 'url';
|
|
4
5
|
|
|
5
6
|
// 要复制的文件位置、目标目录和目标文件路径
|
|
@@ -8,28 +9,28 @@ const __filename = fileURLToPath(import.meta.url), __dirname = path.dirname(__fi
|
|
|
8
9
|
targetDir = path.resolve(process.env.INIT_CWD || process.cwd()), targetFile = path.join(targetDir, fileName);
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
-
* >查看定义:@see {@link copyFile}
|
|
12
|
+
* 复制配置文件到项目根目录
|
|
13
13
|
* @returns {boolean} - 复制是否成功
|
|
14
14
|
*/
|
|
15
15
|
const copyFile = () => {
|
|
16
|
-
console.log(
|
|
16
|
+
console.log(chalk.gray('项目根目录: ') + targetDir);
|
|
17
17
|
try {
|
|
18
18
|
// 目标目录不是 Node 项目 || 包自身的 npm install(开发调试)→ 跳过
|
|
19
19
|
if (!fs.existsSync(path.join(targetDir, 'package.json'))) return true;
|
|
20
20
|
if (targetDir === path.resolve(__dirname, '..')) return true;
|
|
21
|
-
if (fs.existsSync(targetFile))
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
console.log(
|
|
21
|
+
if (fs.existsSync(targetFile)) {
|
|
22
|
+
console.log(chalk.green('✓ ') + chalk.greenBright('已存在: ') + chalk.gray(targetFile));
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
console.log(chalk.cyan('· ') + `未找到 ${fileName},正在创建...`);
|
|
26
|
+
fs.copyFileSync(sourceFile, targetFile);
|
|
27
|
+
console.log(chalk.green('✓ ') + chalk.greenBright(`已生成 ${fileName}: `) + chalk.gray(targetFile));
|
|
26
28
|
return true;
|
|
27
29
|
} catch (error) {
|
|
28
|
-
console.error(
|
|
30
|
+
console.error(chalk.red('✗ ') + `创建 ${fileName} 失败: ` + error.message);
|
|
29
31
|
return false;
|
|
30
32
|
}
|
|
31
33
|
};
|
|
32
34
|
|
|
33
|
-
// 执行脚本并导出函数
|
|
34
35
|
if (process.argv[1] === __filename) copyFile();
|
|
35
36
|
export { copyFile };
|
|
File without changes
|