expo-harmony-toolkit 1.5.2 → 1.6.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/README.en.md +27 -20
- package/README.md +27 -20
- package/build/cli.js +2 -1
- package/build/commands/doctor.d.ts +2 -0
- package/build/commands/doctor.js +14 -1
- package/build/core/constants.d.ts +1 -1
- package/build/core/constants.js +1 -1
- package/build/core/report.d.ts +4 -2
- package/build/core/report.js +56 -6
- package/build/core/template.js +280 -8
- package/build/data/capabilities.d.ts +7 -0
- package/build/data/capabilities.js +109 -0
- package/build/data/dependencyCatalog.js +29 -20
- package/build/types.d.ts +39 -0
- package/docs/cli-build.md +3 -1
- package/docs/official-native-capabilities-sample.md +64 -0
- package/docs/roadmap.md +38 -22
- package/docs/support-matrix.md +78 -53
- package/docs/v1.6.0-acceptance.md +193 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>Expo Harmony Toolkit</h1>
|
|
3
3
|
<p><strong>A HarmonyOS migration, admission, and UI-stack build toolkit for Managed/CNG Expo projects.</strong></p>
|
|
4
|
-
<p>One
|
|
4
|
+
<p>One verified UI-stack matrix, additive preview/experimental capability tiers, managed Harmony sidecar scaffolding, and a toolkit-driven <code>doctor → init → bundle → build-hap</code> path.</p>
|
|
5
5
|
<p>
|
|
6
6
|
<a href="./README.md">简体中文</a> ·
|
|
7
7
|
<a href="./README.en.md">English</a>
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
<p>
|
|
10
10
|
<a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/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
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-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.
|
|
12
|
+
<a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.6.0-111827?style=flat-square"></a>
|
|
13
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
14
|
<img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG-059669?style=flat-square">
|
|
15
15
|
</p>
|
|
16
16
|
<p>
|
|
17
17
|
<a href="./docs/support-matrix.md">Support Matrix</a> ·
|
|
18
18
|
<a href="./docs/cli-build.md">CLI Build Guide</a> ·
|
|
19
|
+
<a href="./docs/official-native-capabilities-sample.md">Official Native Capabilities Sample</a> ·
|
|
19
20
|
<a href="./docs/official-ui-stack-sample.md">Official UI Stack Sample</a> ·
|
|
20
21
|
<a href="./docs/npm-release.md">npm Release Notes</a> ·
|
|
21
22
|
<a href="./docs/roadmap.md">Roadmap</a>
|
|
@@ -23,14 +24,14 @@
|
|
|
23
24
|
</div>
|
|
24
25
|
|
|
25
26
|
> [!IMPORTANT]
|
|
26
|
-
> `v1.
|
|
27
|
+
> Starting with `v1.6`, the toolkit exposes `verified + preview + experimental` support tiers. `expo55-rnoh082-ui-stack` remains the only `verified` public matrix, while `expo-file-system` and `expo-image-picker` move into `preview`. This is still not a claim that arbitrary Expo apps can be published to HarmonyOS unchanged.
|
|
27
28
|
|
|
28
29
|
> [!TIP]
|
|
29
30
|
> The two validated `@react-native-oh-tpl/*` adapters in the public matrix 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
|
|
|
31
32
|
## Overview
|
|
32
33
|
|
|
33
|
-
`expo-harmony-toolkit` provides a constrained, verifiable Expo-to-Harmony toolchain:
|
|
34
|
+
`expo-harmony-toolkit` provides a constrained, verifiable Expo-to-Harmony toolchain and now starts exposing preview-tier native capability bridges:
|
|
34
35
|
|
|
35
36
|
- Expo config plugin entrypoint `app.plugin.js`
|
|
36
37
|
- `expo-harmony doctor`
|
|
@@ -46,20 +47,23 @@
|
|
|
46
47
|
|
|
47
48
|
| Item | Status |
|
|
48
49
|
| --- | --- |
|
|
49
|
-
| Current version | `v1.
|
|
50
|
-
|
|
|
50
|
+
| Current version | `v1.6.0` |
|
|
51
|
+
| Support model | `verified + preview + experimental` |
|
|
52
|
+
| Public `verified` matrix | `expo55-rnoh082-ui-stack` |
|
|
51
53
|
| Supported input | Managed/CNG Expo projects |
|
|
52
|
-
|
|
|
54
|
+
| `verified` JS/UI capabilities | `expo-router`, `expo-linking`, `expo-constants`, `react-native-reanimated`, `react-native-svg` |
|
|
55
|
+
| `preview` native capabilities | `expo-file-system`, `expo-image-picker` |
|
|
56
|
+
| `experimental` capabilities | `expo-location`, `expo-camera`, `expo-notifications`, `react-native-gesture-handler` |
|
|
53
57
|
| Build path | `doctor -> init -> bundle -> build-hap` |
|
|
54
58
|
| Primary sample | `examples/official-ui-stack-sample` |
|
|
59
|
+
| Preview sample | `examples/official-native-capabilities-sample` |
|
|
55
60
|
| Regression baselines | `examples/official-app-shell-sample`, `examples/official-minimal-sample` |
|
|
56
61
|
|
|
57
62
|
<details>
|
|
58
|
-
<summary><strong>
|
|
63
|
+
<summary><strong>Still outside the verified public promise</strong></summary>
|
|
59
64
|
|
|
60
65
|
- bare Expo
|
|
61
|
-
- `expo-image-picker`
|
|
62
|
-
- `expo-file-system`
|
|
66
|
+
- `expo-file-system` and `expo-image-picker` remain `preview`
|
|
63
67
|
- `expo-location`
|
|
64
68
|
- `expo-camera`
|
|
65
69
|
- `expo-notifications`
|
|
@@ -130,6 +134,7 @@ Notes:
|
|
|
130
134
|
cd /path/to/app
|
|
131
135
|
pnpm exec expo-harmony doctor --project-root .
|
|
132
136
|
pnpm exec expo-harmony doctor --project-root . --strict
|
|
137
|
+
pnpm exec expo-harmony doctor --project-root . --target-tier preview
|
|
133
138
|
```
|
|
134
139
|
|
|
135
140
|
2. Generate or refresh the managed Harmony sidecar:
|
|
@@ -157,24 +162,20 @@ pnpm exec expo-harmony build-hap --mode release
|
|
|
157
162
|
Common decision points:
|
|
158
163
|
|
|
159
164
|
- Want to know whether the current project still matches the public matrix: run `doctor --strict`
|
|
165
|
+
- Want to know whether the project at least falls into preview / experimental tiers: run `doctor --target-tier preview` or `doctor --target-tier experimental`
|
|
160
166
|
- Changed dependencies, Expo config, or plugin wiring: run `sync-template`
|
|
161
167
|
- Only want to verify JavaScript/UI portability: run `bundle`
|
|
162
168
|
- About to open DevEco Studio or build a HAP locally: run `env` first
|
|
163
169
|
|
|
164
170
|
## Support Matrix
|
|
165
171
|
|
|
166
|
-
`v1.
|
|
172
|
+
`v1.6` moves to tiered support:
|
|
167
173
|
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
- RNOH and `@react-native-oh/react-native-harmony-cli`: `0.82.18`
|
|
172
|
-
- App Shell packages: `expo-router`, `expo-linking`, `expo-constants`
|
|
173
|
-
- UI stack packages: `react-native-reanimated`, `react-native-svg`
|
|
174
|
-
- Harmony adapters: the matching `@react-native-oh-tpl/*` exact Git specifiers
|
|
175
|
-
- Native identifier: at least `android.package` or `ios.bundleIdentifier`
|
|
174
|
+
- `verified`: the only public matrix remains `expo55-rnoh082-ui-stack`
|
|
175
|
+
- `preview`: `expo-file-system`, `expo-image-picker`
|
|
176
|
+
- `experimental`: `expo-location`, `expo-camera`, `expo-notifications`, `react-native-gesture-handler`
|
|
176
177
|
|
|
177
|
-
`
|
|
178
|
+
`doctor --strict` still means `verified` only. `doctor --target-tier preview` allows the same runtime matrix plus preview-tier capabilities, but that does not promote them into the formal public promise.
|
|
178
179
|
|
|
179
180
|
See [docs/support-matrix.md](./docs/support-matrix.md) for the full allowlist, pairing rules, exact specifiers, issue codes, and release gates.
|
|
180
181
|
|
|
@@ -182,6 +183,8 @@ See [docs/support-matrix.md](./docs/support-matrix.md) for the full allowlist, p
|
|
|
182
183
|
|
|
183
184
|
- `examples/official-ui-stack-sample`
|
|
184
185
|
The primary public sample for `v1.5.0`, covering router, linking, constants, SVG, reanimated, and Harmony sidecar build flow.
|
|
186
|
+
- `examples/official-native-capabilities-sample`
|
|
187
|
+
The new preview sample for `v1.6`, covering the `expo-file-system` and `expo-image-picker` bridge, permission generation, and bundle-time alias flow.
|
|
185
188
|
- `examples/official-app-shell-sample`
|
|
186
189
|
The `v1.1` App Shell regression baseline that protects router behavior while UI-stack support is finalized.
|
|
187
190
|
- `examples/official-minimal-sample`
|
|
@@ -189,6 +192,7 @@ See [docs/support-matrix.md](./docs/support-matrix.md) for the full allowlist, p
|
|
|
189
192
|
|
|
190
193
|
See:
|
|
191
194
|
|
|
195
|
+
- [Official Native Capabilities Sample Guide](./docs/official-native-capabilities-sample.md)
|
|
192
196
|
- [Official UI Stack Sample Guide](./docs/official-ui-stack-sample.md)
|
|
193
197
|
- [Official App Shell Sample Guide](./docs/official-app-shell-sample.md)
|
|
194
198
|
- [Official Minimal Sample Guide](./docs/official-minimal-sample.md)
|
|
@@ -199,6 +203,7 @@ See:
|
|
|
199
203
|
| --- | --- |
|
|
200
204
|
| `expo-harmony doctor` | Inspect Expo config and dependencies and produce a migration report |
|
|
201
205
|
| `expo-harmony doctor --strict` | Run the formal matrix admission gate |
|
|
206
|
+
| `expo-harmony doctor --target-tier preview` | Evaluate whether the project fits at least the preview support tier |
|
|
202
207
|
| `expo-harmony init` | Generate Harmony sidecar files, autolinking artifacts, metadata, and package scripts |
|
|
203
208
|
| `expo-harmony sync-template` | Reapply managed templates and report drift |
|
|
204
209
|
| `expo-harmony env` | Check the local DevEco / hvigor / hdc / signing environment |
|
|
@@ -242,6 +247,7 @@ Manual Harmony acceptance still requires:
|
|
|
242
247
|
- pressing the home-screen motion rail triggers visible animation
|
|
243
248
|
- routing still works after the animation completes
|
|
244
249
|
- `Build Debug Hap(s)` succeeds
|
|
250
|
+
- `official-native-capabilities-sample` at least proves preview route bundling and generated Harmony permissions
|
|
245
251
|
|
|
246
252
|
See [docs/npm-release.md](./docs/npm-release.md) and [docs/signing-and-release.md](./docs/signing-and-release.md).
|
|
247
253
|
|
|
@@ -249,6 +255,7 @@ See [docs/npm-release.md](./docs/npm-release.md) and [docs/signing-and-release.m
|
|
|
249
255
|
|
|
250
256
|
- [Support Matrix](./docs/support-matrix.md)
|
|
251
257
|
- [CLI Build Guide](./docs/cli-build.md)
|
|
258
|
+
- [Official Native Capabilities Sample Guide](./docs/official-native-capabilities-sample.md)
|
|
252
259
|
- [Official UI Stack Sample Guide](./docs/official-ui-stack-sample.md)
|
|
253
260
|
- [Official App Shell Sample Guide](./docs/official-app-shell-sample.md)
|
|
254
261
|
- [Official Minimal Sample Guide](./docs/official-minimal-sample.md)
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>Expo Harmony Toolkit</h1>
|
|
3
3
|
<p><strong>面向 Managed/CNG Expo 项目的 HarmonyOS 迁移、准入检查与 UI-stack 构建工具链。</strong></p>
|
|
4
|
-
<p>One
|
|
4
|
+
<p>One verified UI-stack matrix, additive preview/experimental capability tiers, managed Harmony sidecar scaffolding, and a toolkit-driven <code>doctor → init → bundle → build-hap</code> path.</p>
|
|
5
5
|
<p>
|
|
6
6
|
<a href="./README.md">简体中文</a> ·
|
|
7
7
|
<a href="./README.en.md">English</a>
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
<p>
|
|
10
10
|
<a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/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
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-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.
|
|
12
|
+
<a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.6.0-111827?style=flat-square"></a>
|
|
13
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
14
|
<img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG-059669?style=flat-square">
|
|
15
15
|
</p>
|
|
16
16
|
<p>
|
|
17
17
|
<a href="./docs/support-matrix.md">支持矩阵</a> ·
|
|
18
18
|
<a href="./docs/cli-build.md">CLI 构建指南</a> ·
|
|
19
|
+
<a href="./docs/official-native-capabilities-sample.md">官方 Native Capabilities Sample</a> ·
|
|
19
20
|
<a href="./docs/official-ui-stack-sample.md">官方 UI Stack Sample</a> ·
|
|
20
21
|
<a href="./docs/npm-release.md">npm 发布说明</a> ·
|
|
21
22
|
<a href="./docs/roadmap.md">路线图</a>
|
|
@@ -23,14 +24,14 @@
|
|
|
23
24
|
</div>
|
|
24
25
|
|
|
25
26
|
> [!IMPORTANT]
|
|
26
|
-
> `v1.
|
|
27
|
+
> `v1.6` 开始,toolkit 采用 `verified + preview + experimental` 三层支持模型。`expo55-rnoh082-ui-stack` 仍是唯一 `verified` 公开矩阵;`expo-file-system` 与 `expo-image-picker` 进入 `preview`,但这仍然不是“任意 Expo 项目都能原样发布到 HarmonyOS”的声明。
|
|
27
28
|
|
|
28
29
|
> [!TIP]
|
|
29
30
|
> 由于当前公开矩阵内的两套 `@react-native-oh-tpl/*` adapter 依赖以 Git URL + exact commit 形式接入,仓库开发和官方 UI-stack sample 推荐使用 `pnpm install --ignore-scripts`,避免 Git adapter 在 prepare 阶段拉取私有资源而中断安装。
|
|
30
31
|
|
|
31
32
|
## 概览
|
|
32
33
|
|
|
33
|
-
`expo-harmony-toolkit` 提供一条围绕 Expo 到 HarmonyOS
|
|
34
|
+
`expo-harmony-toolkit` 提供一条围绕 Expo 到 HarmonyOS 迁移的受限、可验证工具链,并开始公开 preview 层的原生能力桥接骨架:
|
|
34
35
|
|
|
35
36
|
- Expo config plugin 根入口 `app.plugin.js`
|
|
36
37
|
- `expo-harmony doctor`
|
|
@@ -46,20 +47,23 @@
|
|
|
46
47
|
|
|
47
48
|
| 项目 | 说明 |
|
|
48
49
|
| --- | --- |
|
|
49
|
-
| 当前版本 | `v1.
|
|
50
|
-
|
|
|
50
|
+
| 当前版本 | `v1.6.0` |
|
|
51
|
+
| 支持模型 | `verified + preview + experimental` |
|
|
52
|
+
| 唯一 `verified` 公开矩阵 | `expo55-rnoh082-ui-stack` |
|
|
51
53
|
| 输入范围 | Managed/CNG Expo 项目 |
|
|
52
|
-
|
|
|
54
|
+
| `verified` JS/UI 能力 | `expo-router`、`expo-linking`、`expo-constants`、`react-native-reanimated`、`react-native-svg` |
|
|
55
|
+
| `preview` 原生能力 | `expo-file-system`、`expo-image-picker` |
|
|
56
|
+
| `experimental` 能力 | `expo-location`、`expo-camera`、`expo-notifications`、`react-native-gesture-handler` |
|
|
53
57
|
| 构建链 | `doctor -> init -> bundle -> build-hap` |
|
|
54
58
|
| 主 sample | `examples/official-ui-stack-sample` |
|
|
59
|
+
| preview sample | `examples/official-native-capabilities-sample` |
|
|
55
60
|
| 回归基线 | `examples/official-app-shell-sample`、`examples/official-minimal-sample` |
|
|
56
61
|
|
|
57
62
|
<details>
|
|
58
|
-
<summary><strong
|
|
63
|
+
<summary><strong>当前仍不在 verified 正式承诺范围</strong></summary>
|
|
59
64
|
|
|
60
65
|
- bare Expo
|
|
61
|
-
- `expo-image-picker`
|
|
62
|
-
- `expo-file-system`
|
|
66
|
+
- `expo-file-system`、`expo-image-picker` 仍只属于 `preview`
|
|
63
67
|
- `expo-location`
|
|
64
68
|
- `expo-camera`
|
|
65
69
|
- `expo-notifications`
|
|
@@ -130,6 +134,7 @@ pnpm install --ignore-scripts
|
|
|
130
134
|
cd /path/to/app
|
|
131
135
|
pnpm exec expo-harmony doctor --project-root .
|
|
132
136
|
pnpm exec expo-harmony doctor --project-root . --strict
|
|
137
|
+
pnpm exec expo-harmony doctor --project-root . --target-tier preview
|
|
133
138
|
```
|
|
134
139
|
|
|
135
140
|
2. 生成或刷新受管 Harmony sidecar:
|
|
@@ -157,24 +162,20 @@ pnpm exec expo-harmony build-hap --mode release
|
|
|
157
162
|
常见使用判断:
|
|
158
163
|
|
|
159
164
|
- 想知道当前项目是否还在公开矩阵里:跑 `doctor --strict`
|
|
165
|
+
- 想知道项目是否只落在 preview / experimental:跑 `doctor --target-tier preview` 或 `doctor --target-tier experimental`
|
|
160
166
|
- 刚改过依赖、Expo 配置或插件:先跑 `sync-template`
|
|
161
167
|
- 只想验证 JS/UI 侧是否能打包:跑 `bundle`
|
|
162
168
|
- 准备进 DevEco Studio 或本机构建 HAP:先跑 `env`
|
|
163
169
|
|
|
164
170
|
## 支持矩阵
|
|
165
171
|
|
|
166
|
-
`v1.
|
|
172
|
+
`v1.6` 开始改成支持分层:
|
|
167
173
|
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
- RNOH / `@react-native-oh/react-native-harmony-cli`:`0.82.18`
|
|
172
|
-
- App Shell 依赖:`expo-router`、`expo-linking`、`expo-constants`
|
|
173
|
-
- UI stack 依赖:`react-native-reanimated`、`react-native-svg`
|
|
174
|
-
- Harmony adapter:对应两项 `@react-native-oh-tpl/*` exact Git specifier
|
|
175
|
-
- 原生标识:至少配置 `android.package` 或 `ios.bundleIdentifier`
|
|
174
|
+
- `verified`:唯一公开矩阵仍是 `expo55-rnoh082-ui-stack`
|
|
175
|
+
- `preview`:`expo-file-system`、`expo-image-picker`
|
|
176
|
+
- `experimental`:`expo-location`、`expo-camera`、`expo-notifications`、`react-native-gesture-handler`
|
|
176
177
|
|
|
177
|
-
|
|
178
|
+
`doctor --strict` 继续只代表 `verified`。`doctor --target-tier preview` 会在同一 runtime matrix 下额外放行 preview 能力,但这不等于它们已经进入正式承诺。
|
|
178
179
|
|
|
179
180
|
完整白名单、配对规则、exact specifier、issue code 与 release gate 见 [docs/support-matrix.md](./docs/support-matrix.md)。
|
|
180
181
|
|
|
@@ -182,6 +183,8 @@ pnpm exec expo-harmony build-hap --mode release
|
|
|
182
183
|
|
|
183
184
|
- `examples/official-ui-stack-sample`
|
|
184
185
|
当前唯一对外主 sample,同时覆盖 router、linking、constants、SVG、reanimated 和 Harmony sidecar 构建链。
|
|
186
|
+
- `examples/official-native-capabilities-sample`
|
|
187
|
+
v1.6 新增的 preview sample,用来承接 `expo-file-system` 与 `expo-image-picker` 的 bridge、permission 与 bundle 骨架验收。
|
|
185
188
|
- `examples/official-app-shell-sample`
|
|
186
189
|
`v1.1` App Shell 回归基线,用来防止 UI-stack 收口引入 router 退化。
|
|
187
190
|
- `examples/official-minimal-sample`
|
|
@@ -190,6 +193,7 @@ pnpm exec expo-harmony build-hap --mode release
|
|
|
190
193
|
详见:
|
|
191
194
|
|
|
192
195
|
- [官方 UI Stack sample 指南](./docs/official-ui-stack-sample.md)
|
|
196
|
+
- [官方 Native Capabilities sample 指南](./docs/official-native-capabilities-sample.md)
|
|
193
197
|
- [官方 App Shell sample 指南](./docs/official-app-shell-sample.md)
|
|
194
198
|
- [官方最小 sample 指南](./docs/official-minimal-sample.md)
|
|
195
199
|
|
|
@@ -199,6 +203,7 @@ pnpm exec expo-harmony build-hap --mode release
|
|
|
199
203
|
| --- | --- |
|
|
200
204
|
| `expo-harmony doctor` | 扫描 Expo 配置与依赖,输出迁移报告 |
|
|
201
205
|
| `expo-harmony doctor --strict` | 将当前矩阵准入检查作为正式 gate 执行 |
|
|
206
|
+
| `expo-harmony doctor --target-tier preview` | 在同一 runtime matrix 下评估项目是否至少落在 `preview` 能力层 |
|
|
202
207
|
| `expo-harmony init` | 生成 Harmony sidecar、autolinking 产物、metadata 与 package scripts |
|
|
203
208
|
| `expo-harmony sync-template` | 再次应用受管模板并检查 drift |
|
|
204
209
|
| `expo-harmony env` | 检查 DevEco / hvigor / hdc / signing 本地环境 |
|
|
@@ -242,6 +247,7 @@ pnpm exec expo-harmony build-hap --mode release
|
|
|
242
247
|
- 点击首页 motion rail 后能触发可见动画
|
|
243
248
|
- 动画完成后路由跳转仍正常
|
|
244
249
|
- `Build Debug Hap(s)` 成功
|
|
250
|
+
- `official-native-capabilities-sample` 至少完成 preview route 的 bundle 与 permission 产物检查
|
|
245
251
|
|
|
246
252
|
详见 [docs/npm-release.md](./docs/npm-release.md) 与 [docs/signing-and-release.md](./docs/signing-and-release.md)。
|
|
247
253
|
|
|
@@ -249,6 +255,7 @@ pnpm exec expo-harmony build-hap --mode release
|
|
|
249
255
|
|
|
250
256
|
- [支持矩阵](./docs/support-matrix.md)
|
|
251
257
|
- [CLI 构建指南](./docs/cli-build.md)
|
|
258
|
+
- [官方 Native Capabilities sample 指南](./docs/official-native-capabilities-sample.md)
|
|
252
259
|
- [官方 UI Stack sample 指南](./docs/official-ui-stack-sample.md)
|
|
253
260
|
- [官方 App Shell sample 指南](./docs/official-app-shell-sample.md)
|
|
254
261
|
- [官方最小 sample 指南](./docs/official-minimal-sample.md)
|
package/build/cli.js
CHANGED
|
@@ -25,7 +25,8 @@ async function run(argv = process.argv) {
|
|
|
25
25
|
.command('doctor')
|
|
26
26
|
.description('Inspect an Expo project and classify dependencies against the Harmony migration matrix')
|
|
27
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
|
|
28
|
+
.option('--strict', 'return a non-zero exit code when the project falls outside the validated verified matrix')
|
|
29
|
+
.option('--target-tier <tier>', 'evaluate the project against verified, preview, or experimental support tiers')
|
|
29
30
|
.option('--json', 'print JSON instead of a human-readable report')
|
|
30
31
|
.option('-o, --output <path>', 'write the JSON report to a file')
|
|
31
32
|
.action(doctor_1.runDoctorCommand);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { DoctorTargetTier } from '../types';
|
|
1
2
|
export interface DoctorCommandOptions {
|
|
2
3
|
projectRoot?: string;
|
|
3
4
|
strict?: boolean;
|
|
4
5
|
json?: boolean;
|
|
5
6
|
output?: string;
|
|
7
|
+
targetTier?: DoctorTargetTier | string;
|
|
6
8
|
}
|
|
7
9
|
export declare function runDoctorCommand(options: DoctorCommandOptions): Promise<void>;
|
package/build/commands/doctor.js
CHANGED
|
@@ -9,7 +9,8 @@ const constants_1 = require("../core/constants");
|
|
|
9
9
|
const report_1 = require("../core/report");
|
|
10
10
|
async function runDoctorCommand(options) {
|
|
11
11
|
const projectRoot = path_1.default.resolve(options.projectRoot ?? process.cwd());
|
|
12
|
-
const
|
|
12
|
+
const targetTier = resolveDoctorTargetTier(options.targetTier, Boolean(options.strict));
|
|
13
|
+
const report = await (0, report_1.buildDoctorReport)(projectRoot, { targetTier });
|
|
13
14
|
if (options.output) {
|
|
14
15
|
await (0, report_1.writeDoctorReport)(projectRoot, report, path_1.default.resolve(options.output));
|
|
15
16
|
}
|
|
@@ -22,3 +23,15 @@ async function runDoctorCommand(options) {
|
|
|
22
23
|
}
|
|
23
24
|
process.stdout.write((0, report_1.renderDoctorReport)(report) + '\n');
|
|
24
25
|
}
|
|
26
|
+
function resolveDoctorTargetTier(targetTier, strict) {
|
|
27
|
+
if (strict) {
|
|
28
|
+
return 'verified';
|
|
29
|
+
}
|
|
30
|
+
if (!targetTier) {
|
|
31
|
+
return 'verified';
|
|
32
|
+
}
|
|
33
|
+
if (targetTier === 'verified' || targetTier === 'preview' || targetTier === 'experimental') {
|
|
34
|
+
return targetTier;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Unsupported doctor target tier: ${targetTier}`);
|
|
37
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const TOOLKIT_PACKAGE_NAME = "expo-harmony-toolkit";
|
|
2
2
|
export declare const CLI_NAME = "expo-harmony";
|
|
3
|
-
export declare const TOOLKIT_VERSION = "1.
|
|
3
|
+
export declare const TOOLKIT_VERSION = "1.6.0";
|
|
4
4
|
export declare const TEMPLATE_VERSION = "rnoh-0.82.18";
|
|
5
5
|
export declare const RNOH_VERSION = "0.82.18";
|
|
6
6
|
export declare const RNOH_CLI_VERSION = "0.82.18";
|
package/build/core/constants.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DESIRED_PACKAGE_SCRIPTS = exports.HARMONY_RUNTIME_PRELUDE_RELATIVE_PATH = exports.HARMONY_ROUTER_ENTRY_FILENAME = exports.STRICT_ENV_EXIT_CODE = exports.STRICT_DOCTOR_EXIT_CODE = exports.DEFAULT_HVIGOR_PLUGIN_FILENAME = exports.PREBUILD_METADATA_FILENAME = exports.TOOLKIT_CONFIG_FILENAME = exports.BUILD_REPORT_FILENAME = exports.ENV_REPORT_FILENAME = exports.DOCTOR_REPORT_FILENAME = exports.MANIFEST_FILENAME = exports.GENERATED_SHIMS_DIR = exports.GENERATED_DIR = exports.SUPPORTED_EXPO_SDKS = exports.RNOH_CLI_VERSION = exports.RNOH_VERSION = exports.TEMPLATE_VERSION = exports.TOOLKIT_VERSION = exports.CLI_NAME = exports.TOOLKIT_PACKAGE_NAME = void 0;
|
|
4
4
|
exports.TOOLKIT_PACKAGE_NAME = 'expo-harmony-toolkit';
|
|
5
5
|
exports.CLI_NAME = 'expo-harmony';
|
|
6
|
-
exports.TOOLKIT_VERSION = '1.
|
|
6
|
+
exports.TOOLKIT_VERSION = '1.6.0';
|
|
7
7
|
exports.TEMPLATE_VERSION = 'rnoh-0.82.18';
|
|
8
8
|
exports.RNOH_VERSION = '0.82.18';
|
|
9
9
|
exports.RNOH_CLI_VERSION = '0.82.18';
|
package/build/core/report.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { DoctorReport } from '../types';
|
|
2
|
-
export declare function buildDoctorReport(projectRoot: string
|
|
1
|
+
import { DoctorTargetTier, DoctorReport } from '../types';
|
|
2
|
+
export declare function buildDoctorReport(projectRoot: string, options?: {
|
|
3
|
+
targetTier?: DoctorTargetTier;
|
|
4
|
+
}): Promise<DoctorReport>;
|
|
3
5
|
export declare function writeDoctorReport(projectRoot: string, report: DoctorReport, outputPath?: string): Promise<string>;
|
|
4
6
|
export declare function renderDoctorReport(report: DoctorReport): string;
|
package/build/core/report.js
CHANGED
|
@@ -12,18 +12,21 @@ const semver_1 = __importDefault(require("semver"));
|
|
|
12
12
|
const constants_1 = require("./constants");
|
|
13
13
|
const dependencyCatalog_1 = require("../data/dependencyCatalog");
|
|
14
14
|
const validatedMatrices_1 = require("../data/validatedMatrices");
|
|
15
|
+
const capabilities_1 = require("../data/capabilities");
|
|
15
16
|
const uiStack_1 = require("../data/uiStack");
|
|
16
17
|
const metadata_1 = require("./metadata");
|
|
17
18
|
const project_1 = require("./project");
|
|
18
19
|
const constants_2 = require("./constants");
|
|
19
20
|
const DEFAULT_RECORD = {
|
|
20
21
|
status: 'unknown',
|
|
22
|
+
supportTier: 'unsupported',
|
|
21
23
|
note: 'This dependency is not in the current compatibility catalog yet.',
|
|
22
24
|
};
|
|
23
|
-
async function buildDoctorReport(projectRoot) {
|
|
25
|
+
async function buildDoctorReport(projectRoot, options = {}) {
|
|
24
26
|
const loadedProject = await (0, project_1.loadProject)(projectRoot);
|
|
25
27
|
const expoSdkVersion = (0, project_1.detectExpoSdkVersion)(loadedProject.packageJson);
|
|
26
28
|
const matrix = validatedMatrices_1.VALIDATED_RELEASE_MATRICES[validatedMatrices_1.DEFAULT_VALIDATED_MATRIX_ID];
|
|
29
|
+
const targetTier = options.targetTier ?? 'verified';
|
|
27
30
|
const expoPlugins = (0, project_1.collectExpoPlugins)(loadedProject.expoConfig);
|
|
28
31
|
const expoSchemes = (0, project_1.collectExpoSchemes)(loadedProject.expoConfig);
|
|
29
32
|
const dependencyRecords = new Map();
|
|
@@ -37,7 +40,19 @@ async function buildDoctorReport(projectRoot) {
|
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
const dependencies = [...dependencyRecords.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
40
|
-
const blockingIssues = await collectBlockingIssues(loadedProject.projectRoot, loadedProject.expoConfig, loadedProject.packageJson, expoPlugins, expoSchemes, expoSdkVersion, dependencies, matrix);
|
|
43
|
+
const blockingIssues = await collectBlockingIssues(loadedProject.projectRoot, loadedProject.expoConfig, loadedProject.packageJson, expoPlugins, expoSchemes, expoSdkVersion, dependencies, matrix, targetTier);
|
|
44
|
+
const capabilities = (0, capabilities_1.getCapabilityDefinitionsForProject)(loadedProject.packageJson).map((definition) => ({
|
|
45
|
+
id: definition.id,
|
|
46
|
+
packageName: definition.packageName,
|
|
47
|
+
status: definition.status,
|
|
48
|
+
supportTier: definition.supportTier,
|
|
49
|
+
note: definition.note,
|
|
50
|
+
docsUrl: definition.docsUrl,
|
|
51
|
+
nativePackageNames: [...definition.nativePackageNames],
|
|
52
|
+
harmonyPermissions: [...definition.harmonyPermissions],
|
|
53
|
+
sampleRoute: definition.sampleRoute,
|
|
54
|
+
acceptanceChecklist: [...definition.acceptanceChecklist],
|
|
55
|
+
}));
|
|
41
56
|
const blockingDependencyNames = new Set(blockingIssues
|
|
42
57
|
.filter((issue) => issue.code.startsWith('dependency.') && issue.subject)
|
|
43
58
|
.map((issue) => issue.subject));
|
|
@@ -58,6 +73,7 @@ async function buildDoctorReport(projectRoot) {
|
|
|
58
73
|
rnohVersion: constants_1.RNOH_VERSION,
|
|
59
74
|
rnohCliVersion: constants_1.RNOH_CLI_VERSION,
|
|
60
75
|
expoSdkVersion,
|
|
76
|
+
targetTier,
|
|
61
77
|
expoConfig: {
|
|
62
78
|
name: loadedProject.expoConfig.name ?? null,
|
|
63
79
|
slug: loadedProject.expoConfig.slug ?? null,
|
|
@@ -74,6 +90,13 @@ async function buildDoctorReport(projectRoot) {
|
|
|
74
90
|
manual: resolvedDependencies.filter((dependency) => dependency.status === 'manual').length,
|
|
75
91
|
unknown: resolvedDependencies.filter((dependency) => dependency.status === 'unknown').length,
|
|
76
92
|
},
|
|
93
|
+
supportSummary: {
|
|
94
|
+
verified: resolvedDependencies.filter((dependency) => dependency.supportTier === 'verified').length,
|
|
95
|
+
preview: resolvedDependencies.filter((dependency) => dependency.supportTier === 'preview').length,
|
|
96
|
+
experimental: resolvedDependencies.filter((dependency) => dependency.supportTier === 'experimental').length,
|
|
97
|
+
unsupported: resolvedDependencies.filter((dependency) => dependency.supportTier === 'unsupported').length,
|
|
98
|
+
},
|
|
99
|
+
capabilities,
|
|
77
100
|
blockingIssues,
|
|
78
101
|
advisories,
|
|
79
102
|
warnings,
|
|
@@ -92,19 +115,29 @@ function renderDoctorReport(report) {
|
|
|
92
115
|
`Config: ${report.appConfigPath ?? 'not found'}`,
|
|
93
116
|
`Expo SDK: ${report.expoSdkVersion ?? 'unknown'} (recognized ${constants_1.SUPPORTED_EXPO_SDKS.join(', ')})`,
|
|
94
117
|
`Matrix: ${report.matrixId ?? 'none'}`,
|
|
118
|
+
`Target tier: ${report.targetTier}`,
|
|
95
119
|
`Eligibility: ${report.eligibility}`,
|
|
96
120
|
`Schemes: ${report.expoConfig.schemes.join(', ') || 'none'}`,
|
|
97
121
|
`Plugins: ${report.expoConfig.plugins.join(', ') || 'none'}`,
|
|
98
122
|
`RNOH template: ${report.templateVersion} / runtime ${report.rnohVersion}`,
|
|
99
123
|
`Summary: ${report.summary.supported} supported, ${report.summary.manual} manual, ${report.summary.unknown} unknown (${report.summary.total} total)`,
|
|
124
|
+
`Support tiers: ${report.supportSummary.verified} verified, ${report.supportSummary.preview} preview, ${report.supportSummary.experimental} experimental, ${report.supportSummary.unsupported} unsupported`,
|
|
100
125
|
'',
|
|
101
126
|
`Dependencies:`,
|
|
102
127
|
...report.dependencies.map((dependency) => {
|
|
103
128
|
const replacement = dependency.replacement ? ` | replacement: ${dependency.replacement}` : '';
|
|
104
129
|
const blocking = dependency.blocking ? ' | blocking: yes' : '';
|
|
105
|
-
return `- [${dependency.status}] ${dependency.name}@${dependency.version} (${dependency.source}) - ${dependency.note}${replacement}${blocking}`;
|
|
130
|
+
return `- [${dependency.status}/${dependency.supportTier}] ${dependency.name}@${dependency.version} (${dependency.source}) - ${dependency.note}${replacement}${blocking}`;
|
|
106
131
|
}),
|
|
107
132
|
];
|
|
133
|
+
if (report.capabilities.length > 0) {
|
|
134
|
+
sections.push('', 'Capabilities:', ...report.capabilities.map((capability) => {
|
|
135
|
+
const permissions = capability.harmonyPermissions.length > 0
|
|
136
|
+
? ` | permissions: ${capability.harmonyPermissions.join(', ')}`
|
|
137
|
+
: '';
|
|
138
|
+
return `- [${capability.status}/${capability.supportTier}] ${capability.packageName} -> ${capability.nativePackageNames.join(', ') || 'toolkit-managed bridge'} | sample: ${capability.sampleRoute}${permissions}`;
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
108
141
|
if (report.blockingIssues.length > 0) {
|
|
109
142
|
sections.push('', 'Blocking issues:', ...report.blockingIssues.map((issue) => `- ${issue.code}: ${issue.message}${issue.subject ? ` (${issue.subject})` : ''}`));
|
|
110
143
|
}
|
|
@@ -123,13 +156,14 @@ function createDependencyRecord(name, version, source) {
|
|
|
123
156
|
version,
|
|
124
157
|
source,
|
|
125
158
|
status: matrixRecord.status,
|
|
159
|
+
supportTier: matrixRecord.supportTier,
|
|
126
160
|
blocking: false,
|
|
127
161
|
note: matrixRecord.note,
|
|
128
162
|
replacement: matrixRecord.replacement,
|
|
129
163
|
docsUrl: matrixRecord.docsUrl,
|
|
130
164
|
};
|
|
131
165
|
}
|
|
132
|
-
async function collectBlockingIssues(projectRoot, expoConfig, packageJson, expoPlugins, expoSchemes, expoSdkVersion, dependencies, matrix) {
|
|
166
|
+
async function collectBlockingIssues(projectRoot, expoConfig, packageJson, expoPlugins, expoSchemes, expoSdkVersion, dependencies, matrix, targetTier) {
|
|
133
167
|
const issues = [];
|
|
134
168
|
const dependencyMap = new Map(dependencies.map((dependency) => [dependency.name, dependency]));
|
|
135
169
|
if (expoSdkVersion !== matrix.expoSdkVersion) {
|
|
@@ -167,10 +201,10 @@ async function collectBlockingIssues(projectRoot, expoConfig, packageJson, expoP
|
|
|
167
201
|
}
|
|
168
202
|
}
|
|
169
203
|
for (const dependency of dependencies) {
|
|
170
|
-
if (!
|
|
204
|
+
if (!isDependencyAllowedForTargetTier(dependency.name, matrix, targetTier)) {
|
|
171
205
|
issues.push({
|
|
172
206
|
code: 'dependency.not_allowed',
|
|
173
|
-
message: `${dependency.name} is outside the
|
|
207
|
+
message: `${dependency.name} is outside the ${targetTier} support tier for ${matrix.id}.`,
|
|
174
208
|
subject: dependency.name,
|
|
175
209
|
});
|
|
176
210
|
}
|
|
@@ -282,6 +316,12 @@ function buildWarnings(expoConfig, expoSdkVersion, dependencies) {
|
|
|
282
316
|
if (dependencies.some((dependency) => dependency.status === 'manual')) {
|
|
283
317
|
warnings.push('Manual-review dependencies were detected. They remain outside the current validated matrix even though the toolkit can still scaffold exploratory files.');
|
|
284
318
|
}
|
|
319
|
+
if (dependencies.some((dependency) => dependency.supportTier === 'preview')) {
|
|
320
|
+
warnings.push('Preview-tier dependencies were detected. The toolkit can scaffold and bundle them, but runtime behavior is not part of the verified public promise yet.');
|
|
321
|
+
}
|
|
322
|
+
if (dependencies.some((dependency) => dependency.supportTier === 'experimental')) {
|
|
323
|
+
warnings.push('Experimental-tier dependencies were detected. Expect bridge drift, runtime gaps, or additional manual validation before claiming release readiness.');
|
|
324
|
+
}
|
|
285
325
|
if (dependencies.some((dependency) => dependency.status === 'unknown')) {
|
|
286
326
|
warnings.push('Unknown dependencies were detected. The toolkit can scaffold the project, but runtime portability is not guaranteed.');
|
|
287
327
|
}
|
|
@@ -294,6 +334,16 @@ function buildAdvisories(expoConfig) {
|
|
|
294
334
|
}
|
|
295
335
|
return advisories;
|
|
296
336
|
}
|
|
337
|
+
function isDependencyAllowedForTargetTier(dependencyName, matrix, targetTier) {
|
|
338
|
+
if (matrix.allowedDependencies.includes(dependencyName)) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
const capability = capabilities_1.CAPABILITY_BY_PACKAGE[dependencyName];
|
|
342
|
+
if (!capability) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
return (0, capabilities_1.isSupportTierAllowed)(capability.supportTier, targetTier);
|
|
346
|
+
}
|
|
297
347
|
function matchesVersionRange(rawVersion, range) {
|
|
298
348
|
const coerced = semver_1.default.coerce(rawVersion);
|
|
299
349
|
if (!coerced) {
|