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.
- package/LICENSE +21 -0
- package/README.en.md +197 -0
- package/README.md +197 -0
- package/app.plugin.js +1 -0
- package/bin/expo-harmony.js +9 -0
- package/build/cli.d.ts +1 -0
- package/build/cli.js +56 -0
- package/build/commands/buildHap.d.ts +5 -0
- package/build/commands/buildHap.js +26 -0
- package/build/commands/bundle.d.ts +4 -0
- package/build/commands/bundle.js +18 -0
- package/build/commands/doctor.d.ts +7 -0
- package/build/commands/doctor.js +24 -0
- package/build/commands/env.d.ts +5 -0
- package/build/commands/env.js +22 -0
- package/build/commands/init.d.ts +5 -0
- package/build/commands/init.js +29 -0
- package/build/commands/syncTemplate.d.ts +5 -0
- package/build/commands/syncTemplate.js +23 -0
- package/build/core/build.d.ts +25 -0
- package/build/core/build.js +434 -0
- package/build/core/constants.d.ts +21 -0
- package/build/core/constants.js +32 -0
- package/build/core/env.d.ts +8 -0
- package/build/core/env.js +185 -0
- package/build/core/metadata.d.ts +9 -0
- package/build/core/metadata.js +54 -0
- package/build/core/project.d.ts +18 -0
- package/build/core/project.js +206 -0
- package/build/core/report.d.ts +4 -0
- package/build/core/report.js +319 -0
- package/build/core/template.d.ts +6 -0
- package/build/core/template.js +1030 -0
- package/build/data/compatibilityMatrix.d.ts +2 -0
- package/build/data/compatibilityMatrix.js +99 -0
- package/build/data/dependencyCatalog.d.ts +2 -0
- package/build/data/dependencyCatalog.js +108 -0
- package/build/data/uiStack.d.ts +39 -0
- package/build/data/uiStack.js +48 -0
- package/build/data/validatedMatrices.d.ts +3 -0
- package/build/data/validatedMatrices.js +94 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +27 -0
- package/build/plugin.d.ts +7 -0
- package/build/plugin.js +76 -0
- package/build/types.d.ts +182 -0
- package/build/types.js +2 -0
- package/docs/cli-build.md +99 -0
- package/docs/npm-release.md +89 -0
- package/docs/official-app-shell-sample.md +39 -0
- package/docs/official-minimal-sample.md +32 -0
- package/docs/official-ui-stack-sample.md +77 -0
- package/docs/roadmap.md +67 -0
- package/docs/signing-and-release.md +57 -0
- package/docs/support-matrix.md +149 -0
- package/package.json +78 -0
- package/templates/harmony/AppScope/app.json5 +10 -0
- package/templates/harmony/AppScope/resources/base/element/string.json +8 -0
- package/templates/harmony/AppScope/resources/base/media/app_icon.png +0 -0
- package/templates/harmony/README.md +31 -0
- package/templates/harmony/build-profile.json5 +37 -0
- package/templates/harmony/codelinter.json +19 -0
- package/templates/harmony/entry/build-profile.json5 +18 -0
- package/templates/harmony/entry/hvigorfile.ts +13 -0
- package/templates/harmony/entry/oh-package.json5 +14 -0
- package/templates/harmony/entry/src/main/cpp/CMakeLists.txt +55 -0
- package/templates/harmony/entry/src/main/cpp/PackageProvider.cpp +12 -0
- package/templates/harmony/entry/src/main/ets/PackageProvider.ets +6 -0
- package/templates/harmony/entry/src/main/ets/entryability/EntryAbility.ets +11 -0
- package/templates/harmony/entry/src/main/ets/pages/Index.ets +42 -0
- package/templates/harmony/entry/src/main/ets/workers/RNOHWorker.ets +8 -0
- package/templates/harmony/entry/src/main/module.json5 +31 -0
- package/templates/harmony/entry/src/main/resources/base/element/color.json +8 -0
- package/templates/harmony/entry/src/main/resources/base/element/string.json +16 -0
- package/templates/harmony/entry/src/main/resources/base/media/background.png +0 -0
- package/templates/harmony/entry/src/main/resources/base/media/foreground.png +0 -0
- package/templates/harmony/entry/src/main/resources/base/media/layered_image.json +6 -0
- package/templates/harmony/entry/src/main/resources/base/media/startIcon.png +0 -0
- package/templates/harmony/entry/src/main/resources/base/profile/main_pages.json +5 -0
- package/templates/harmony/entry/src/main/resources/rawfile/.gitkeep +1 -0
- package/templates/harmony/hvigor/hvigor-config.json5 +10 -0
- package/templates/harmony/hvigorfile.ts +7 -0
- 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;
|
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,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,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,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,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,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
|
+
}
|