expo-harmony-toolkit 1.5.0

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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +197 -0
  3. package/README.md +197 -0
  4. package/app.plugin.js +1 -0
  5. package/bin/expo-harmony.js +9 -0
  6. package/build/cli.d.ts +1 -0
  7. package/build/cli.js +56 -0
  8. package/build/commands/buildHap.d.ts +5 -0
  9. package/build/commands/buildHap.js +26 -0
  10. package/build/commands/bundle.d.ts +4 -0
  11. package/build/commands/bundle.js +18 -0
  12. package/build/commands/doctor.d.ts +7 -0
  13. package/build/commands/doctor.js +24 -0
  14. package/build/commands/env.d.ts +5 -0
  15. package/build/commands/env.js +22 -0
  16. package/build/commands/init.d.ts +5 -0
  17. package/build/commands/init.js +29 -0
  18. package/build/commands/syncTemplate.d.ts +5 -0
  19. package/build/commands/syncTemplate.js +23 -0
  20. package/build/core/build.d.ts +25 -0
  21. package/build/core/build.js +434 -0
  22. package/build/core/constants.d.ts +21 -0
  23. package/build/core/constants.js +32 -0
  24. package/build/core/env.d.ts +8 -0
  25. package/build/core/env.js +185 -0
  26. package/build/core/metadata.d.ts +9 -0
  27. package/build/core/metadata.js +54 -0
  28. package/build/core/project.d.ts +18 -0
  29. package/build/core/project.js +206 -0
  30. package/build/core/report.d.ts +4 -0
  31. package/build/core/report.js +319 -0
  32. package/build/core/template.d.ts +6 -0
  33. package/build/core/template.js +1030 -0
  34. package/build/data/compatibilityMatrix.d.ts +2 -0
  35. package/build/data/compatibilityMatrix.js +99 -0
  36. package/build/data/dependencyCatalog.d.ts +2 -0
  37. package/build/data/dependencyCatalog.js +108 -0
  38. package/build/data/uiStack.d.ts +39 -0
  39. package/build/data/uiStack.js +48 -0
  40. package/build/data/validatedMatrices.d.ts +3 -0
  41. package/build/data/validatedMatrices.js +94 -0
  42. package/build/index.d.ts +4 -0
  43. package/build/index.js +27 -0
  44. package/build/plugin.d.ts +7 -0
  45. package/build/plugin.js +76 -0
  46. package/build/types.d.ts +182 -0
  47. package/build/types.js +2 -0
  48. package/docs/cli-build.md +99 -0
  49. package/docs/npm-release.md +89 -0
  50. package/docs/official-app-shell-sample.md +39 -0
  51. package/docs/official-minimal-sample.md +32 -0
  52. package/docs/official-ui-stack-sample.md +77 -0
  53. package/docs/roadmap.md +67 -0
  54. package/docs/signing-and-release.md +57 -0
  55. package/docs/support-matrix.md +149 -0
  56. package/package.json +78 -0
  57. package/templates/harmony/AppScope/app.json5 +10 -0
  58. package/templates/harmony/AppScope/resources/base/element/string.json +8 -0
  59. package/templates/harmony/AppScope/resources/base/media/app_icon.png +0 -0
  60. package/templates/harmony/README.md +31 -0
  61. package/templates/harmony/build-profile.json5 +37 -0
  62. package/templates/harmony/codelinter.json +19 -0
  63. package/templates/harmony/entry/build-profile.json5 +18 -0
  64. package/templates/harmony/entry/hvigorfile.ts +13 -0
  65. package/templates/harmony/entry/oh-package.json5 +14 -0
  66. package/templates/harmony/entry/src/main/cpp/CMakeLists.txt +55 -0
  67. package/templates/harmony/entry/src/main/cpp/PackageProvider.cpp +12 -0
  68. package/templates/harmony/entry/src/main/ets/PackageProvider.ets +6 -0
  69. package/templates/harmony/entry/src/main/ets/entryability/EntryAbility.ets +11 -0
  70. package/templates/harmony/entry/src/main/ets/pages/Index.ets +42 -0
  71. package/templates/harmony/entry/src/main/ets/workers/RNOHWorker.ets +8 -0
  72. package/templates/harmony/entry/src/main/module.json5 +31 -0
  73. package/templates/harmony/entry/src/main/resources/base/element/color.json +8 -0
  74. package/templates/harmony/entry/src/main/resources/base/element/string.json +16 -0
  75. package/templates/harmony/entry/src/main/resources/base/media/background.png +0 -0
  76. package/templates/harmony/entry/src/main/resources/base/media/foreground.png +0 -0
  77. package/templates/harmony/entry/src/main/resources/base/media/layered_image.json +6 -0
  78. package/templates/harmony/entry/src/main/resources/base/media/startIcon.png +0 -0
  79. package/templates/harmony/entry/src/main/resources/base/profile/main_pages.json +5 -0
  80. package/templates/harmony/entry/src/main/resources/rawfile/.gitkeep +1 -0
  81. package/templates/harmony/hvigor/hvigor-config.json5 +10 -0
  82. package/templates/harmony/hvigorfile.ts +7 -0
  83. package/templates/harmony/oh-package.json5 +13 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BlackishGreen33
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,197 @@
1
+ <div align="center">
2
+ <h1>Expo Harmony Toolkit</h1>
3
+ <p><strong>A HarmonyOS migration, admission, and UI-stack build toolkit for Managed/CNG Expo projects.</strong></p>
4
+ <p>One validated UI-stack matrix, explicit dependency admission rules, managed Harmony sidecar scaffolding, and a toolkit-driven <code>doctor → init → bundle → build-hap</code> path.</p>
5
+ <p>
6
+ <a href="./README.md">简体中文</a> ·
7
+ <a href="./README.en.md">English</a>
8
+ </p>
9
+ <p>
10
+ <a href="https://github.com/BlackishGreen33/Expo-Harmony-Plugin/actions/workflows/ci.yml"><img alt="Checks" src="https://img.shields.io/badge/checks-passing-16a34a?style=flat-square&logo=githubactions&logoColor=white"></a>
11
+ <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-0f766e?style=flat-square"></a>
12
+ <a href="https://github.com/BlackishGreen33/Expo-Harmony-Plugin/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.5.0-111827?style=flat-square"></a>
13
+ <a href="./docs/support-matrix.md"><img alt="Matrix" src="https://img.shields.io/badge/matrix-expo55--rnoh082--ui--stack-2563eb?style=flat-square"></a>
14
+ <img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG-059669?style=flat-square">
15
+ </p>
16
+ <p>
17
+ <a href="./docs/support-matrix.md">Support Matrix</a> ·
18
+ <a href="./docs/cli-build.md">CLI Build Guide</a> ·
19
+ <a href="./docs/official-ui-stack-sample.md">Official UI Stack Sample</a> ·
20
+ <a href="./docs/npm-release.md">npm Release Notes</a> ·
21
+ <a href="./docs/roadmap.md">Roadmap</a>
22
+ </p>
23
+ </div>
24
+
25
+ > [!IMPORTANT]
26
+ > `v1.5.0` makes one formal public promise only: `expo55-rnoh082-ui-stack`. This is not a claim that arbitrary Expo applications can be published to HarmonyOS unchanged.
27
+
28
+ > [!TIP]
29
+ > The three validated `@react-native-oh-tpl/*` adapters are currently consumed via exact Git URLs and commits. For repository development and the official UI-stack sample, prefer `pnpm install --ignore-scripts` so adapter prepare hooks do not fail on private upstream resources.
30
+
31
+ ## Overview
32
+
33
+ `expo-harmony-toolkit` provides a constrained, verifiable Expo-to-Harmony toolchain:
34
+
35
+ - Expo config plugin entrypoint `app.plugin.js`
36
+ - `expo-harmony doctor`
37
+ - `expo-harmony init`
38
+ - `expo-harmony sync-template`
39
+ - `expo-harmony env`
40
+ - `expo-harmony bundle`
41
+ - `expo-harmony build-hap --mode debug|release`
42
+ - Managed `harmony/` sidecar templates and autolinking artifacts
43
+ - Stable `.expo-harmony/*.json` reports and metadata
44
+
45
+ ## Current Status
46
+
47
+ | Item | Status |
48
+ | --- | --- |
49
+ | Current version | `v1.5.0` |
50
+ | Public matrix | `expo55-rnoh082-ui-stack` |
51
+ | Supported input | Managed/CNG Expo projects |
52
+ | Validated JS/UI capabilities | `expo-router`, `expo-linking`, `expo-constants`, `react-native-reanimated`, `react-native-svg`, `react-native-gesture-handler` |
53
+ | Build path | `doctor -> init -> bundle -> build-hap` |
54
+ | Primary sample | `examples/official-ui-stack-sample` |
55
+ | Regression baselines | `examples/official-app-shell-sample`, `examples/official-minimal-sample` |
56
+
57
+ <details>
58
+ <summary><strong>Currently out of scope</strong></summary>
59
+
60
+ - bare Expo
61
+ - `expo-image-picker`
62
+ - `expo-file-system`
63
+ - `expo-location`
64
+ - `expo-camera`
65
+ - `expo-notifications`
66
+ - multiple public matrices
67
+
68
+ </details>
69
+
70
+ ## Quick Start
71
+
72
+ For this repository:
73
+
74
+ ```bash
75
+ pnpm install --ignore-scripts
76
+ pnpm build
77
+ pnpm test
78
+ ```
79
+
80
+ For any Expo project:
81
+
82
+ ```bash
83
+ expo-harmony doctor --project-root /path/to/app
84
+ expo-harmony doctor --project-root /path/to/app --strict
85
+ expo-harmony init --project-root /path/to/app
86
+ expo-harmony sync-template --project-root /path/to/app
87
+ ```
88
+
89
+ Inside the project root:
90
+
91
+ ```bash
92
+ cd /path/to/app
93
+ expo-harmony env --strict
94
+ expo-harmony build-hap --mode debug
95
+ ```
96
+
97
+ If you only need the JavaScript artifact:
98
+
99
+ ```bash
100
+ expo-harmony bundle
101
+ ```
102
+
103
+ ## Support Matrix
104
+
105
+ `v1.5.0` stays on one public matrix: `expo55-rnoh082-ui-stack`.
106
+
107
+ - Expo SDK: `55`
108
+ - React: `19.2.x`
109
+ - React Native: `0.83.x`
110
+ - RNOH and `@react-native-oh/react-native-harmony-cli`: `0.82.18`
111
+ - App Shell packages: `expo-router`, `expo-linking`, `expo-constants`
112
+ - UI stack packages: `react-native-reanimated`, `react-native-svg`, `react-native-gesture-handler`
113
+ - Harmony adapters: the matching `@react-native-oh-tpl/*` exact Git specifiers
114
+ - Native identifier: at least `android.package` or `ios.bundleIdentifier`
115
+
116
+ See [docs/support-matrix.md](./docs/support-matrix.md) for the full allowlist, pairing rules, exact specifiers, issue codes, and release gates.
117
+
118
+ ## Official Samples
119
+
120
+ - `examples/official-ui-stack-sample`
121
+ The primary public sample for `v1.5.0`, covering router, linking, constants, SVG, gesture, reanimated, and Harmony sidecar build flow.
122
+ - `examples/official-app-shell-sample`
123
+ The `v1.1` App Shell regression baseline that protects router behavior while UI-stack support is finalized.
124
+ - `examples/official-minimal-sample`
125
+ The smallest smoke baseline for sidecar templates and the shortest bundle path.
126
+
127
+ See:
128
+
129
+ - [Official UI Stack Sample Guide](./docs/official-ui-stack-sample.md)
130
+ - [Official App Shell Sample Guide](./docs/official-app-shell-sample.md)
131
+ - [Official Minimal Sample Guide](./docs/official-minimal-sample.md)
132
+
133
+ ## CLI Commands
134
+
135
+ | Command | Purpose |
136
+ | --- | --- |
137
+ | `expo-harmony doctor` | Inspect Expo config and dependencies and produce a migration report |
138
+ | `expo-harmony doctor --strict` | Run the formal matrix admission gate |
139
+ | `expo-harmony init` | Generate Harmony sidecar files, autolinking artifacts, metadata, and package scripts |
140
+ | `expo-harmony sync-template` | Reapply managed templates and report drift |
141
+ | `expo-harmony env` | Check the local DevEco / hvigor / hdc / signing environment |
142
+ | `expo-harmony bundle` | Generate a standard `bundle.harmony.js` |
143
+ | `expo-harmony build-hap --mode debug` | Trigger a debug HAP build |
144
+ | `expo-harmony build-hap --mode release` | Trigger a release HAP build; signing must be ready |
145
+
146
+ Key managed outputs include:
147
+
148
+ - `harmony/oh-package.json5`
149
+ - `harmony/entry/src/main/ets/RNOHPackagesFactory.ets`
150
+ - `harmony/entry/src/main/cpp/RNOHPackagesFactory.h`
151
+ - `harmony/entry/src/main/cpp/autolinking.cmake`
152
+ - `metro.harmony.config.js`
153
+ - `.expo-harmony/manifest.json`
154
+ - `.expo-harmony/doctor-report.json`
155
+ - `.expo-harmony/env-report.json`
156
+ - `.expo-harmony/build-report.json`
157
+ - `.expo-harmony/toolkit-config.json`
158
+
159
+ ## Release And Validation
160
+
161
+ Pre-publish checks:
162
+
163
+ - `pnpm build`
164
+ - `pnpm test`
165
+ - `npm pack --dry-run`
166
+ - tarball smoke: `doctor --strict`, `init --force`, `bundle`
167
+
168
+ Automatic publishing defaults to hosted CI only:
169
+
170
+ - GitHub workflow runs `build/test/pack/tarball smoke`
171
+ - `build-hap --mode debug` does not block npm publish
172
+ - npm publish uses the `latest` dist-tag and provenance
173
+
174
+ Manual Harmony acceptance still requires:
175
+
176
+ - `official-ui-stack-sample` launches successfully
177
+ - SVG renders correctly
178
+ - the gesture triggers visible animation
179
+ - routing still works after the animation completes
180
+ - `Build Debug Hap(s)` succeeds
181
+
182
+ See [docs/npm-release.md](./docs/npm-release.md) and [docs/signing-and-release.md](./docs/signing-and-release.md).
183
+
184
+ ## Documentation
185
+
186
+ - [Support Matrix](./docs/support-matrix.md)
187
+ - [CLI Build Guide](./docs/cli-build.md)
188
+ - [Official UI Stack Sample Guide](./docs/official-ui-stack-sample.md)
189
+ - [Official App Shell Sample Guide](./docs/official-app-shell-sample.md)
190
+ - [Official Minimal Sample Guide](./docs/official-minimal-sample.md)
191
+ - [npm Release Notes](./docs/npm-release.md)
192
+ - [Signing and Release Notes](./docs/signing-and-release.md)
193
+ - [Roadmap](./docs/roadmap.md)
194
+
195
+ ## License
196
+
197
+ Released under the [MIT License](./LICENSE).
package/README.md ADDED
@@ -0,0 +1,197 @@
1
+ <div align="center">
2
+ <h1>Expo Harmony Toolkit</h1>
3
+ <p><strong>面向 Managed/CNG Expo 项目的 HarmonyOS 迁移、准入检查与 UI-stack 构建工具链。</strong></p>
4
+ <p>One validated UI-stack matrix, explicit dependency admission rules, managed Harmony sidecar scaffolding, and a toolkit-driven <code>doctor → init → bundle → build-hap</code> path.</p>
5
+ <p>
6
+ <a href="./README.md">简体中文</a> ·
7
+ <a href="./README.en.md">English</a>
8
+ </p>
9
+ <p>
10
+ <a href="https://github.com/BlackishGreen33/Expo-Harmony-Plugin/actions/workflows/ci.yml"><img alt="Checks" src="https://img.shields.io/badge/checks-passing-16a34a?style=flat-square&logo=githubactions&logoColor=white"></a>
11
+ <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-0f766e?style=flat-square"></a>
12
+ <a href="https://github.com/BlackishGreen33/Expo-Harmony-Plugin/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.5.0-111827?style=flat-square"></a>
13
+ <a href="./docs/support-matrix.md"><img alt="Matrix" src="https://img.shields.io/badge/matrix-expo55--rnoh082--ui--stack-2563eb?style=flat-square"></a>
14
+ <img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG-059669?style=flat-square">
15
+ </p>
16
+ <p>
17
+ <a href="./docs/support-matrix.md">支持矩阵</a> ·
18
+ <a href="./docs/cli-build.md">CLI 构建指南</a> ·
19
+ <a href="./docs/official-ui-stack-sample.md">官方 UI Stack Sample</a> ·
20
+ <a href="./docs/npm-release.md">npm 发布说明</a> ·
21
+ <a href="./docs/roadmap.md">路线图</a>
22
+ </p>
23
+ </div>
24
+
25
+ > [!IMPORTANT]
26
+ > `v1.5.0` 只对 `expo55-rnoh082-ui-stack` 做正式公开承诺。这不是“任意 Expo 项目都能原样发布到 HarmonyOS”的声明,而是对一条受限、可验证矩阵的稳定承诺。
27
+
28
+ > [!TIP]
29
+ > 由于当前三套 `@react-native-oh-tpl/*` adapter 依赖以 Git URL + exact commit 形式接入,仓库开发和官方 UI-stack sample 推荐使用 `pnpm install --ignore-scripts`,避免 Git adapter 在 prepare 阶段拉取私有资源而中断安装。
30
+
31
+ ## 概览
32
+
33
+ `expo-harmony-toolkit` 提供一条围绕 Expo 到 HarmonyOS 迁移的受限、可验证工具链:
34
+
35
+ - Expo config plugin 根入口 `app.plugin.js`
36
+ - `expo-harmony doctor`
37
+ - `expo-harmony init`
38
+ - `expo-harmony sync-template`
39
+ - `expo-harmony env`
40
+ - `expo-harmony bundle`
41
+ - `expo-harmony build-hap --mode debug|release`
42
+ - 受管 `harmony/` sidecar 模板与 autolinking 产物
43
+ - `.expo-harmony/*.json` 形式的稳定报告与元数据
44
+
45
+ ## 当前状态
46
+
47
+ | 项目 | 说明 |
48
+ | --- | --- |
49
+ | 当前版本 | `v1.5.0` |
50
+ | 唯一公开矩阵 | `expo55-rnoh082-ui-stack` |
51
+ | 输入范围 | Managed/CNG Expo 项目 |
52
+ | 已验证 JS/UI 能力 | `expo-router`、`expo-linking`、`expo-constants`、`react-native-reanimated`、`react-native-svg`、`react-native-gesture-handler` |
53
+ | 构建链 | `doctor -> init -> bundle -> build-hap` |
54
+ | 主 sample | `examples/official-ui-stack-sample` |
55
+ | 回归基线 | `examples/official-app-shell-sample`、`examples/official-minimal-sample` |
56
+
57
+ <details>
58
+ <summary><strong>当前不在承诺范围</strong></summary>
59
+
60
+ - bare Expo
61
+ - `expo-image-picker`
62
+ - `expo-file-system`
63
+ - `expo-location`
64
+ - `expo-camera`
65
+ - `expo-notifications`
66
+ - 多矩阵并行支持
67
+
68
+ </details>
69
+
70
+ ## 快速开始
71
+
72
+ 仓库自身:
73
+
74
+ ```bash
75
+ pnpm install --ignore-scripts
76
+ pnpm build
77
+ pnpm test
78
+ ```
79
+
80
+ 任意 Expo 项目:
81
+
82
+ ```bash
83
+ expo-harmony doctor --project-root /path/to/app
84
+ expo-harmony doctor --project-root /path/to/app --strict
85
+ expo-harmony init --project-root /path/to/app
86
+ expo-harmony sync-template --project-root /path/to/app
87
+ ```
88
+
89
+ 进入项目根目录后的 CLI 构建链:
90
+
91
+ ```bash
92
+ cd /path/to/app
93
+ expo-harmony env --strict
94
+ expo-harmony build-hap --mode debug
95
+ ```
96
+
97
+ 如果你只想单独生成 JS bundle:
98
+
99
+ ```bash
100
+ expo-harmony bundle
101
+ ```
102
+
103
+ ## 支持矩阵
104
+
105
+ `v1.5.0` 继续坚持单矩阵路线:`expo55-rnoh082-ui-stack`。
106
+
107
+ - Expo SDK:`55`
108
+ - React:`19.2.x`
109
+ - React Native:`0.83.x`
110
+ - RNOH / `@react-native-oh/react-native-harmony-cli`:`0.82.18`
111
+ - App Shell 依赖:`expo-router`、`expo-linking`、`expo-constants`
112
+ - UI stack 依赖:`react-native-reanimated`、`react-native-svg`、`react-native-gesture-handler`
113
+ - Harmony adapter:对应三项 `@react-native-oh-tpl/*` exact Git specifier
114
+ - 原生标识:至少配置 `android.package` 或 `ios.bundleIdentifier`
115
+
116
+ 完整白名单、配对规则、exact specifier、issue code 与 release gate 见 [docs/support-matrix.md](./docs/support-matrix.md)。
117
+
118
+ ## 官方 Samples
119
+
120
+ - `examples/official-ui-stack-sample`
121
+ 当前唯一对外主 sample,同时覆盖 router、linking、constants、SVG、gesture、reanimated 和 Harmony sidecar 构建链。
122
+ - `examples/official-app-shell-sample`
123
+ `v1.1` App Shell 回归基线,用来防止 UI-stack 收口引入 router 退化。
124
+ - `examples/official-minimal-sample`
125
+ 最小 smoke baseline,用来回归 sidecar 模板与最短 bundle 路径。
126
+
127
+ 详见:
128
+
129
+ - [官方 UI Stack sample 指南](./docs/official-ui-stack-sample.md)
130
+ - [官方 App Shell sample 指南](./docs/official-app-shell-sample.md)
131
+ - [官方最小 sample 指南](./docs/official-minimal-sample.md)
132
+
133
+ ## CLI 命令
134
+
135
+ | 命令 | 作用 |
136
+ | --- | --- |
137
+ | `expo-harmony doctor` | 扫描 Expo 配置与依赖,输出迁移报告 |
138
+ | `expo-harmony doctor --strict` | 将当前矩阵准入检查作为正式 gate 执行 |
139
+ | `expo-harmony init` | 生成 Harmony sidecar、autolinking 产物、metadata 与 package scripts |
140
+ | `expo-harmony sync-template` | 再次应用受管模板并检查 drift |
141
+ | `expo-harmony env` | 检查 DevEco / hvigor / hdc / signing 本地环境 |
142
+ | `expo-harmony bundle` | 生成标准 `bundle.harmony.js` |
143
+ | `expo-harmony build-hap --mode debug` | 触发 debug HAP 构建 |
144
+ | `expo-harmony build-hap --mode release` | 触发 release HAP 构建,需要 signing 就绪 |
145
+
146
+ 关键受管产物包括:
147
+
148
+ - `harmony/oh-package.json5`
149
+ - `harmony/entry/src/main/ets/RNOHPackagesFactory.ets`
150
+ - `harmony/entry/src/main/cpp/RNOHPackagesFactory.h`
151
+ - `harmony/entry/src/main/cpp/autolinking.cmake`
152
+ - `metro.harmony.config.js`
153
+ - `.expo-harmony/manifest.json`
154
+ - `.expo-harmony/doctor-report.json`
155
+ - `.expo-harmony/env-report.json`
156
+ - `.expo-harmony/build-report.json`
157
+ - `.expo-harmony/toolkit-config.json`
158
+
159
+ ## 发布与验收
160
+
161
+ 发布前统一检查:
162
+
163
+ - `pnpm build`
164
+ - `pnpm test`
165
+ - `npm pack --dry-run`
166
+ - tarball 安装 smoke:`doctor --strict`、`init --force`、`bundle`
167
+
168
+ 自动发布默认走 hosted CI only:
169
+
170
+ - GitHub workflow 跑 `build/test/pack/tarball smoke`
171
+ - `build-hap --mode debug` 不阻塞 npm publish
172
+ - npm 发布使用 `latest` dist-tag 和 provenance
173
+
174
+ 手动 Harmony 验收继续要求:
175
+
176
+ - `official-ui-stack-sample` 成功启动
177
+ - SVG 正常渲染
178
+ - gesture 能触发可见动画
179
+ - 动画完成后路由跳转仍正常
180
+ - `Build Debug Hap(s)` 成功
181
+
182
+ 详见 [docs/npm-release.md](./docs/npm-release.md) 与 [docs/signing-and-release.md](./docs/signing-and-release.md)。
183
+
184
+ ## 文档索引
185
+
186
+ - [支持矩阵](./docs/support-matrix.md)
187
+ - [CLI 构建指南](./docs/cli-build.md)
188
+ - [官方 UI Stack sample 指南](./docs/official-ui-stack-sample.md)
189
+ - [官方 App Shell sample 指南](./docs/official-app-shell-sample.md)
190
+ - [官方最小 sample 指南](./docs/official-minimal-sample.md)
191
+ - [npm 发布说明](./docs/npm-release.md)
192
+ - [签名与 Release 说明](./docs/signing-and-release.md)
193
+ - [路线图](./docs/roadmap.md)
194
+
195
+ ## License
196
+
197
+ 本项目基于 [MIT License](./LICENSE) 发布。
package/app.plugin.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./build/plugin').default;
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('../build/cli')
4
+ .run(process.argv)
5
+ .catch((error) => {
6
+ const message = error instanceof Error ? error.stack ?? error.message : String(error);
7
+ process.stderr.write(message + '\n');
8
+ process.exit(1);
9
+ });
package/build/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function run(argv?: string[]): Promise<void>;
package/build/cli.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = run;
4
+ const commander_1 = require("commander");
5
+ const constants_1 = require("./core/constants");
6
+ const buildHap_1 = require("./commands/buildHap");
7
+ const bundle_1 = require("./commands/bundle");
8
+ const doctor_1 = require("./commands/doctor");
9
+ const env_1 = require("./commands/env");
10
+ const init_1 = require("./commands/init");
11
+ const syncTemplate_1 = require("./commands/syncTemplate");
12
+ async function run(argv = process.argv) {
13
+ const program = new commander_1.Command();
14
+ program
15
+ .name(constants_1.CLI_NAME)
16
+ .description('Validated Expo-to-Harmony toolkit for managed Expo UI-stack projects')
17
+ .version(constants_1.TOOLKIT_VERSION);
18
+ program
19
+ .command('env')
20
+ .description('Inspect the local DevEco/Harmony CLI environment for bundle and HAP builds')
21
+ .option('--strict', 'return a non-zero exit code when required local build tools are missing')
22
+ .option('--json', 'print JSON instead of a human-readable report')
23
+ .action(env_1.runEnvCommand);
24
+ program
25
+ .command('doctor')
26
+ .description('Inspect an Expo project and classify dependencies against the Harmony migration matrix')
27
+ .option('-p, --project-root <path>', 'path to the Expo project')
28
+ .option('--strict', 'return a non-zero exit code when the project falls outside the validated v1.5 matrix')
29
+ .option('--json', 'print JSON instead of a human-readable report')
30
+ .option('-o, --output <path>', 'write the JSON report to a file')
31
+ .action(doctor_1.runDoctorCommand);
32
+ program
33
+ .command('bundle')
34
+ .description('Generate a Harmony JavaScript bundle with the validated Metro sidecar')
35
+ .option('-p, --project-root <path>', 'path to the Expo project')
36
+ .action(bundle_1.runBundleCommand);
37
+ program
38
+ .command('build-hap')
39
+ .description('Build a Harmony HAP from the validated sidecar and bundle outputs')
40
+ .option('-p, --project-root <path>', 'path to the Expo project')
41
+ .option('--mode <mode>', 'build mode: debug or release', 'debug')
42
+ .action(buildHap_1.runBuildHapCommand);
43
+ program
44
+ .command('init')
45
+ .description('Scaffold Harmony sidecar files, vendored template files, and package scripts')
46
+ .option('-p, --project-root <path>', 'path to the Expo project')
47
+ .option('-f, --force', 'overwrite drifted managed files')
48
+ .action(init_1.runInitCommand);
49
+ program
50
+ .command('sync-template')
51
+ .description('Re-apply the vendored Harmony template in an idempotent way')
52
+ .option('-p, --project-root <path>', 'path to the Expo project')
53
+ .option('-f, --force', 'overwrite drifted managed files')
54
+ .action(syncTemplate_1.runSyncTemplateCommand);
55
+ await program.parseAsync(argv);
56
+ }
@@ -0,0 +1,5 @@
1
+ export interface BuildHapCommandOptions {
2
+ projectRoot?: string;
3
+ mode?: 'debug' | 'release';
4
+ }
5
+ export declare function runBuildHapCommand(options: BuildHapCommandOptions): Promise<void>;
@@ -0,0 +1,26 @@
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.runBuildHapCommand = runBuildHapCommand;
7
+ const path_1 = __importDefault(require("path"));
8
+ const build_1 = require("../core/build");
9
+ const metadata_1 = require("../core/metadata");
10
+ async function runBuildHapCommand(options) {
11
+ const projectRoot = path_1.default.resolve(options.projectRoot ?? process.cwd());
12
+ const mode = options.mode ?? 'debug';
13
+ if (mode !== 'debug' && mode !== 'release') {
14
+ process.exitCode = 1;
15
+ process.stderr.write(`Unsupported build mode "${mode}". Expected debug or release.\n`);
16
+ return;
17
+ }
18
+ const report = await (0, build_1.buildHapProject)(projectRoot, {
19
+ mode,
20
+ });
21
+ await (0, metadata_1.writeBuildReport)(projectRoot, report);
22
+ if (report.status === 'failed') {
23
+ process.exitCode = 1;
24
+ }
25
+ process.stdout.write((0, build_1.renderBuildReport)(report) + '\n');
26
+ }
@@ -0,0 +1,4 @@
1
+ export interface BundleCommandOptions {
2
+ projectRoot?: string;
3
+ }
4
+ export declare function runBundleCommand(options: BundleCommandOptions): Promise<void>;
@@ -0,0 +1,18 @@
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.runBundleCommand = runBundleCommand;
7
+ const path_1 = __importDefault(require("path"));
8
+ const build_1 = require("../core/build");
9
+ const metadata_1 = require("../core/metadata");
10
+ async function runBundleCommand(options) {
11
+ const projectRoot = path_1.default.resolve(options.projectRoot ?? process.cwd());
12
+ const report = await (0, build_1.bundleProject)(projectRoot);
13
+ await (0, metadata_1.writeBuildReport)(projectRoot, report);
14
+ if (report.status === 'failed') {
15
+ process.exitCode = 1;
16
+ }
17
+ process.stdout.write((0, build_1.renderBuildReport)(report) + '\n');
18
+ }
@@ -0,0 +1,7 @@
1
+ export interface DoctorCommandOptions {
2
+ projectRoot?: string;
3
+ strict?: boolean;
4
+ json?: boolean;
5
+ output?: string;
6
+ }
7
+ export declare function runDoctorCommand(options: DoctorCommandOptions): Promise<void>;
@@ -0,0 +1,24 @@
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.runDoctorCommand = runDoctorCommand;
7
+ const path_1 = __importDefault(require("path"));
8
+ const constants_1 = require("../core/constants");
9
+ const report_1 = require("../core/report");
10
+ async function runDoctorCommand(options) {
11
+ const projectRoot = path_1.default.resolve(options.projectRoot ?? process.cwd());
12
+ const report = await (0, report_1.buildDoctorReport)(projectRoot);
13
+ if (options.output) {
14
+ await (0, report_1.writeDoctorReport)(projectRoot, report, path_1.default.resolve(options.output));
15
+ }
16
+ if (options.strict && report.eligibility === 'ineligible') {
17
+ process.exitCode = constants_1.STRICT_DOCTOR_EXIT_CODE;
18
+ }
19
+ if (options.json) {
20
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
21
+ return;
22
+ }
23
+ process.stdout.write((0, report_1.renderDoctorReport)(report) + '\n');
24
+ }
@@ -0,0 +1,5 @@
1
+ export interface EnvCommandOptions {
2
+ strict?: boolean;
3
+ json?: boolean;
4
+ }
5
+ export declare function runEnvCommand(options: EnvCommandOptions): Promise<void>;
@@ -0,0 +1,22 @@
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.runEnvCommand = runEnvCommand;
7
+ const path_1 = __importDefault(require("path"));
8
+ const env_1 = require("../core/env");
9
+ const metadata_1 = require("../core/metadata");
10
+ async function runEnvCommand(options) {
11
+ const projectRoot = path_1.default.resolve(process.cwd());
12
+ const report = await (0, env_1.buildEnvReport)(projectRoot);
13
+ await (0, metadata_1.writeEnvReport)(projectRoot, report);
14
+ if (options.strict && report.blockingIssues.length > 0) {
15
+ process.exitCode = (0, env_1.getStrictEnvExitCode)();
16
+ }
17
+ if (options.json) {
18
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
19
+ return;
20
+ }
21
+ process.stdout.write((0, env_1.renderEnvReport)(report) + '\n');
22
+ }
@@ -0,0 +1,5 @@
1
+ export interface InitCommandOptions {
2
+ projectRoot?: string;
3
+ force?: boolean;
4
+ }
5
+ export declare function runInitCommand(options: InitCommandOptions): Promise<void>;
@@ -0,0 +1,29 @@
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.runInitCommand = runInitCommand;
7
+ const path_1 = __importDefault(require("path"));
8
+ const template_1 = require("../core/template");
9
+ const report_1 = require("../core/report");
10
+ async function runInitCommand(options) {
11
+ const projectRoot = path_1.default.resolve(options.projectRoot ?? process.cwd());
12
+ const result = await (0, template_1.initProject)(projectRoot, Boolean(options.force));
13
+ const lines = [
14
+ (0, report_1.renderDoctorReport)(result.report),
15
+ '',
16
+ `Scaffold result:`,
17
+ `- written: ${result.sync.writtenFiles.length}`,
18
+ `- unchanged: ${result.sync.unchangedFiles.length}`,
19
+ `- skipped: ${result.sync.skippedFiles.length}`,
20
+ `- doctor report: ${result.doctorReportPath}`,
21
+ ];
22
+ if (result.sync.warnings.length > 0 || result.packageWarnings.length > 0) {
23
+ lines.push('', 'Warnings:');
24
+ for (const warning of [...result.sync.warnings, ...result.packageWarnings]) {
25
+ lines.push(`- ${warning}`);
26
+ }
27
+ }
28
+ process.stdout.write(lines.join('\n') + '\n');
29
+ }
@@ -0,0 +1,5 @@
1
+ export interface SyncTemplateCommandOptions {
2
+ projectRoot?: string;
3
+ force?: boolean;
4
+ }
5
+ export declare function runSyncTemplateCommand(options: SyncTemplateCommandOptions): Promise<void>;