@flun/node-mobile-app 4.0.6 → 4.0.7

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.
Files changed (49) hide show
  1. package/Android /346/236/204/345/273/272/346/214/207/345/215/227.md" +84 -76
  2. package/CHANGELOG.md +6 -5
  3. package/LICENSE +9 -4
  4. package/README.md +170 -77
  5. package/bin/cli.js +9 -9
  6. package/cli-assets/server.js +7 -4
  7. package/mobileAppConfig.js +32 -32
  8. package/package.json +5 -5
  9. package/scripts/copy-files.js +1 -1
  10. package/src/commands/build.js +9 -4
  11. package/src/commands/clean.js +3 -2
  12. package/src/commands/test.js +13 -9
  13. package/src/config.js +3 -3
  14. package/src/copy.js +41 -36
  15. package/src/fingerprint.js +4 -3
  16. package/src/generate.js +5 -3
  17. package/src/install.js +38 -27
  18. package/src/optimize-node-modules.js +10 -5
  19. package/src/patcher.js +37 -32
  20. package/src/platform-ios.js +6 -6
  21. package/src/utils.js +26 -22
  22. package/template/App.tsx +4 -4
  23. package/template/Gemfile +1 -1
  24. package/template/android/app/build.gradle +1 -1
  25. package/template/android/app/proguard-rules.pro +1 -1
  26. package/template/android/app/src/main/java/com/myapp/MainActivity.kt +1 -1
  27. package/template/android/app/src/main/java/com/myapp/MainApplication.kt +1 -1
  28. package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +6 -3
  29. package/template/android/app/src/main/res/raw/keep.xml +1 -1
  30. package/template/android/app/src/main/res/values/strings.xml +1 -1
  31. package/template/android/app/src/main/res/values/styles.xml +1 -1
  32. package/template/android/build.gradle +1 -1
  33. package/template/android/gradle.properties +1 -1
  34. package/template/android/gradlew +3 -2
  35. package/template/android/gradlew.bat +3 -2
  36. package/template/android/settings.gradle +1 -1
  37. package/template/app.json +1 -1
  38. package/template/index.js +2 -2
  39. package/template/ios/.xcode.env +1 -1
  40. package/template/ios/MyApp/Images.xcassets/AppIcon.appiconset/Contents.json +1 -1
  41. package/template/ios/MyApp/Images.xcassets/Contents.json +1 -1
  42. package/template/ios/MyApp/Info.plist +1 -1
  43. package/template/ios/MyApp/LaunchScreen.storyboard +30 -12
  44. package/template/ios/MyApp/PrivacyInfo.xcprivacy +1 -1
  45. package/template/ios/MyApp.xcodeproj/project.pbxproj +34 -15
  46. package/template/ios/MyApp.xcodeproj/xcshareddata/xcschemes/MyApp.xcscheme +1 -1
  47. package/template/ios/Podfile +1 -1
  48. package/template/package.json +1 -1
  49. package//350/207/252/345/256/232/344/271/211libnode/346/214/207/345/215/227.md +131 -98
package/README.md CHANGED
@@ -17,7 +17,9 @@
17
17
  - **增量构建**:文件指纹判断缓存,无变化时 10 秒级重跑
18
18
  - **跨平台**:Android(Windows / macOS / Linux)+ iOS(仅 macOS)
19
19
  - **离线可用**:`build` 打出的 APK / IPA 内置 JS,无需连电脑也能跑
20
- - **node_modules 优化(默认开启)**:构建时先交叉编译原生模块,再自动执行六阶段优化--先清空 postinstall 资源(保留 js 供合并),再合并 JS,清平台二进制,清开发文件,清中间产物,清冗余与空目录;编译源文件与 node-gyp 中间产物在编译完成后一并清理,仅保留 `.node`;大幅减少打包文件数与体积,加速启动,无需配置;
20
+ - **node_modules 优化(默认开启)**:构建时先交叉编译原生模块,再自动执行六阶段优化--先清空 postinstall 资源(保留 js 供合并),
21
+ 再合并 JS,清平台二进制,清开发文件,清中间产物,清冗余与空目录;编译源文件与 node-gyp 中间产物在编译完成后一并清理,仅保留
22
+ `.node`;大幅减少打包文件数与体积,加速启动,无需配置;
21
23
  - **热更新开箱可用**:`test` 自动启动 Metro 并后台常驻,命令结束后仍可热更新;详见 [自动化机制](#自动化机制);
22
24
  - **签名切换自动处理**:设备已装包与待装包签名不一致时,`test` 自动卸载重装,避免 `INSTALL_FAILED_UPDATE_INCOMPATIBLE`;
23
25
 
@@ -56,7 +58,8 @@
56
58
  │ ├── fingerprint.js # 缓存指纹(判断是否需要清理构建缓存)
57
59
  │ ├── generate.js # 生成 main.js(桥接)+ mobileApp.runtime.ts(运行时配置)
58
60
  │ ├── install.js # 依赖安装 + 原生模块交叉编译(前移至优化前) + 插件 abiFilters patch + WebView SSL patch
59
- │ ├── optimize-node-modules.js # node_modules 优化(六阶段:清资源 / 合并 JS / 清平台二进制 / 清开发文件与编译源文件 / 清 node-gyp 中间产物 / 清冗余与空目录)
61
+ │ ├── optimize-node-modules.js # node_modules 优化(六阶段:清资源 / 合并 JS / 清平台二进制 / 清开发文件与编译源文件 / 清
62
+ node-gyp 中间产物 / 清冗余与空目录)
60
63
  │ ├── patcher.js # 应用信息 / Android / iOS 配置 patch
61
64
  │ ├── platform-ios.js # iOS 构建(pod install / xcodebuild archive / export)
62
65
  │ ├── utils.js # 通用工具(文件操作 / 进程调用 / adb / hermesc 定位)
@@ -89,7 +92,8 @@
89
92
  ```
90
93
 
91
94
  **关键约定**:
92
- - `template/` 是**只读模板**;CLI 每次 `test`/`build` 会把它复制到你的项目的 `node-mobile-app-build/`,你如手改模板内容会被覆盖
95
+ - `template/` 是**只读模板**;CLI 每次 `test`/`build` 会把它复制到你的项目的 `node-mobile-app-build/`,
96
+ 你如手改模板内容会被覆盖
93
97
  - 你的持久化的自定义项应写进 `mobileAppConfig.js`,或通过 `keepFiles` 保留特定模板文件
94
98
  - `template/.watchmanconfig` 会在 `copyTemplate` 时复制到 `node-mobile-app-build/.watchmanconfig`,供 Metro 的 watchman 使用
95
99
 
@@ -119,7 +123,8 @@
119
123
 
120
124
  ### 允许安装脚本执行
121
125
 
122
- 本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了 `ignore-scripts=true`),可能会导致安装不完整或运行时异常;
126
+ 本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了
127
+ `ignore-scripts=true`),可能会导致安装不完整或运行时异常;
123
128
 
124
129
  推荐在项目根目录的 `package.json` 中添加 `allowScripts` 字段,显式放行本包及其依赖的脚本:
125
130
 
@@ -160,14 +165,18 @@ npx node-mobile-app build --release
160
165
 
161
166
  ## 命令说明
162
167
 
163
- | 命令 | 用途 |
164
- | ------------------------------------- | -------------------------------------------------------------------------------------------------- |
165
- | `npx node-mobile-app test` | 编译并安装到设备(**需连 USB**);自动启动 Metro 并后台常驻,支持热更新;设备已装包签名不一致时自动卸载 |
166
- | `npx node-mobile-app test --ios` | iOS 版(仅 macOS) |
167
- | `npx node-mobile-app build` | 打 debug APK(**离线可用**) |
168
- | `npx node-mobile-app build --release` | 打 release APK(默认用 debug keystore 签名,可安装;上架需配置 `android.signing`) |
169
- | `npx node-mobile-app build --ios` | 打 IPA(仅 macOS) |
170
- | `npx node-mobile-app clean` | 清理构建缓存 |
168
+ - `npx node-mobile-app test`
169
+ - 编译并安装到设备(**需连 USB**);自动启动 Metro 并后台常驻,支持热更新;设备已装包签名不一致时自动卸载
170
+ - `npx node-mobile-app test --ios`
171
+ - iOS 版(仅 macOS)
172
+ - `npx node-mobile-app build`
173
+ - 打 debug APK(**离线可用**)
174
+ - `npx node-mobile-app build --release`
175
+ - 打 release APK(默认用 debug keystore 签名,可安装;上架需配置 `android.signing`)
176
+ - `npx node-mobile-app build --ios`
177
+ - 打 IPA(仅 macOS)
178
+ - `npx node-mobile-app clean`
179
+ - 清理构建缓存
171
180
 
172
181
  ### 常用选项
173
182
 
@@ -210,7 +219,9 @@ adb install -r "./out/myapp-1.0.0-release.apk"
210
219
 
211
220
  **说明**:
212
221
  - `-r` = 覆盖安装(保留应用数据)
213
- - **签名不同的包不能互相覆盖**:`build` 的 release 包用 `android.signing` 的 keystore 签名,`test` / `build` 的 debug 包用模板自带 debug keystore 签名;两者包名相同但签名不同,直接 `install -r` 会报 `INSTALL_FAILED_UPDATE_INCOMPATIBLE: signatures do not match`;需先执行 `adb uninstall <appId>` 再安装(会清除应用数据)
222
+ - **签名不同的包不能互相覆盖**:`build` 的 release 包用 `android.signing` 的 keystore 签名,`test` / `build` 的 debug
223
+ 包用模板自带 debug keystore 签名;两者包名相同但签名不同,直接 `install -r` 会报
224
+ `INSTALL_FAILED_UPDATE_INCOMPATIBLE: signatures do not match`;需先执行 `adb uninstall <appId>` 再安装(会清除应用数据)
214
225
  - `adb` 找不到?在 `mobileAppConfig.js` 里配 `android.adbPath`
215
226
  - `test` 命令已自动处理签名冲突并安装,无需手动
216
227
 
@@ -218,11 +229,18 @@ adb install -r "./out/myapp-1.0.0-release.apk"
218
229
 
219
230
  ## 自动化机制
220
231
 
221
- - **快照双哈希**:`.deps-snapshot.json` 记录 `optimizeHash`(`optimize-node-modules.js` 内容哈希)与 `installerHash`(`install.js` 内容哈希);任一变化即触发重装、重编、重优化,避免流程代码更新后仍复用旧缓存;
222
- - **bundle 变化失效 Gradle 缓存**:`test` 生成 JS bundle 后,与 `android/app/build/intermediates/assets/debug/mergeDebugAssets/` 中的合并结果比对 SHA256;不一致时删除合并目录,强制 Gradle 重新合并;一致时保持 UP-TO-DATE;
223
- - **编译前执行 `patch-package.js`**:把原生模块 `package.json` 中的 `node-gyp-build` 替换为 `node-gyp-build-mobile`,使编译走 `@flun/nodejs-mobile-gyp` 而非 npm 自带的 `node-gyp`;
224
- - **Metro 生命周期**:`test` 执行前检测 `localhost:8081/status`;未运行则以 detached 方式启动 `react-native start`(日志写入 `node-mobile-app-build/.metro.log`),就绪后继续;`run-android` 加 `--no-packager`,命令结束后 Metro 保持运行;30 秒未就绪打印警告并回落到 APK 内置 bundle;
225
- - **输出分级**:步骤标题 0 空格,动作与状态 2 空格,结果与完成 4 空格,子项详情 8 空格;优化阶段作为步骤子项,阶段标题 4 空格、阶段结果 6 空格;
232
+ - **快照双哈希**:`.deps-snapshot.json` 记录 `optimizeHash`(`optimize-node-modules.js` 内容哈希)与
233
+ `installerHash`(`install.js` 内容哈希);任一变化即触发重装、重编、重优化,避免流程代码更新后仍复用旧缓存;
234
+ - **bundle 变化失效 Gradle 缓存**:`test` 生成 JS bundle 后,与
235
+ `android/app/build/intermediates/assets/debug/mergeDebugAssets/` 中的合并结果比对 SHA256;不一致时删除合并目录,强制
236
+ Gradle 重新合并;一致时保持 UP-TO-DATE;
237
+ - **编译前执行 `patch-package.js`**:把原生模块 `package.json` 中的 `node-gyp-build` 替换为 `node-gyp-build-mobile`,
238
+ 使编译走 `@flun/nodejs-mobile-gyp` 而非 npm 自带的 `node-gyp`;
239
+ - **Metro 生命周期**:`test` 执行前检测 `localhost:8081/status`;未运行则以 detached 方式启动 `react-native start`(日志写入
240
+ `node-mobile-app-build/.metro.log`),就绪后继续;`run-android` 加 `--no-packager`,命令结束后 Metro 保持运行;30
241
+ 秒未就绪打印警告并回落到 APK 内置 bundle;
242
+ - **输出分级**:步骤标题 0 空格,动作与状态 2 空格,结果与完成 4 空格,子项详情 8 空格;优化阶段作为步骤子项,阶段标题 4 空格、
243
+ 阶段结果 6 空格;
226
244
  - **精简与详细双模式**:默认只输出关键行与结果,`--debug` 或 `OPTIMIZE_DEBUG=1` 时全量输出(含 Gradle 与 Metro 完整日志);
227
245
 
228
246
  ## 配置文件 `mobileAppConfig.js`
@@ -231,52 +249,109 @@ adb install -r "./out/myapp-1.0.0-release.apk"
231
249
 
232
250
  ### 必填
233
251
 
234
- | 字段 | 类型 | 说明 |
235
- | ------------ | -------- | --------------------------------------- |
236
- | `serverPath` | `string` | Node 启动脚本路径(默认 `'./server.js'`) |
237
-
238
- ### 应用信息
239
-
240
- | 字段 | 类型 | 说明 |
241
- | ------------- | ---------------- | ------------------------------------------------------------------------ |
242
- | `appName` | `string \| null` | 应用显示名称(默认从 `package.json` 的 `name` 读) |
243
- | `outputDir` | `string \| null` | APK / IPA 输出目录(默认 `./out`) |
244
- | `appId` | `string` | 应用唯一标识,反向域名格式(Android `applicationId` / iOS Bundle ID) |
245
- | `versionCode` | `int` | 内部版本号(Android `versionCode` / iOS `CFBundleVersion`) |
246
- | `versionName` | `string` | 你的可见版本名(Android `versionName` / iOS `CFBundleShortVersionString`) |
247
-
248
- ### WebView
249
-
250
- | 字段 | 类型 | 默认 | 说明 |
251
- | --------------------- | --------- | ------------------- | ----------------------------------------------------------------------------------------- |
252
- | `showLoading` | `boolean` | `true` | 是否显示加载指示器 |
253
- | `loadingText` | `string` | `'正在启动服务...'` | 加载提示文字 |
254
- | `enableJavaScript` | `boolean` | `true` | 是否启用 JavaScript |
255
- | `enableDomStorage` | `boolean` | `true` | 是否启用 DOM Storage |
256
- | `allowFileAccess` | `boolean` | `true` | 是否允许访问本地文件 |
257
- | `mixedContentMode` | `string` | `'always'` | 混合内容模式:`never` / `always` / `compatibility` |
258
- | `backgroundColor` | 见下 | `'#ffffff'` | WebView 背景色;支持:固定色 `'#ffffff'` / 跟随系统 `'system'` / 深浅两值 `{ light, dark }` |
259
- | `allowSelfSignedCert` | `boolean` | `false` | 见下文"HTTPS 自签证书" |
252
+ - `appName`
253
+ - 类型:`string \| null`
254
+ - 默认:`null`
255
+ - 说明:应用显示名称(默认从 `package.json` 的 `name` 读)
256
+ - `outputDir`
257
+ - 类型:`string \| null`
258
+ - 默认:`null`
259
+ - 说明:APK / IPA 输出目录(默认 `./out`)
260
+ - `showLoading`
261
+ - 类型:`boolean`
262
+ - 默认:`true`
263
+ - 说明:是否显示加载指示器
264
+ - `loadingText`
265
+ - 类型:`string`
266
+ - 默认:`'正在启动服务...'`
267
+ - 说明:加载提示文字
268
+ - `enableJavaScript`
269
+ - 类型:`boolean`
270
+ - 默认:`true`
271
+ - 说明:是否启用 JavaScript
272
+ - `enableDomStorage`
273
+ - 类型:`boolean`
274
+ - 默认:`true`
275
+ - 说明:是否启用 DOM Storage
276
+ - `allowFileAccess`
277
+ - 类型:`boolean`
278
+ - 默认:`true`
279
+ - 说明:是否允许访问本地文件
280
+ - `mixedContentMode`
281
+ - 类型:`string`
282
+ - 默认:`'always'`
283
+ - 说明:混合内容模式:`never` / `always` / `compatibility`
284
+ - `backgroundColor`
285
+ - 类型:见下
286
+ - 默认:`'#ffffff'`
287
+ - 说明:WebView 背景色;支持:固定色 `'#ffffff'` / 跟随系统 `'system'` / 深浅两值 `{ light, dark }`
288
+ - `allowSelfSignedCert`
289
+ - 类型:`boolean`
290
+ - 默认:`false`
291
+ - 说明:见下文"HTTPS 自签证书"
260
292
 
261
293
  ### Android
262
294
 
263
- | 字段 | 类型 | 默认 | 说明 |
264
- | ----------------------- | ---------------- | -------------------- | ---------------------------------------------------------------------------------------------------- |
265
- | `sdkPath` | `string \| null` | `null` | Android SDK 路径(默认自动查找) |
266
- | `adbPath` | `string \| null` | `null` | adb 路径(默认自动查找) |
267
- | `minSdkVersion` | `int` | `29` | 最低 SDK(29=Android 10.0) |
268
- | `targetSdkVersion` | `int` | `36` | 目标 SDK |
269
- | `compileSdkVersion` | `int` | `37` | 编译 SDK |
270
- | `buildToolsVersion` | `string` | `'37.0.0'` | 构建工具版本 |
271
- | `abiFilters` | `string[]` | `['arm64-v8a']` | 打包架构(真机 `arm64-v8a`,模拟器加 `x86_64`) |
272
- | `icon` | `string` | `'./build/icon.png'` | 应用图标(建议 512×512 PNG) |
273
- | `usesCleartextTraffic` | `boolean` | `true` | 是否允许明文 HTTP |
274
- | `permissions` | `string[]` | `[]` | 额外权限(如 `'android.permission.CAMERA'`) |
275
- | `signing` | `object` | - | 自定义签名配置.**不填则用模板自带的 debug keystore 签名**(可安装测试,不可上架);填则以下 4 项均为必填 |
276
- | `signing.keystore` | `string` | - | keystore 路径(相对你的项目根) |
277
- | `signing.storePassword` | `string` | - | keystore 密码 |
278
- | `signing.keyAlias` | `string` | - | 密钥别名 |
279
- | `signing.keyPassword` | `string` | - | 密钥密码(通常与 `storePassword` 相同) |
295
+ - `sdkPath`
296
+ - 类型:`string \| null`
297
+ - 默认:`null`
298
+ - 说明:Android SDK 路径(默认自动查找)
299
+ - `adbPath`
300
+ - 类型:`string \| null`
301
+ - 默认:`null`
302
+ - 说明:adb 路径(默认自动查找)
303
+ - `minSdkVersion`
304
+ - 类型:`int`
305
+ - 默认:`29`
306
+ - 说明:最低 SDK(29=Android 10.0)
307
+ - `targetSdkVersion`
308
+ - 类型:`int`
309
+ - 默认:`36`
310
+ - 说明:目标 SDK
311
+ - `compileSdkVersion`
312
+ - 类型:`int`
313
+ - 默认:`37`
314
+ - 说明:编译 SDK
315
+ - `buildToolsVersion`
316
+ - 类型:`string`
317
+ - 默认:`'37.0.0'`
318
+ - 说明:构建工具版本
319
+ - `abiFilters`
320
+ - 类型:`string[]`
321
+ - 默认:`['arm64-v8a']`
322
+ - 说明:打包架构(真机 `arm64-v8a`,模拟器加 `x86_64`)
323
+ - `icon`
324
+ - 类型:`string`
325
+ - 默认:`'./build/icon.png'`
326
+ - 说明:应用图标(建议 512×512 PNG)
327
+ - `usesCleartextTraffic`
328
+ - 类型:`boolean`
329
+ - 默认:`true`
330
+ - 说明:是否允许明文 HTTP
331
+ - `permissions`
332
+ - 类型:`string[]`
333
+ - 默认:`[]`
334
+ - 说明:额外权限(如 `'android.permission.CAMERA'`)
335
+ - `signing`
336
+ - 类型:`object`
337
+ - 默认:-
338
+ - 说明:自定义签名配置.**不填则用模板自带的 debug keystore 签名**(可安装测试,不可上架);填则以下 4 项均为必填
339
+ - `signing.keystore`
340
+ - 类型:`string`
341
+ - 默认:-
342
+ - 说明:keystore 路径(相对你的项目根)
343
+ - `signing.storePassword`
344
+ - 类型:`string`
345
+ - 默认:-
346
+ - 说明:keystore 密码
347
+ - `signing.keyAlias`
348
+ - 类型:`string`
349
+ - 默认:-
350
+ - 说明:密钥别名
351
+ - `signing.keyPassword`
352
+ - 类型:`string`
353
+ - 默认:-
354
+ - 说明:密钥密码(通常与 `storePassword` 相同)
280
355
 
281
356
  #### 生成自签名 keystore
282
357
 
@@ -316,7 +391,8 @@ signing: {
316
391
  },
317
392
  ```
318
393
 
319
- > **提示**:`storepass` / `keypass` 可填相同值,对应配置里 `storePassword` / `keyPassword` 也填相同值.四项缺一不可,缺项时构建前会报错.
394
+ > **提示**:`storepass` / `keypass` 可填相同值,对应配置里 `storePassword` / `keyPassword` 也填相同值.四项缺一不可,
395
+ 缺项时构建前会报错.
320
396
 
321
397
  ### iOS
322
398
 
@@ -362,13 +438,18 @@ signing: {
362
438
  **编译与优化顺序**:先交叉编译原生模块(见 `install.js`),再执行以下六阶段优化:
363
439
 
364
440
  1. **清空资源**:扫描各包 `postinstall` 引用的目录/文件,保留目录结构,清空内容;js 文件一律保留,留待合并阶段处理;
365
- 2. **合并 JS**:按包入口 bundle,多个 JS 文件合并为单文件(`bundled/` 目录);合并后同步删除 `package.json` 的 `scripts` 字段(运行时无安装期,且 Gradle `npm rebuild` 会触发脚本导致失败);
366
- 3. **清平台二进制**:按目标平台 + `abiFilters` 判定;`prebuilds/<platform>-<arch>` 按目录整体处理--Android 只保留 `android`,`linux` / `darwin` / `win32` 删除;iOS 只保留 `darwin`;其余二进制逐文件按文件头判定;
367
- 4. **清开发文件与编译源文件**:删除 `.md` / `.map` / `.d.ts` / `.h` / `.c` / `.cc` / `.cpp` / `.cxx` 等非运行文件;原生模块已在优化前完成编译,编译源文件无需保留;
368
- 5. **清 node-gyp 中间产物**:删除 `build/Release/obj.target/` 下的中间副本,以及 `.o` / `.d` / `.mk` / `.stamp` / `Makefile` 等编译中间文件;
441
+ 2. **合并 JS**:按包入口 bundle,多个 JS 文件合并为单文件(`bundled/` 目录);合并后同步删除 `package.json` 的 `scripts`
442
+ 字段(运行时无安装期,且 Gradle `npm rebuild` 会触发脚本导致失败);
443
+ 3. **清平台二进制**:按目标平台 + `abiFilters` 判定;`prebuilds/<platform>-<arch>` 按目录整体处理--Android 只保留 `android`,
444
+ `linux` / `darwin` / `win32` 删除;iOS 只保留 `darwin`;其余二进制逐文件按文件头判定;
445
+ 4. **清开发文件与编译源文件**:删除 `.md` / `.map` / `.d.ts` / `.h` / `.c` / `.cc` / `.cpp` / `.cxx` 等非运行文件;
446
+ 原生模块已在优化前完成编译,编译源文件无需保留;
447
+ 5. **清 node-gyp 中间产物**:删除 `build/Release/obj.target/` 下的中间副本,以及 `.o` / `.d` / `.mk` / `.stamp` / `Makefile`
448
+ 等编译中间文件;
369
449
  6. **清不参与运行的文件和空目录**.
370
450
 
371
- **缓存**:快照记录在 `node-mobile-app-build/.deps-snapshot.json`,构建时自动判断"重装并优化 / 仅优化 / 跳过";改动模板或依赖会自动触发重编.
451
+ **缓存**:快照记录在 `node-mobile-app-build/.deps-snapshot.json`,构建时自动判断"重装并优化 / 仅优化 / 跳过";
452
+ 改动模板或依赖会自动触发重编.
372
453
 
373
454
  **异常排查**:
374
455
 
@@ -378,7 +459,8 @@ signing: {
378
459
 
379
460
  ## 服务地址说明
380
461
 
381
- CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Server.prototype.listen`,从 `server.address()` 取真实端口,从 `Server` 类型判断 `http` / `https`;
462
+ CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Server.prototype.listen`,从 `server.address()` 取真实端口,从
463
+ `Server` 类型判断 `http` / `https`;
382
464
 
383
465
  因此 `mobileAppConfig.js` **不需要**配置端口/协议;动态端口(`listen(0)`)会拿到系统分配的真实端口;
384
466
 
@@ -420,7 +502,9 @@ CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Ser
420
502
  | `test` 离线 | ✓(内置 bundle) | ✓(需 `FORCE_BUNDLING=1`) |
421
503
  | 图标尺寸 | 5 个密度(48~192) | 9 个尺寸(40~1024) |
422
504
 
423
- **iOS 运行时版本**:iOS 附件的实际运行时版本随适配进度变化,**当前对应 v18.20.4**;如需更高版本,可克隆 [nodejs-mobile 仓库](https://github.com/OpenFlun/nodejs-mobile) 自行编译 `NodeMobile.xcframework`,流程与 Android 自编译 `libnode.so` 类似(见 `自定义libnode指南.md`);
505
+ **iOS 运行时版本**:iOS 附件的实际运行时版本随适配进度变化,**当前对应 v18.20.4**;如需更高版本,可克隆 [nodejs-mobile
506
+ 仓库](https://github.com/OpenFlun/nodejs-mobile) 自行编译 `NodeMobile.xcframework`,流程与 Android 自编译 `libnode.so`
507
+ 类似(见 `自定义libnode指南.md`);
424
508
 
425
509
  ---
426
510
 
@@ -456,7 +540,9 @@ CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Ser
456
540
 
457
541
  ### Q:`test` 拔掉 USB 后 App 加载慢?
458
542
 
459
- `test` 打的是 debug 版,启动时优先连 Metro;CLI 会在 `[9/11]` 自动启动 Metro 并保持后台常驻,但设备通过 `adb reverse` 访问 `localhost:8081`,**拔掉 USB 后端口转发失效**,设备连不上 Metro,debug 包等待超时后才回落到 APK 内置的 bundle;这个"等超时"是 RN debug 版的固有行为;
543
+ `test` 打的是 debug 版,启动时优先连 Metro;CLI 会在 `[9/11]` 自动启动 Metro 并保持后台常驻,但设备通过 `adb reverse` 访问
544
+ `localhost:8081`,**拔掉 USB 后端口转发失效**,设备连不上 Metro,debug 包等待超时后才回落到 APK 内置的 bundle;这个"等超时"是
545
+ RN debug 版的固有行为;
460
546
 
461
547
  拔线后的启动速度(从快到慢):
462
548
 
@@ -464,11 +550,13 @@ CLI **自动识别 `server.js` 实际监听的协议和端口**--拦截 `net.Ser
464
550
  2. **`npx node-mobile-app build`** 打包后安装到手机--次之(debug 版,会先尝试连 Metro 再回退)
465
551
  3. **`npx node-mobile-app test`** 后手动打开应用--最慢(同上,且 `test` 面向"连 USB 热更新"场景)
466
552
 
467
- 若需要**热更新**,保持 USB 连接(维持 `adb reverse` 的端口转发);不需要热更新时,用 `build --release` 打包可避免等待 Metro 超时.
553
+ 若需要**热更新**,保持 USB 连接(维持 `adb reverse` 的端口转发);不需要热更新时,用 `build --release` 打包可避免等待 Metro
554
+ 超时.
468
555
 
469
556
  ### Q:`test` 装到手机后拔 USB 就崩?
470
557
 
471
- 前提是 APK 里有内置 bundle;`test` 每次都会先 `react-native bundle` 打进 APK;**如果崩,检查 CLI 输出里 `[7/11] 生成 JS bundle(离线可用)` 是否成功**;
558
+ 前提是 APK 里有内置 bundle;`test` 每次都会先 `react-native bundle` 打进
559
+ APK;**如果崩,检查 CLI 输出里 `[7/11] 生成 JS bundle(离线可用)` 是否成功**;
472
560
 
473
561
  ### Q:Node 进程启动失败怎么排查?
474
562
 
@@ -484,7 +572,8 @@ adb logcat | grep -E "NODEJS-MOBILE|ReactNativeJS"
484
572
  ```
485
573
 
486
574
  ### Q:`npm install` 时提示 `install-scripts not yet covered by allowScripts`?
487
- 这是 npm 的提醒,表示某些依赖带有 `postinstall` / `install` 脚本,尚未在 `allowScripts` 里显式放行;`@flun/node-mobile-app` 及其依赖(`@flun/nodejs-mobile-react-native`、`esbuild` 等)需要执行这些脚本才能完整安装;
575
+ 这是 npm 的提醒,表示某些依赖带有 `postinstall` / `install` 脚本,尚未在 `allowScripts` 里显式放行;`@flun/node-mobile-app`
576
+ 及其依赖(`@flun/nodejs-mobile-react-native`、`esbuild` 等)需要执行这些脚本才能完整安装;
488
577
 
489
578
  放行方式见 [允许安装脚本执行](#允许安装脚本执行):在项目根 `package.json` 中添加 `allowScripts` 字段,逐项设为 `true`;
490
579
 
@@ -492,7 +581,8 @@ adb logcat | grep -E "NODEJS-MOBILE|ReactNativeJS"
492
581
 
493
582
  ### Q:Android 构建卡在拉取依赖(`react-android` / `hermes-android` 等)?
494
583
 
495
- Android 模板的 `settings.gradle` 与 `build.gradle` 已在 `google()` / `mavenCentral()` 之前插入阿里云镜像,国内网络优先走镜像;海外网络镜像不命中会自动回退官方源,不影响功能.
584
+ Android 模板的 `settings.gradle` 与 `build.gradle` 已在 `google()` / `mavenCentral()` 之前插入阿里云镜像,
585
+ 国内网络优先走镜像;海外网络镜像不命中会自动回退官方源,不影响功能.
496
586
 
497
587
  若仍卡住:
498
588
 
@@ -509,7 +599,8 @@ Express 5 的路由语法与 4 不同(如 `app.get('*')` 要改成 `app.get('/{*
509
599
 
510
600
  ### Q:`libnode.so` 和 `NodeMobile.xcframework` 是怎么来的?
511
601
 
512
- 从 `@flun/nodejs-mobile-react-native` 1.0.1 起,两套预编译二进制**不在 npm 包里**,改为 postinstall 阶段自动从 Gitee / GitHub Release 下载:
602
+ 从 `@flun/nodejs-mobile-react-native` 1.0.1 起,两套预编译二进制**不在 npm 包里**,改为 postinstall 阶段自动从 Gitee /
603
+ GitHub Release 下载:
513
604
 
514
605
  | 资源 | 目标位置 |
515
606
  | ------------------------------ | --------------------------------------------------------------------------- |
@@ -537,13 +628,15 @@ NODE_MOBILE_PREBUILT_IOS_SKIP=1 # 只跳过 iOS
537
628
  2. 如果 `mobileAppConfig.js` 的 `android.abiFilters` 与 libnode 支持的架构不一致,同步调整
538
629
  3. 跑 `npx node-mobile-app build` 或 `npx node-mobile-app test`
539
630
 
540
- **CLI 会自动处理缓存**:指纹机制检测到 `libnode.so` 或 `abiFilters` 变化,自动清理 `.cxx` / `android/build` / `app/build`,全量重编;**你无需手动清任何缓存**;
631
+ **CLI 会自动处理缓存**:指纹机制检测到 `libnode.so` 或 `abiFilters` 变化,自动清理 `.cxx` / `android/build` / `app/build`,
632
+ 全量重编;**你无需手动清任何缓存**;
541
633
 
542
634
  详细流程见 `自定义libnode指南.md`;
543
635
 
544
636
  ### Q:iOS 提示 `fallbackSourceURL does not override`?
545
637
 
546
- RN 0.87 的 API 若变动,`AppDelegate.swift` 里的 fallback 可能不生效--不影响"连 Mac + Metro"正常开发,只是拔 Mac 后无法 fallback 到内置 bundle;届时反馈,我们更新适配;
638
+ RN 0.87 的 API 若变动,`AppDelegate.swift` 里的 fallback 可能不生效--不影响"连 Mac + Metro"正常开发,只是拔 Mac 后无法
639
+ fallback 到内置 bundle;届时反馈,我们更新适配;
547
640
 
548
641
  ---
549
642
 
package/bin/cli.js CHANGED
@@ -8,17 +8,17 @@ const HELP = `
8
8
  @flun/node-mobile-app
9
9
 
10
10
  指令:
11
- node-mobile-app test 编译并安装到设备(Android,需连 USB,走 Metro 热更新)
12
- node-mobile-app test --ios iOS 版(仅 macOS)
13
- node-mobile-app build 打 debug APK(Android,离线可用)
14
- node-mobile-app build --release 打 release APK(Android,默认 debug keystore 签名,可安装)
15
- node-mobile-app build --ios 打 IPA(仅 macOS)
16
- node-mobile-app clean 清理构建缓存(不限平台)
11
+ node-mobile-app test 编译并安装到设备(Android,需连 USB,走 Metro 热更新)
12
+ node-mobile-app test --ios iOS 版(仅 macOS)
13
+ node-mobile-app build 打 debug APK(Android,离线可用)
14
+ node-mobile-app build --release 打 release APK(Android,默认 debug keystore 签名,可安装)
15
+ node-mobile-app build --ios 打 IPA(仅 macOS)
16
+ node-mobile-app clean 清理构建缓存(不限平台)
17
17
 
18
18
  选项:
19
- --device <id> 指定设备(仅 test)
20
- --arch <abi> 指定 ABI,逗号分隔(仅 build,Android)
21
- --debug 显示详细日志(所有指令通用)
19
+ --device <id> 指定设备(仅 test)
20
+ --arch <abi> 指定 ABI,逗号分隔(仅 build,Android)
21
+ --debug 显示详细日志(所有指令通用)
22
22
  `,
23
23
 
24
24
  parseFlags = args => {
@@ -1,8 +1,8 @@
1
1
  // ============================================================
2
- // 这是 @flun/node-mobile-app 自动生成的默认入口脚本。
3
- // 请替换为你自己的 Node.js 启动脚本。
2
+ // 这是 @flun/node-mobile-app 自动生成的默认入口脚本;
3
+ // 请替换为你自己的 Node.js 启动脚本;
4
4
  //
5
- // 你可以:
5
+ // 你可以:
6
6
  // 1. 直接修改这个文件
7
7
  // 2. 或用 mobileAppConfig.js 里的 serverPath 指向你自己的入口文件
8
8
  // ============================================================
@@ -17,7 +17,10 @@ const PORT = 3000,
17
17
  <title>Node Mobile App</title>
18
18
  <style>
19
19
  html,body{height:100%;margin:0;padding:0}
20
- body{display:flex;flex-direction:column;justify-content:center;align-items:center;min-height:100vh;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff;text-align:center;padding:24px;box-sizing:border-box}
20
+ body{display:flex;flex-direction:column;justify-content:center;align-items:center;
21
+ min-height:100vh;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
22
+ background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);
23
+ color:#fff;text-align:center;padding:24px;box-sizing:border-box}
21
24
  h1{margin:0 0 16px;font-size:28px;font-weight:600;letter-spacing:.5px}
22
25
  p{margin:0;font-size:16px;opacity:.9;line-height:1.5}
23
26
  .hint{margin-top:32px;font-size:13px;opacity:.65}
@@ -5,20 +5,20 @@
5
5
  * 所有路径相对于用户项目根目录
6
6
  *
7
7
  * ===== 常用命令 =====
8
- * npx node-mobile-app test 编译并安装到设备(需连 USB,走 Metro 热更新)
9
- * npx node-mobile-app test --ios iOS 版(仅 macOS)
10
- * npx node-mobile-app build 打 debug APK(离线可用)
11
- * npx node-mobile-app build --release 打 release APK(默认 debug keystore 签名,可安装;上架需配 android.signing)
12
- * npx node-mobile-app build --ios 打 IPA(仅 macOS)
8
+ * npx node-mobile-app test 编译并安装到设备(需连 USB,走 Metro 热更新)
9
+ * npx node-mobile-app test --ios iOS 版(仅 macOS)
10
+ * npx node-mobile-app build 打 debug APK(离线可用)
11
+ * npx node-mobile-app build --release 打 release APK(默认 debug keystore 签名,可安装;上架需配 android.signing)
12
+ * npx node-mobile-app build --ios 打 IPA(仅 macOS)
13
13
  * npx node-mobile-app clean 清理构建缓存
14
14
  *
15
- * 任意命令加 --debug 可显示详细日志(优化各阶段明细、各命令完整输出)
15
+ * 任意命令加 --debug 可显示详细日志(优化各阶段明细、各命令完整输出)
16
16
  *
17
17
  * ===== 服务地址说明 =====
18
- * CLI 会自动识别 server.js 实际监听的协议和端口(拦截 listen 调用),
18
+ * CLI 会自动识别 server.js 实际监听的协议和端口(拦截 listen 调用),
19
19
  * App 使用该地址加载页面,无需在此配置;
20
20
  * 若 5 秒内未能识别,App 显示启动失败并可在页面展开详情查看原因;
21
- * 动态端口(listen(0))会自动获取系统分配的真实端口;
21
+ * 动态端口(listen(0))会自动获取系统分配的真实端口;
22
22
  */
23
23
  export default {
24
24
  // ===== 必填 =====
@@ -39,16 +39,16 @@ export default {
39
39
  enableDomStorage: true, // 是否启用 DOM Storage(localStorage 等)
40
40
  allowFileAccess: true, // 是否允许访问本地文件
41
41
  mixedContentMode: 'always', // 混合内容模式:never / always / compatibility
42
- // WebView 背景色,支持三种写法:
42
+ // WebView 背景色,支持三种写法:
43
43
  // '#ffffff' / 'white' 固定色
44
- // 'system' 跟随系统(内部用默认浅/深色)
44
+ // 'system' 跟随系统(内部用默认浅/深色)
45
45
  // { light: '#fff', dark: '#1a1a1a' } 按系统主题切换
46
46
  backgroundColor: '#ffffff',
47
- // 仅影响 HTTPS 场景下 App 是否加载自签证书的页面:
48
- // - 用 HTTP:与本开关无关
49
- // - 用 HTTPS + 真证书(如 Let's Encrypt):与本开关无关
50
- // - 用 HTTPS + 自签证书 + 已装进设备信任库:与本开关无关
51
- // - 用 HTTPS + 自签证书 + 未装设备(本地测试最常见):必须设为 true
47
+ // 仅影响 HTTPS 场景下 App 是否加载自签证书的页面:
48
+ // - 用 HTTP:与本开关无关
49
+ // - 用 HTTPS + 真证书(如 Let's Encrypt):与本开关无关
50
+ // - 用 HTTPS + 自签证书 + 已装进设备信任库:与本开关无关
51
+ // - 用 HTTPS + 自签证书 + 未装设备(本地测试最常见):必须设为 true
52
52
  // ⚠️ 开启后仅对 127.0.0.1 / localhost 放行不受信任的 HTTPS 证书,其他域名仍严格校验
53
53
  // 用于本地自签证书测试,生产环境建议关闭
54
54
  allowSelfSignedCert: false,
@@ -71,24 +71,24 @@ export default {
71
71
  // 'android.permission.CAMERA',
72
72
  // 'android.permission.READ_EXTERNAL_STORAGE',
73
73
  ],
74
- // 签名配置(可选)
74
+ // 签名配置(可选)
75
75
  //
76
- // 不配置也能打 release:模板默认用 debug keystore 签名(可安装、可本地测试,不可上架);
77
- // 需要上架时再配置下面任一种:
76
+ // 不配置也能打 release:模板默认用 debug keystore 签名(可安装、可本地测试,不可上架);
77
+ // 需要上架时再配置下面任一种:
78
78
  //
79
- // 方式一:Android SDK 自带 debug keystore(本地测试常用)
79
+ // 方式一:Android SDK 自带 debug keystore(本地测试常用)
80
80
  // keystore: %USERPROFILE%\.android\debug.keystore (Windows)
81
81
  // ~/.android/debug.keystore (macOS/Linux)
82
82
  // storePassword: 'android'
83
83
  // keyAlias: 'androiddebugkey'
84
84
  // keyPassword: 'android'
85
85
  //
86
- // 取消下方整块注释即视为「启用签名」,此时 4 项均为必填(缺项会在构建前报错)
86
+ // 取消下方整块注释即视为"启用签名",此时 4 项均为必填(缺项会在构建前报错)
87
87
  signing: {
88
- // keystore: './build/release.keystore', // 必填:keystore 路径(相对用户项目根)
89
- // storePassword: '', // 必填:keystore 密码
90
- // keyAlias: '', // 必填:密钥别名
91
- // keyPassword: '', // 必填:密钥密码(与 storePassword 相同可填同值)
88
+ // keystore: './build/release.keystore', // 必填:keystore 路径(相对用户项目根)
89
+ // storePassword: '', // 必填:keystore 密码
90
+ // keyAlias: '', // 必填:密钥别名
91
+ // keyPassword: '', // 必填:密钥密码(与 storePassword 相同可填同值)
92
92
  },
93
93
  },
94
94
 
@@ -97,10 +97,10 @@ export default {
97
97
  ios: {
98
98
  deploymentTarget: '15.1', // 最低 iOS 版本(RN 0.87 要求 15.1+)
99
99
  icon: './build/icon.png', // 应用图标(建议 1024x1024 PNG)
100
- // 签名配置(仅 macOS,发布时必填):
100
+ // 签名配置(仅 macOS,发布时必填):
101
101
  // teamId Apple Developer Team ID
102
- // provisioningProfile 描述文件路径(写入 exportOptions.plist)
103
- // certificate P12 证书路径(导入临时 keychain 供 xcodebuild 签名用)
102
+ // provisioningProfile 描述文件路径(写入 exportOptions.plist)
103
+ // certificate P12 证书路径(导入临时 keychain 供 xcodebuild 签名用)
104
104
  // certificatePassword P12 密码
105
105
  signing: {
106
106
  // teamId: '',
@@ -120,14 +120,14 @@ export default {
120
120
  // 构建时合并 JS + 清空已复制到项目根的资源 + 清平台二进制 + 清开发文件,加速启动
121
121
  // 默认开启;若构建或运行异常,可按下述方式关闭或排除问题包
122
122
  optimize: {
123
- enabled: true, // 是否启用(默认 true)
124
- // exclude: ['包名'], // 排除优化时异常或不想优化的包(默认空)
123
+ enabled: true, // 是否启用(默认 true)
124
+ // exclude: ['包名'], // 排除优化时异常或不想优化的包(默认空)
125
125
  },
126
126
 
127
127
  // ===== 扩展点 =====
128
- // 不重置的模板文件(路径相对于 node-mobile-app-build/,如 'android/app/src/main/AndroidManifest.xml')
128
+ // 不重置的模板文件(路径相对于 node-mobile-app-build/,如 'android/app/src/main/AndroidManifest.xml')
129
129
  keepFiles: [],
130
- // 额外 RN 原生依赖包名数组(用于 autolink 识别,含原生模块的包必须在此声明)
130
+ // 额外 RN 原生依赖包名数组(用于 autolink 识别,含原生模块的包必须在此声明)
131
131
  // 安装命令用 --save-dev,这些是开发依赖,不会进移动端 Node 环境
132
132
  // 示例: npm install --save-dev react-native-camera
133
133
  // extraRnDependencies: ['react-native-camera']
@@ -163,4 +163,4 @@ export default {
163
163
  '@parcel/watcher': true,
164
164
  'bcrypt': true,
165
165
  },
166
- };
166
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flun/node-mobile-app",
3
- "version": "4.0.6",
4
- "description": "将 Node.js 项目一键打包为 Android / iOS 移动应用,运行时由 @flun/nodejs-mobile-react-native 提供,无需修改你的 Node 代码;",
3
+ "version": "4.0.7",
4
+ "description": "将 Node.js 项目一键打包为 Android / iOS 移动应用,运行时由 @flun/nodejs-mobile-react-native 提供;",
5
5
  "author": "flun <open@flun.top>",
6
6
  "keywords": [
7
7
  "node-mobile",
@@ -40,10 +40,10 @@
40
40
  "postinstall": "node scripts/copy-files.js"
41
41
  },
42
42
  "dependencies": {
43
- "@babel/core": "^8.0.7",
44
- "@eslint-react/eslint-plugin": "^5.28.0",
43
+ "@babel/core": "^8.0.9",
44
+ "@eslint-react/eslint-plugin": "^5.28.8",
45
45
  "@eslint/js": "^10.0.1",
46
- "@flun/nodejs-mobile-react-native": "^2.1.0",
46
+ "@flun/nodejs-mobile-react-native": "^2.1.1",
47
47
  "@react-native/babel-preset": "^0.88.0",
48
48
  "@react-native/metro-config": "^0.88.0",
49
49
  "@react-native-community/cli": "^20.2.0",
@@ -15,7 +15,7 @@ const __filename = fileURLToPath(import.meta.url), __dirname = path.dirname(__fi
15
15
  const copyFile = () => {
16
16
  console.log(`🔍 检查 ${fileName} 文件...`), console.log(`📁 项目根目录:${targetDir}`);
17
17
  try {
18
- // 目标目录不是 Node 项目 || 包自身的 npm install(开发调试)→ 跳过
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
21
  if (fs.existsSync(targetFile)) return true; // 目标文件已存在 → 直接返回