@expo-harmony/cli 55.0.26-harmony.1 → 55.0.26-harmony.11
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 +87 -38
- package/build/args.d.ts +19 -0
- package/build/args.js +27 -0
- package/build/bin/expo-harmony.d.ts +2 -0
- package/build/bin/expo-harmony.js +1 -1
- package/build/buildHap/build.d.ts +18 -0
- package/build/buildHap/build.js +38 -0
- package/build/buildHap/index.d.ts +2 -0
- package/build/buildHap/index.js +19 -0
- package/build/buildHap/options.d.ts +7 -0
- package/build/buildHap/options.js +30 -0
- package/build/cli.d.ts +3 -0
- package/build/cli.js +40 -0
- package/build/command.d.ts +9 -0
- package/build/command.js +20 -0
- package/build/development/manifest.d.ts +1 -0
- package/build/development/manifest.js +121 -0
- package/build/development/protocol.d.ts +3 -0
- package/build/development/protocol.js +33 -0
- package/build/development/session.d.ts +5 -0
- package/build/development/session.js +66 -0
- package/build/doctor/doctor.d.ts +20 -0
- package/build/doctor/doctor.js +226 -0
- package/build/doctor/index.d.ts +2 -0
- package/build/doctor/index.js +16 -0
- package/build/doctor/options.d.ts +5 -0
- package/build/doctor/options.js +15 -0
- package/build/entry.d.ts +2 -0
- package/build/entry.js +47 -0
- package/build/errors.d.ts +12 -0
- package/build/errors.js +16 -0
- package/build/expo.d.ts +19 -0
- package/build/expo.js +53 -0
- package/build/exportEmbed/export.d.ts +18 -0
- package/build/exportEmbed/export.js +124 -0
- package/build/exportEmbed/index.d.ts +2 -0
- package/build/exportEmbed/index.js +19 -0
- package/build/exportEmbed/manifest.d.ts +44 -0
- package/build/exportEmbed/manifest.js +171 -0
- package/build/exportEmbed/options.d.ts +7 -0
- package/build/exportEmbed/options.js +22 -0
- package/build/file.d.ts +10 -0
- package/build/file.js +110 -0
- package/build/help.d.ts +1 -0
- package/build/help.js +44 -0
- package/build/index.d.ts +14 -0
- package/build/index.js +6 -5
- package/build/internal/development.d.ts +1 -0
- package/build/internal/development.js +8 -0
- package/build/internal/export.d.ts +1 -0
- package/build/internal/export.js +80 -0
- package/build/internal/prebuild.d.ts +3 -0
- package/build/internal/prebuild.js +9 -0
- package/build/log.d.ts +4 -0
- package/build/log.js +6 -0
- package/build/modules/index.d.ts +2 -0
- package/build/modules/index.js +16 -0
- package/build/modules/modules.d.ts +42 -0
- package/build/modules/modules.js +84 -0
- package/build/modules/options.d.ts +10 -0
- package/build/modules/options.js +45 -0
- package/build/native/bare.d.ts +4 -0
- package/build/native/bare.js +130 -0
- package/build/native/build.d.ts +15 -0
- package/build/native/build.js +69 -0
- package/build/native/cache.d.ts +11 -0
- package/build/native/cache.js +73 -0
- package/build/native/hvigor.d.ts +3 -0
- package/build/native/hvigor.js +83 -0
- package/build/native/install.d.ts +5 -0
- package/build/native/install.js +13 -0
- package/build/native/prepareProject.d.ts +11 -0
- package/build/native/prepareProject.js +68 -0
- package/build/native/project.d.ts +8 -0
- package/build/native/project.js +58 -0
- package/build/native/toolchain.d.ts +16 -0
- package/build/native/toolchain.js +172 -0
- package/build/native/types.d.ts +47 -0
- package/build/native/types.js +2 -0
- package/build/path.d.ts +4 -0
- package/build/path.js +27 -0
- package/build/prebuild/check.d.ts +10 -0
- package/build/prebuild/check.js +151 -0
- package/build/prebuild/clean.d.ts +2 -0
- package/build/prebuild/clean.js +50 -0
- package/build/prebuild/index.d.ts +2 -0
- package/build/prebuild/index.js +51 -0
- package/build/prebuild/options.d.ts +10 -0
- package/build/prebuild/options.js +50 -0
- package/build/prebuild/prebuild.d.ts +6 -0
- package/build/prebuild/prebuild.js +70 -0
- package/build/prebuild/template.d.ts +8 -0
- package/build/prebuild/template.js +86 -0
- package/build/process.d.ts +41 -0
- package/build/process.js +324 -0
- package/build/profile.d.ts +1 -0
- package/build/profile.js +12 -0
- package/build/project.d.ts +2 -0
- package/build/project.js +23 -0
- package/build/projectLock.d.ts +2 -0
- package/build/projectLock.js +185 -0
- package/build/run/devices.d.ts +29 -0
- package/build/run/devices.js +223 -0
- package/build/run/emulators.d.ts +13 -0
- package/build/run/emulators.js +82 -0
- package/build/run/index.d.ts +2 -0
- package/build/run/index.js +25 -0
- package/build/run/metro.d.ts +21 -0
- package/build/run/metro.js +162 -0
- package/build/run/options.d.ts +14 -0
- package/build/run/options.js +62 -0
- package/build/run/run.d.ts +46 -0
- package/build/run/run.js +135 -0
- package/build/runtime/config.d.ts +37 -0
- package/build/runtime/config.js +169 -0
- package/build/runtime/contract.d.ts +5 -0
- package/build/runtime/contract.js +109 -0
- package/build/runtime/fingerprint.d.ts +1 -0
- package/build/runtime/fingerprint.js +49 -0
- package/build/runtime/index.d.ts +2 -0
- package/build/runtime/index.js +50 -0
- package/build/start/index.d.ts +2 -0
- package/build/start/index.js +38 -0
- package/build/start/options.d.ts +9 -0
- package/build/start/options.js +38 -0
- package/build/updates/export.d.ts +14 -0
- package/build/updates/export.js +46 -0
- package/build/updates/index.d.ts +2 -0
- package/build/updates/index.js +96 -0
- package/build/upstream.d.ts +9 -0
- package/build/upstream.js +17 -0
- package/package.json +21 -5
- package/src/bin/expo-harmony.ts +2 -2
- package/src/buildHap/build.ts +13 -105
- package/src/buildHap/index.ts +19 -0
- package/src/cli.ts +27 -225
- package/src/command.ts +24 -0
- package/src/development/manifest.ts +127 -0
- package/src/development/protocol.ts +42 -0
- package/src/development/session.ts +83 -0
- package/src/doctor/doctor.ts +67 -62
- package/src/doctor/index.ts +16 -0
- package/src/exportEmbed/export.ts +73 -72
- package/src/exportEmbed/index.ts +17 -0
- package/src/file.ts +20 -11
- package/src/help.ts +41 -0
- package/src/index.ts +5 -3
- package/src/internal/development.ts +8 -0
- package/src/internal/export.ts +82 -0
- package/src/internal/prebuild.ts +3 -0
- package/src/log.ts +5 -0
- package/src/modules/index.ts +16 -0
- package/src/native/bare.ts +145 -0
- package/src/native/build.ts +83 -0
- package/src/{run → native}/cache.ts +10 -9
- package/src/native/hvigor.ts +88 -0
- package/src/native/install.ts +17 -0
- package/src/native/prepareProject.ts +99 -0
- package/src/native/project.ts +88 -0
- package/src/{tools.ts → native/toolchain.ts} +38 -140
- package/src/native/types.ts +44 -0
- package/src/prebuild/check.ts +56 -31
- package/src/prebuild/clean.ts +3 -3
- package/src/prebuild/index.ts +56 -0
- package/src/prebuild/prebuild.ts +11 -1
- package/src/prebuild/template.ts +2 -2
- package/src/process.ts +175 -64
- package/src/profile.ts +13 -0
- package/src/projectLock.ts +9 -1
- package/src/run/devices.ts +24 -11
- package/src/run/emulators.ts +4 -6
- package/src/run/index.ts +25 -0
- package/src/run/metro.ts +10 -1
- package/src/run/options.ts +2 -0
- package/src/run/run.ts +63 -129
- package/src/runtime/config.ts +207 -0
- package/src/runtime/contract.ts +167 -0
- package/src/runtime/fingerprint.ts +49 -0
- package/src/runtime/index.ts +54 -0
- package/src/start/index.ts +37 -0
- package/src/start/options.ts +12 -0
- package/src/updates/export.ts +60 -0
- package/src/updates/index.ts +89 -0
- package/src/buildHap/common.ts +0 -145
- package/src/run/install.ts +0 -41
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[**GitHub 仓库**](https://github.com/renbaoshuo/expo-harmony/tree/master/packages/cli)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Expo 项目的 HarmonyOS 命令行工具,用于启动 Metro 开发服务、生成原生工程,以及构建、安装和运行应用。
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
@@ -10,29 +10,31 @@
|
|
|
10
10
|
npm install --save-dev @expo-harmony/cli
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
CLI
|
|
13
|
+
请将 CLI 安装在应用项目中,并使用 Node.js 20 或更高版本。Metro 配置需要接入 `@expo-harmony/metro-config`。
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
通过 Expo 配置生成原生工程的项目(CNG),还需要在 Expo 配置中注册 `@expo-harmony/prebuild-config` 插件。
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
手工维护 `harmony/` 工程的项目可以使用 bare 模式,按原生工程中的配置构建和运行,无需注册 prebuild 插件。
|
|
18
|
+
|
|
19
|
+
构建 HAP 需要 HarmonyOS SDK(含 HMS 和 OpenHarmony 组件)、OHPM 和 Hvigor。连接设备或模拟器还需要 HDC。可以运行 `npx expo-harmony doctor` 检查环境。
|
|
18
20
|
|
|
19
21
|
## 命令
|
|
20
22
|
|
|
21
23
|
| 命令 | 作用 |
|
|
22
24
|
| --- | --- |
|
|
23
|
-
| `expo-harmony start` | 启动
|
|
25
|
+
| `expo-harmony start` | 启动 Metro 开发服务 |
|
|
24
26
|
| `expo-harmony prebuild` | 使用 Expo CNG 生成或更新 HarmonyOS 原生工程 |
|
|
25
|
-
| `expo-harmony prebuild --clean` |
|
|
26
|
-
| `expo-harmony prebuild --check` |
|
|
27
|
-
| `expo-harmony build` | 构建 HAP
|
|
28
|
-
| `expo-harmony doctor` |
|
|
29
|
-
| `expo-harmony modules list` |
|
|
30
|
-
| `expo-harmony modules inspect` |
|
|
31
|
-
| `expo-harmony modules verify` |
|
|
27
|
+
| `expo-harmony prebuild --clean` | 删除并重新生成由 CNG 管理的原生工程 |
|
|
28
|
+
| `expo-harmony prebuild --check` | 检查 CNG 生成的文件是否需要更新 |
|
|
29
|
+
| `expo-harmony build` | 构建 HAP |
|
|
30
|
+
| `expo-harmony doctor` | 检查项目配置、依赖、开发环境和签名配置 |
|
|
31
|
+
| `expo-harmony modules list` | 列出项目中的 Expo Modules 及其来源 |
|
|
32
|
+
| `expo-harmony modules inspect` | 查看模块的 Harmony 配置和原生文件路径 |
|
|
33
|
+
| `expo-harmony modules verify` | 检查模块配置、依赖冲突和原生文件 |
|
|
32
34
|
| `expo-harmony export:embed` | 导出 Hermes 字节码、资源和 Source Map 并写入原生工程 |
|
|
33
|
-
| `expo-harmony run` | 构建 HAP
|
|
35
|
+
| `expo-harmony run` | 构建 HAP,在设备或模拟器上安装并启动应用 |
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
所有命令都可以指定项目路径,`modules` 的路径写在子命令后面。不指定时,CLI 从当前目录向上查找最近的项目根目录:
|
|
36
38
|
|
|
37
39
|
```sh
|
|
38
40
|
npx expo-harmony start ./my-app
|
|
@@ -50,11 +52,13 @@ npx expo-harmony start --port 8082
|
|
|
50
52
|
npx expo-harmony start --clear
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
`start`
|
|
55
|
+
`start` 启动 Metro,为 Debug 应用提供 JS Bundle。运行这个命令不需要 HarmonyOS SDK 或已连接的设备。日志显示在当前终端,按 Ctrl+C 退出。
|
|
56
|
+
|
|
57
|
+
可以用 `--port <number>` 指定端口,默认是 `8081`。`--reset-cache` 清除 Metro 缓存,也可以写成 `--clear` 或 `-c`。
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
端口上已有 Metro 时,命令会提示并退出,已有服务继续运行。要清除它的缓存,需要先停止服务,再带缓存选项启动。端口被其他进程占用时,命令会报错。
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
要构建并启动应用,在另一个终端运行 `npx expo-harmony run --no-bundler`。如果指定了端口,两个命令应使用相同的 `--port`。
|
|
58
62
|
|
|
59
63
|
## Prebuild
|
|
60
64
|
|
|
@@ -64,9 +68,19 @@ npx expo-harmony prebuild --clean
|
|
|
64
68
|
npx expo-harmony prebuild --check
|
|
65
69
|
```
|
|
66
70
|
|
|
67
|
-
`prebuild`
|
|
71
|
+
`prebuild` 根据 Expo 配置生成或更新 HarmonyOS 原生工程,仅用于 CNG 项目。它使用 `@expo-harmony/template` 模板,不接受 `--platform` 和 `--template` 选项。
|
|
72
|
+
|
|
73
|
+
命令会先检查项目配置和依赖,有错误时停止。这一步不要求安装好原生构建工具。
|
|
74
|
+
|
|
75
|
+
依赖安装支持以下选项:
|
|
76
|
+
|
|
77
|
+
- `--no-install`:跳过依赖安装。
|
|
78
|
+
- `--npm`、`--yarn`、`--pnpm` 或 `--bun`:选择包管理器,最多指定一个。
|
|
79
|
+
- `--skip-dependency-update <packages>`:跳过指定依赖的版本更新。
|
|
68
80
|
|
|
69
|
-
`--clean`
|
|
81
|
+
`--clean` 删除原生工程后重新生成。如果工程缺少 CNG 清单或模板标记,或者目录是符号链接、位于项目之外,命令会拒绝删除。
|
|
82
|
+
|
|
83
|
+
`--check` 比较现有文件与当前配置生成的文件,不修改项目文件。没有差异时退出码为 0;有差异时列出变更,退出码为 2。它不能与其他会修改工程的选项一起使用。
|
|
70
84
|
|
|
71
85
|
## Build
|
|
72
86
|
|
|
@@ -76,9 +90,11 @@ npx expo-harmony build --variant release
|
|
|
76
90
|
npx expo-harmony build --sync
|
|
77
91
|
```
|
|
78
92
|
|
|
79
|
-
`build`
|
|
93
|
+
`build` 构建 HAP,并输出文件路径。执行这个命令不需要连接设备,构建后也不会安装或启动应用。
|
|
94
|
+
|
|
95
|
+
默认构建 Debug 版本。使用 `--variant release` 构建 Release 版本时,命令会将 JS Bundle 和资源打包进应用。
|
|
80
96
|
|
|
81
|
-
|
|
97
|
+
项目没有原生工程时,命令会先运行 prebuild。已有 CNG 工程时,会检查生成的文件是否需要更新;有差异时停止构建,可以使用 `--sync` 重新生成后继续构建。bare 工程按现有原生配置构建,不支持 `--sync`。
|
|
82
98
|
|
|
83
99
|
## Doctor
|
|
84
100
|
|
|
@@ -86,7 +102,18 @@ npx expo-harmony build --sync
|
|
|
86
102
|
npx expo-harmony doctor
|
|
87
103
|
```
|
|
88
104
|
|
|
89
|
-
`doctor`
|
|
105
|
+
`doctor` 逐项检查:
|
|
106
|
+
|
|
107
|
+
- CNG 工程的 Expo `harmony` 配置和 prebuild 插件;bare 工程的包名、模块、Ability 和构建配置
|
|
108
|
+
- 签名配置文件及其引用
|
|
109
|
+
- Metro 配置是否启用 Harmony
|
|
110
|
+
- RNOH 运行时和 CLI、`@expo-harmony/expo-modules-autolinking` 是否已安装
|
|
111
|
+
- Expo Modules 的 Harmony 支持和配置
|
|
112
|
+
- HarmonyOS SDK 是否完整
|
|
113
|
+
- HDC、OHPM、Hvigor 是否可用
|
|
114
|
+
- 原生工程的 Hvigor 文件和自动链接配置
|
|
115
|
+
|
|
116
|
+
有 `error` 级别的问题时,命令以非零退出码结束。CNG 工程未配置外部签名文件时会显示警告,仍可构建未签名的 HAP。证书是否有效,需要在构建时由 Hvigor 检查。
|
|
90
117
|
|
|
91
118
|
## Modules
|
|
92
119
|
|
|
@@ -96,9 +123,13 @@ npx expo-harmony modules inspect --package expo-linear-gradient
|
|
|
96
123
|
npx expo-harmony modules verify
|
|
97
124
|
```
|
|
98
125
|
|
|
99
|
-
`list`
|
|
126
|
+
`list` 显示模块的包名、版本和来源,并标出不支持 Harmony 的模块、重复版本和缺失的依赖。
|
|
100
127
|
|
|
101
|
-
|
|
128
|
+
`inspect` 查看模块的 Harmony 配置,包括包目录、ArkTS 模块和 HAR 路径。可以用 `--package <name>` 筛选单个模块,找不到时会报错。
|
|
129
|
+
|
|
130
|
+
`verify` 检查模块配置、依赖冲突和原生文件。有 `error` 级别的问题时,以非零退出码结束。
|
|
131
|
+
|
|
132
|
+
三个子命令都支持 `--variant debug|release`,也可以用 `--native-modules-dir <dir>` 指定应用内的模块目录,默认是 `./modules`。
|
|
102
133
|
|
|
103
134
|
## Export
|
|
104
135
|
|
|
@@ -108,7 +139,11 @@ npx expo-harmony export:embed --check
|
|
|
108
139
|
npx expo-harmony export:embed --reset-cache
|
|
109
140
|
```
|
|
110
141
|
|
|
111
|
-
`export:embed`
|
|
142
|
+
`export:embed` 导出 Release 应用使用的 Hermes 字节码、资源和 Source Map。命令会先检查项目配置,再生成并校验导出文件。
|
|
143
|
+
|
|
144
|
+
Bundle 和资源写入原生模块的 `rawfile` 目录,Source Map 写入 `.expo/harmony/export/`。导出清单位于 `.expo/harmony/export-manifest.json`。
|
|
145
|
+
|
|
146
|
+
`--check` 检查已有导出文件是否与清单一致,不重新导出。`--reset-cache` 清除 Metro 转换缓存后重新导出。
|
|
112
147
|
|
|
113
148
|
## Run
|
|
114
149
|
|
|
@@ -119,32 +154,46 @@ npx expo-harmony run \
|
|
|
119
154
|
--port 8081
|
|
120
155
|
```
|
|
121
156
|
|
|
122
|
-
`run`
|
|
157
|
+
`run` 构建 HAP,在设备或模拟器上安装并启动应用。它使用项目中已有的原生工程;如果工程不存在,会先运行 prebuild。
|
|
123
158
|
|
|
124
159
|
常用选项:
|
|
125
160
|
|
|
126
|
-
- `--variant debug|release
|
|
127
|
-
- `--device <id-or-name>`:选择已连接的 HDC 设备,或按完整名称启动本地模拟器,例如 `--device "Pura 90 Pro"
|
|
128
|
-
- `--port <number
|
|
129
|
-
- `--no-bundler
|
|
161
|
+
- `--variant debug|release`:构建模式,默认 `debug`。Release 将 JS Bundle 和资源打包进应用,不启动 Metro。
|
|
162
|
+
- `--device <id-or-name>`:选择已连接的 HDC 设备,或按完整名称启动本地模拟器,例如 `--device "Pura 90 Pro"`。有多个候选目标时必须指定。
|
|
163
|
+
- `--port <number>`:电脑上的 Metro 端口,默认 `8081`。Debug 模式下会配置设备端口映射,让应用连接到这个端口。
|
|
164
|
+
- `--no-bundler`:使用已运行的 Metro,找不到服务时报错。不加此选项时,会启动 Metro,或复用端口上已有的 Metro。
|
|
130
165
|
- `--reset-cache`:Debug 模式下启动 Metro 时清除 Metro 缓存;Release 模式下清除生产导出缓存。
|
|
131
|
-
- `--no-install
|
|
132
|
-
- `--app-id <bundleName
|
|
133
|
-
- `--sync
|
|
166
|
+
- `--no-install`:仍会构建 HAP,但跳过安装,启动设备上已有的应用。
|
|
167
|
+
- `--app-id <bundleName>`:指定要启动的应用;与项目配置中的包名不同时,必须同时使用 `--no-install`。
|
|
168
|
+
- `--sync`:构建前重新运行 prebuild,仅用于 CNG 项目。bare 项目不支持此选项。
|
|
134
169
|
|
|
135
|
-
Debug
|
|
170
|
+
Debug 模式下,如果 Metro 由这个命令启动,日志会显示在当前终端,按 Ctrl+C 退出。
|
|
136
171
|
|
|
137
|
-
未指定 `--device`
|
|
172
|
+
未指定 `--device` 时,优先使用已连接的设备。没有连接的设备时,先等待正在启动的模拟器;如果也没有正在启动的模拟器,就启动本地唯一的模拟器。有多个可选设备或模拟器时,需要用 `--device` 指定。
|
|
138
173
|
|
|
139
|
-
|
|
174
|
+
按名称选择或自动启动模拟器时,CLI 会等待模拟器开机并连接 HDC,最多等待 120 秒。退出 CLI 或 Metro 后,模拟器继续运行。
|
|
175
|
+
|
|
176
|
+
自动启动模拟器需要 DevEco Studio 6.1.0 或更高版本,并且已在 Device Manager 中创建模拟器。启动日志位于 `.expo/harmony/emulator.log`。启动失败时,可以先在 DevEco Studio 中打开模拟器,处理首次使用协议或登录提示。需要登录开发者账号的模拟器版本不支持从命令行启动,详见 [华为的模拟器文档](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-emulator-command-line)。
|
|
140
177
|
|
|
141
178
|
## 工具链与环境变量
|
|
142
179
|
|
|
143
|
-
|
|
180
|
+
CLI 先读取环境变量指定的工具路径,再查找 DevEco Studio 的安装目录,最后从 `PATH` 查找。
|
|
181
|
+
|
|
182
|
+
找不到工具或需要使用其他版本时,可以设置以下变量:
|
|
183
|
+
|
|
184
|
+
| 环境变量 | 用途 |
|
|
185
|
+
| --- | --- |
|
|
186
|
+
| `HARMONY_HDC` | 指定 HDC 路径 |
|
|
187
|
+
| `HARMONY_EMULATOR` | 指定模拟器命令行工具路径 |
|
|
188
|
+
| `HARMONY_OHPM` | 指定 OHPM 路径 |
|
|
189
|
+
| `HARMONY_HVIGORW` | 指定 Hvigor 路径 |
|
|
190
|
+
| `HARMONY_NODE` | 指定 OHPM、Hvigor 使用的 Node.js 路径 |
|
|
191
|
+
| `EXPO_HARMONY_NODE` | 指定原生构建过程中运行 Expo Harmony CLI 的 Node.js 路径,要求 Node.js 20 或更高版本 |
|
|
192
|
+
| `DEVECO_SDK_HOME`、`HARMONY_HOME` 或 `OHOS_SDK_HOME` | 指定 HarmonyOS SDK 根目录 |
|
|
144
193
|
|
|
145
194
|
## Author
|
|
146
195
|
|
|
147
196
|
**expo-harmony** © [Baoshuo](https://github.com/renbaoshuo), Released under the MIT License.<br>
|
|
148
197
|
Authored and maintained by Baoshuo with help from [contributors](https://github.com/renbaoshuo/expo-harmony/contributors).
|
|
149
198
|
|
|
150
|
-
> [Personal Website](https://baoshuo.ren) · [Blog](https://blog.baoshuo.ren) · GitHub [@renbaoshuo](https://github.com/renbaoshuo) · Twitter [@
|
|
199
|
+
> [Personal Website](https://baoshuo.ren) · [Blog](https://blog.baoshuo.ren) · GitHub [@renbaoshuo](https://github.com/renbaoshuo) · Twitter [@renbaoshuo](https://twitter.com/renbaoshuo)
|
package/build/args.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type Option = {
|
|
2
|
+
short?: string;
|
|
3
|
+
type: 'boolean' | 'string';
|
|
4
|
+
};
|
|
5
|
+
type Options = Record<string, Option>;
|
|
6
|
+
type Values<T extends Options> = {
|
|
7
|
+
[K in keyof T]?: T[K]['type'] extends 'boolean' ? boolean : string;
|
|
8
|
+
};
|
|
9
|
+
declare const CommonOptions: {
|
|
10
|
+
readonly help: {
|
|
11
|
+
readonly short: "h";
|
|
12
|
+
readonly type: "boolean";
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
declare function parseArgs<T extends Options>(options: T, argv: string[]): {
|
|
16
|
+
positionals: string[];
|
|
17
|
+
values: Values<T>;
|
|
18
|
+
};
|
|
19
|
+
export { CommonOptions, parseArgs };
|
package/build/args.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CommonOptions = void 0;
|
|
4
|
+
exports.parseArgs = parseArgs;
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
const CommonOptions = {
|
|
8
|
+
help: { short: 'h', type: 'boolean' },
|
|
9
|
+
};
|
|
10
|
+
exports.CommonOptions = CommonOptions;
|
|
11
|
+
function parseArgs(options, argv) {
|
|
12
|
+
try {
|
|
13
|
+
return (0, node_util_1.parseArgs)({
|
|
14
|
+
allowPositionals: true,
|
|
15
|
+
args: argv,
|
|
16
|
+
options,
|
|
17
|
+
strict: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
catch (cause) {
|
|
21
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
22
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', message, {
|
|
23
|
+
cause,
|
|
24
|
+
operation: 'parse-arguments',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type NativeBuildResult } from '../native/build';
|
|
2
|
+
export interface HarmonyBuildOptions {
|
|
3
|
+
io?: Pick<Console, 'error' | 'log' | 'warn'>;
|
|
4
|
+
sync?: boolean;
|
|
5
|
+
variant?: 'debug' | 'release';
|
|
6
|
+
}
|
|
7
|
+
export interface HarmonyBuildResult extends NativeBuildResult {
|
|
8
|
+
bundleName: string;
|
|
9
|
+
headless: true;
|
|
10
|
+
installed: false;
|
|
11
|
+
launched: false;
|
|
12
|
+
ok: true;
|
|
13
|
+
schemaVersion: 1;
|
|
14
|
+
steps: Record<string, number>;
|
|
15
|
+
variant: 'debug' | 'release';
|
|
16
|
+
}
|
|
17
|
+
declare function buildHarmonyAsync(root: string, options?: HarmonyBuildOptions): Promise<HarmonyBuildResult>;
|
|
18
|
+
export { buildHarmonyAsync };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildHarmonyAsync = buildHarmonyAsync;
|
|
4
|
+
const projectLock_1 = require("../projectLock");
|
|
5
|
+
const build_1 = require("../native/build");
|
|
6
|
+
const prepareProject_1 = require("../native/prepareProject");
|
|
7
|
+
const toolchain_1 = require("../native/toolchain");
|
|
8
|
+
async function buildHarmonyUnlockedAsync(root, options = {}) {
|
|
9
|
+
const settings = {
|
|
10
|
+
io: options.io || console,
|
|
11
|
+
requireDeviceTools: false,
|
|
12
|
+
sync: Boolean(options.sync),
|
|
13
|
+
variant: options.variant || 'debug',
|
|
14
|
+
};
|
|
15
|
+
const steps = {
|
|
16
|
+
device: 0,
|
|
17
|
+
install: 0,
|
|
18
|
+
launch: 0,
|
|
19
|
+
metro: 0,
|
|
20
|
+
metroPort: 0,
|
|
21
|
+
};
|
|
22
|
+
const plan = await (0, prepareProject_1.prepareNativeProjectAsync)(root, settings, steps);
|
|
23
|
+
const built = await (0, build_1.buildNativeAsync)(root, plan, (0, toolchain_1.resolveHarmonyToolchain)(), settings, steps);
|
|
24
|
+
return {
|
|
25
|
+
bundleName: plan.bundleName,
|
|
26
|
+
...built,
|
|
27
|
+
headless: true,
|
|
28
|
+
installed: false,
|
|
29
|
+
launched: false,
|
|
30
|
+
ok: true,
|
|
31
|
+
schemaVersion: 1,
|
|
32
|
+
steps,
|
|
33
|
+
variant: settings.variant,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function buildHarmonyAsync(root, options = {}) {
|
|
37
|
+
return (0, projectLock_1.withHarmonyProjectLockAsync)(root, 'build', () => buildHarmonyUnlockedAsync(root, options));
|
|
38
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.command = void 0;
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const options_1 = require("./options");
|
|
6
|
+
const command = async (argv, io) => {
|
|
7
|
+
const args = (0, command_1.parseCommandArgs)(options_1.parseBuildArgs, argv, io);
|
|
8
|
+
if (!args)
|
|
9
|
+
return 0;
|
|
10
|
+
const { projectRoot: root, options } = args;
|
|
11
|
+
const { buildHarmonyAsync } = await import('./build.js');
|
|
12
|
+
const result = await buildHarmonyAsync(root, {
|
|
13
|
+
...options,
|
|
14
|
+
io,
|
|
15
|
+
});
|
|
16
|
+
io.log(`Built ${result.variant} HAP at ${result.hapPath}; no device actions were performed.`);
|
|
17
|
+
return 0;
|
|
18
|
+
};
|
|
19
|
+
exports.command = command;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseBuildArgs = parseBuildArgs;
|
|
4
|
+
const args_1 = require("../args");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const BuildOptions = {
|
|
7
|
+
...args_1.CommonOptions,
|
|
8
|
+
sync: { type: 'boolean' },
|
|
9
|
+
variant: { type: 'string' },
|
|
10
|
+
};
|
|
11
|
+
function parseBuildArgs(argv) {
|
|
12
|
+
const { positionals, values } = (0, args_1.parseArgs)(BuildOptions, argv);
|
|
13
|
+
if (positionals.length > 1) {
|
|
14
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Unexpected build positional argument: ${positionals[1]}`, {
|
|
15
|
+
operation: 'parse-arguments',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const variant = values.variant || 'debug';
|
|
19
|
+
if (variant !== 'debug' && variant !== 'release') {
|
|
20
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--variant must be debug or release.', {
|
|
21
|
+
operation: 'parse-arguments',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
help: Boolean(values.help),
|
|
26
|
+
project: positionals[0],
|
|
27
|
+
sync: Boolean(values.sync),
|
|
28
|
+
variant: variant,
|
|
29
|
+
};
|
|
30
|
+
}
|
package/build/cli.d.ts
ADDED
package/build/cli.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runAsync = runAsync;
|
|
4
|
+
exports.mainAsync = mainAsync;
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const help_1 = require("./help");
|
|
7
|
+
const commands = {
|
|
8
|
+
'runtime': () => import('./runtime/index.js'),
|
|
9
|
+
'start': () => import('./start/index.js'),
|
|
10
|
+
'prebuild': () => import('./prebuild/index.js'),
|
|
11
|
+
'build': () => import('./buildHap/index.js'),
|
|
12
|
+
'doctor': () => import('./doctor/index.js'),
|
|
13
|
+
'modules': () => import('./modules/index.js'),
|
|
14
|
+
'export': () => import('./updates/index.js'),
|
|
15
|
+
'export:embed': () => import('./exportEmbed/index.js'),
|
|
16
|
+
'run': () => import('./run/index.js'),
|
|
17
|
+
};
|
|
18
|
+
async function runAsync(argv = process.argv.slice(2), io = console) {
|
|
19
|
+
process.env.EXPO_HARMONY = '1';
|
|
20
|
+
process.env.EXPO_METRO_TARGET = 'harmony';
|
|
21
|
+
if (argv.length === 0 || (argv.length === 1 && ['--help', '-h'].includes(argv[0]))) {
|
|
22
|
+
io.log(help_1.Help);
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
const name = argv[0];
|
|
26
|
+
if (!Object.hasOwn(commands, name)) {
|
|
27
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Unknown command: ${name}`, { operation: 'parse-arguments' });
|
|
28
|
+
}
|
|
29
|
+
const { command } = await commands[name]();
|
|
30
|
+
return command(argv.slice(1), io);
|
|
31
|
+
}
|
|
32
|
+
async function mainAsync(argv = process.argv.slice(2)) {
|
|
33
|
+
try {
|
|
34
|
+
process.exitCode = await runAsync(argv);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error(`[${error.code || 'ERR_HARMONY_UNKNOWN'}] ${error.message}`);
|
|
38
|
+
process.exitCode = error.exitCode || 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Log } from './log';
|
|
2
|
+
export type Command = (argv: string[], io: Log) => Promise<number>;
|
|
3
|
+
export declare function parseCommandArgs<T extends {
|
|
4
|
+
help: boolean;
|
|
5
|
+
project?: string;
|
|
6
|
+
}>(parse: (argv: string[]) => T, argv: string[], io: Log): {
|
|
7
|
+
options: T;
|
|
8
|
+
projectRoot: string;
|
|
9
|
+
} | null;
|
package/build/command.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseCommandArgs = parseCommandArgs;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const help_1 = require("./help");
|
|
9
|
+
const project_1 = require("./project");
|
|
10
|
+
function parseCommandArgs(parse, argv, io) {
|
|
11
|
+
const options = parse(argv);
|
|
12
|
+
if (options.help) {
|
|
13
|
+
io.log(help_1.Help);
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
options,
|
|
18
|
+
projectRoot: (0, project_1.resolveProject)(options.project ? node_path_1.default.resolve(options.project) : process.cwd()),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installHarmonyManifest(root: string): void;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.installHarmonyManifest = installHarmonyManifest;
|
|
7
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const node_module_1 = require("node:module");
|
|
9
|
+
const errors_1 = require("../errors");
|
|
10
|
+
const expo_1 = require("../expo");
|
|
11
|
+
const project_1 = require("../native/project");
|
|
12
|
+
const contract_1 = require("../runtime/contract");
|
|
13
|
+
const protocol_1 = require("./protocol");
|
|
14
|
+
async function createManifestResponseAsync(middleware, options, require) {
|
|
15
|
+
const { ExpoGoManifestHandlerMiddleware: Manifest, ResponseContentType: Types } = require('./build/src/start/server/middleware/ExpoGoManifestHandlerMiddleware');
|
|
16
|
+
const { getCodeSigningInfoAsync, signManifestString } = require('./build/src/utils/codesigning');
|
|
17
|
+
const { serializeDictionary } = require('structured-headers');
|
|
18
|
+
const { exp, expoGoConfig, bundleUrl, hostUri } = await middleware._resolveProjectSettingsAsync(options);
|
|
19
|
+
const requirements = await (0, contract_1.resolveRuntimeRequirementsAsync)(middleware.projectRoot);
|
|
20
|
+
const signing = await getCodeSigningInfoAsync(exp, options.expectSignature, middleware.options.privateKeyPath);
|
|
21
|
+
const scope = await Manifest.getScopeKeyAsync({ slug: exp.slug, codeSigningInfo: signing });
|
|
22
|
+
const bundle = new URL(bundleUrl);
|
|
23
|
+
bundle.searchParams.set('transform.engine', 'hermes');
|
|
24
|
+
bundle.searchParams.set('transform.bytecode', '0');
|
|
25
|
+
bundle.searchParams.set('unstable_transformProfile', 'hermes-stable');
|
|
26
|
+
bundle.searchParams.set('lazy', 'false');
|
|
27
|
+
const name = exp.harmony?.bundleName || (await (0, project_1.resolveHarmonyBuildPlanAsync)(middleware.projectRoot)).bundleName;
|
|
28
|
+
const id = exp.extra?.eas?.projectId;
|
|
29
|
+
const address = (0, protocol_1.canonicalHarmonyManifestURL)(`${bundle.origin}${options.harmonyManifestPath ?? `${protocol_1.HarmonyManifestPath}?platform=harmony`}`);
|
|
30
|
+
const manifest = JSON.stringify({
|
|
31
|
+
id: node_crypto_1.default.randomUUID(),
|
|
32
|
+
createdAt: new Date().toISOString(),
|
|
33
|
+
runtimeVersion: requirements.runtimeVersion,
|
|
34
|
+
launchAsset: { key: 'bundle', contentType: 'application/javascript', url: bundle.toString() },
|
|
35
|
+
assets: [],
|
|
36
|
+
metadata: {},
|
|
37
|
+
extra: {
|
|
38
|
+
eas: { projectId: id ?? undefined },
|
|
39
|
+
expoClient: { ...exp, hostUri },
|
|
40
|
+
expoGo: expoGoConfig,
|
|
41
|
+
scopeKey: scope,
|
|
42
|
+
harmony: {
|
|
43
|
+
projectId: id || `@${exp.owner || 'anonymous'}/${exp.slug}/${name}`,
|
|
44
|
+
manifestUrl: address,
|
|
45
|
+
launchLink: (0, protocol_1.createHarmonyLaunchLink)(address),
|
|
46
|
+
requirements,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
const signature = signing
|
|
51
|
+
? serializeDictionary(new Map(Object.entries({
|
|
52
|
+
keyid: signing.keyId,
|
|
53
|
+
sig: signManifestString(manifest, signing),
|
|
54
|
+
alg: 'rsa-v1_5-sha256',
|
|
55
|
+
}).map(([key, value]) => [key, [value, new Map()]])))
|
|
56
|
+
: undefined;
|
|
57
|
+
if (options.responseContentType === Types.MULTIPART_MIXED) {
|
|
58
|
+
return middleware.encodeFormDataAsync({
|
|
59
|
+
stringifiedManifest: manifest,
|
|
60
|
+
manifestPartHeaders: signature ? { 'expo-signature': signature } : undefined,
|
|
61
|
+
certificateChainBody: signing?.certificateChainForResponse.join('\n') ?? null,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const headers = middleware.getDefaultResponseHeaders();
|
|
65
|
+
headers.set('content-type', Manifest.getContentTypeForResponseContentType(options.responseContentType));
|
|
66
|
+
if (signature)
|
|
67
|
+
headers.set('expo-signature', signature);
|
|
68
|
+
return new Response(manifest, { headers });
|
|
69
|
+
}
|
|
70
|
+
function installHarmonyManifest(root) {
|
|
71
|
+
const expo = (0, node_module_1.createRequire)((0, expo_1.resolveExpoCli)(root).cliPath);
|
|
72
|
+
const require = (0, node_module_1.createRequire)(expo.resolve('@expo/cli/package.json'));
|
|
73
|
+
const version = require('./package.json').version;
|
|
74
|
+
if (version !== '55.0.32') {
|
|
75
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPO_CLI_UNSUPPORTED', `The Harmony manifest adapter requires @expo/cli 55.0.32; found ${version}.`, { operation: 'development-manifest' });
|
|
76
|
+
}
|
|
77
|
+
const { BundlerDevServer } = require('./build/src/start/server/BundlerDevServer');
|
|
78
|
+
const { parsePlatformHeader } = require('./build/src/start/server/middleware/resolvePlatform');
|
|
79
|
+
const create = BundlerDevServer.prototype.getManifestMiddlewareAsync;
|
|
80
|
+
// Reuse Expo's terminal QR renderer and dev-session publishing with our
|
|
81
|
+
// registered link, rather than emitting a second, incompatible QR code.
|
|
82
|
+
const nativeURL = BundlerDevServer.prototype.getNativeRuntimeUrl;
|
|
83
|
+
const redirectURL = BundlerDevServer.prototype.getRedirectUrl;
|
|
84
|
+
BundlerDevServer.prototype.getNativeRuntimeUrl = function (options = {}) {
|
|
85
|
+
const address = this.getUrlCreator().constructUrl({ ...options, scheme: 'http' });
|
|
86
|
+
if (!address)
|
|
87
|
+
return nativeURL.call(this, options);
|
|
88
|
+
const manifest = new URL(protocol_1.HarmonyManifestPath, address);
|
|
89
|
+
manifest.searchParams.set('platform', 'harmony');
|
|
90
|
+
return (0, protocol_1.createHarmonyLaunchLink)(manifest.toString());
|
|
91
|
+
};
|
|
92
|
+
BundlerDevServer.prototype.getRedirectUrl = function (platform = null) {
|
|
93
|
+
return platform === null || platform === 'harmony'
|
|
94
|
+
? this.getNativeRuntimeUrl()
|
|
95
|
+
: redirectURL.call(this, platform);
|
|
96
|
+
};
|
|
97
|
+
// Expo installs the manifest before Metro's enhancer; adapt only the created instance.
|
|
98
|
+
BundlerDevServer.prototype.getManifestMiddlewareAsync = async function (...args) {
|
|
99
|
+
const middleware = await create.apply(this, args);
|
|
100
|
+
const parse = middleware.getParsedHeaders;
|
|
101
|
+
const respond = middleware._getManifestResponseAsync;
|
|
102
|
+
middleware.getParsedHeaders = (request) => {
|
|
103
|
+
if (parsePlatformHeader(request) !== 'harmony')
|
|
104
|
+
return parse.call(middleware, request);
|
|
105
|
+
// Bypass only the upstream platform assertion without changing the actual request.
|
|
106
|
+
const url = new URL(request.url, 'http://localhost');
|
|
107
|
+
url.searchParams.delete('platform');
|
|
108
|
+
url.searchParams.append('platform', 'harmony');
|
|
109
|
+
const manifest = new URL((0, protocol_1.canonicalHarmonyManifestURL)(url.toString()));
|
|
110
|
+
const harmonyManifestPath = manifest.pathname + manifest.search;
|
|
111
|
+
url.searchParams.set('platform', 'ios');
|
|
112
|
+
const copy = Object.create(request);
|
|
113
|
+
copy.url = url.pathname + url.search;
|
|
114
|
+
return { ...parse.call(middleware, copy), platform: 'harmony', harmonyManifestPath };
|
|
115
|
+
};
|
|
116
|
+
middleware._getManifestResponseAsync = options => options.platform === 'harmony'
|
|
117
|
+
? createManifestResponseAsync(middleware, options, require)
|
|
118
|
+
: respond.call(middleware, options);
|
|
119
|
+
return middleware;
|
|
120
|
+
};
|
|
121
|
+
}
|