deveco_hmigbot 0.21.5
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 +26 -0
- package/README.md +50 -0
- package/agents/hmigbot-worker.md +61 -0
- package/agents/hmigbot.md +22 -0
- package/agents/workflow-subagent.md +55 -0
- package/commands/hmigbot.md +17 -0
- package/dist/index.js +1 -0
- package/manifest.json +19 -0
- package/package.json +29 -0
- package/skills/migrate-core/FILES.md +26 -0
- package/skills/migrate-core/SKILL.md +484 -0
- package/skills/migrate-core/references/README.md +64 -0
- package/skills/migrate-core/references/flow/arkts-vector-gate.md +55 -0
- package/skills/migrate-core/references/flow/build-error-patterns.md +52 -0
- package/skills/migrate-core/references/flow/conventions-template.md +244 -0
- package/skills/migrate-core/references/flow/navigation-migration.md +42 -0
- package/skills/migrate-core/references/flow/platform-api-guards.md +59 -0
- package/skills/migrate-core/references/flow/platform-model-gaps.md +43 -0
- package/skills/migrate-core/references/flow/resource-conversion.md +46 -0
- package/skills/migrate-core/references/flow/ui-layout-semantics.md +124 -0
- package/skills/migrate-core/references/flow/unit-breakdown.md +42 -0
- package/skills/migrate-core/references/host-capabilities.md +24 -0
- package/skills/migrate-core/references/topics/app-identity.md +214 -0
- package/skills/migrate-core/references/topics/env-doctor.md +245 -0
- package/skills/migrate-core/references/topics/i18n/README.md +458 -0
- package/skills/migrate-core/references/topics/i18n/references/code-examples.md +304 -0
- package/skills/migrate-core/references/topics/i18n/references/common-pitfalls.md +354 -0
- package/skills/migrate-core/references/topics/i18n/references/dynamic-language-switch.md +464 -0
- package/skills/migrate-core/references/topics/i18n/references/language-codes.md +104 -0
- package/skills/migrate-core/references/topics/icon-sizing.md +98 -0
- package/skills/migrate-core/references/topics/library-migration/README.md +234 -0
- package/skills/migrate-core/references/topics/library-migration/closed-source-sdk.md +128 -0
- package/skills/migrate-core/references/topics/library-migration/download-api-decision.md +84 -0
- package/skills/migrate-core/references/topics/library-migration/library-mapping-table.md +100 -0
- package/skills/migrate-core/references/topics/library-migration/napi-compile-guide.md +84 -0
- package/skills/migrate-core/references/topics/library-migration/ohpm-search-guide.md +73 -0
- package/skills/migrate-core/references/topics/library-migration/stdlib-mapping-table.md +34 -0
- package/skills/migrate-core/references/topics/resources/aar-decompile.md +25 -0
- package/skills/migrate-core/references/topics/resources/conversion-rules.md +625 -0
- package/skills/migrate-core/references/topics/resources/dependency-analysis-rules.md +328 -0
- package/skills/migrate-core/references/topics/resources/material-design-icons.md +173 -0
- package/skills/migrate-core/references/topics/resources/svg-fix-patterns.md +175 -0
- package/skills/migrate-core/references/topics/resources/xml-drawable-to-svg-rules.md +513 -0
- package/skills/migrate-core/references/topics/system-capabilities/README.md +331 -0
- package/skills/migrate-core/references/topics/system-capabilities/avplayer-guide.md +161 -0
- package/skills/migrate-core/references/topics/system-capabilities/background-tasks.md +403 -0
- package/skills/migrate-core/references/topics/system-capabilities/browser-intent.md +121 -0
- package/skills/migrate-core/references/topics/system-capabilities/camera-picker.md +118 -0
- package/skills/migrate-core/references/topics/system-capabilities/document-picker.md +246 -0
- package/skills/migrate-core/references/topics/system-capabilities/file-utils.md +131 -0
- package/skills/migrate-core/references/topics/system-capabilities/permission-helper.md +112 -0
- package/skills/migrate-core/references/topics/system-capabilities/photo-access-helper.md +208 -0
- package/skills/migrate-core/references/topics/system-capabilities/print-management.md +213 -0
- package/skills/migrate-core/references/topics/system-capabilities/share-panel.md +177 -0
- package/skills/migrate-core/references/topics/system-capabilities/system-settings.md +322 -0
- package/skills/migrate-core/references/topics/system-capabilities/telephony-dial.md +49 -0
- package/skills/migrate-core/references/topics/system-capabilities/video-playback.md +42 -0
- package/skills/migrate-core/references/topics/system-capabilities/webview-patterns.md +38 -0
- package/skills/migrate-core/references/topics/ui-alignment/README.md +344 -0
- package/skills/migrate-core/references/topics/ui-alignment/references/dark-mode.md +47 -0
- package/skills/migrate-core/references/topics/ui-alignment/references/layout-mapping.md +301 -0
- package/skills/migrate-core/references/topics/ui-alignment/references/visual-patterns.md +411 -0
- package/skills/migrate-core/scripts/closure/check-anchors.mjs +186 -0
- package/skills/migrate-core/scripts/closure/check-api-guards.mjs +175 -0
- package/skills/migrate-core/scripts/closure/check-consumers.mjs +301 -0
- package/skills/migrate-core/scripts/closure/check-permissions.mjs +165 -0
- package/skills/migrate-core/scripts/closure/check-resources.mjs +130 -0
- package/skills/migrate-core/scripts/closure/check-routes.mjs +527 -0
- package/skills/migrate-core/scripts/closure/check-safearea.mjs +122 -0
- package/skills/migrate-core/scripts/closure/check-stubs.mjs +69 -0
- package/skills/migrate-core/scripts/closure/closure-suite.mjs +256 -0
- package/skills/migrate-core/scripts/closure/idioms.json +105 -0
- package/skills/migrate-core/scripts/convert/convert-resources.mjs +437 -0
- package/skills/migrate-core/scripts/feasibility/feasibility.mjs +235 -0
- package/skills/migrate-core/scripts/feasibility/tables/cross-platform.json +11 -0
- package/skills/migrate-core/scripts/feasibility/tables/deprecated-api.json +10 -0
- package/skills/migrate-core/scripts/feasibility/tables/imported-arkts-core.json +425 -0
- package/skills/migrate-core/scripts/feasibility/tables/lib-equivalence.json +206 -0
- package/skills/migrate-core/scripts/feasibility/tables/system-capabilities.json +22 -0
- package/skills/migrate-core/scripts/front.mjs +107 -0
- package/skills/migrate-core/scripts/interface/ark-extract.mjs +172 -0
- package/skills/migrate-core/scripts/interface/interface.mjs +152 -0
- package/skills/migrate-core/scripts/ledger/ledger.mjs +383 -0
- package/skills/migrate-core/scripts/ledger/parse-cards.mjs +98 -0
- package/skills/migrate-core/scripts/lib/literals.mjs +37 -0
- package/skills/migrate-core/scripts/lib/scan.mjs +315 -0
- package/skills/migrate-core/scripts/smoke/align-sdk.mjs +118 -0
- package/skills/migrate-core/scripts/smoke/ensure-sign.mjs +53 -0
- package/skills/migrate-core/scripts/smoke/smoke.mjs +238 -0
- package/skills/migrate-core/scripts/smoke/verdict.mjs +31 -0
- package/skills/migrate-core/scripts/smoke/walk.mjs +480 -0
- package/skills/migrate-core/scripts/transpile/mapping.json +76 -0
- package/skills/migrate-core/scripts/transpile/transpile-layout.mjs +404 -0
- package/skills/migrate-core/scripts/vectors/run-arkts-vectors.mjs +107 -0
- package/skills/migrate-core/scripts/vectors/setup-arkts-test.mjs +90 -0
- package/skills/migrate-core/scripts/wire/extractors.mjs +258 -0
- package/skills/migrate-core/scripts/wire/wire-routes.mjs +507 -0
- package/skills/migrate-core/templates/acceptance.js +365 -0
- package/skills/migrate-core/templates/explore.js +86 -0
- package/skills/migrate-core/templates/implement.js +211 -0
- package/skills/migrate-core/templates/mig_slices.js +491 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# references/ — 领域语料(带验证位的假设缓存)
|
|
2
|
+
|
|
3
|
+
语料的地位:**加速器,不是权威**。真值只有两个——源应用(该做什么)与目标平台可执行事实(什么写法成立)。语料与两者冲突时,语料让路并被修正。
|
|
4
|
+
|
|
5
|
+
## 目录形态
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
references/
|
|
9
|
+
├── README.md 本页:加载索引(按场景取件,不整目录搬运)
|
|
10
|
+
├── host-capabilities.md 宿主能力实测清单(哪些工具/命令可用、已知限制)
|
|
11
|
+
├── flow/ 流程必读件:SKILL.md 各阶段点名引用的方法与模板
|
|
12
|
+
│ ├── conventions-template.md 2b 装配 conventions.md 的种子(§A 硬规则 / §A2 运行期规则 / §E UI 约定…)
|
|
13
|
+
│ ├── unit-breakdown.md 切片/单元拆分口径
|
|
14
|
+
│ ├── navigation-migration.md 导航形态谱(manifest / res/navigation / Compose NavHost / 单 Activity 壳)与分派表
|
|
15
|
+
│ ├── ui-layout-semantics.md 布局语义映射(XML/Compose → ArkUI)
|
|
16
|
+
│ ├── resource-conversion.md 资源转换细则(convert-resources 的规则说明)
|
|
17
|
+
│ ├── platform-model-gaps.md 线程/后台/生命周期不 1:1 的平台模型差异
|
|
18
|
+
│ ├── platform-api-guards.md 会抛 API 的守卫写法(check-api-guards 门的知识源)
|
|
19
|
+
│ ├── build-error-patterns.md 构建/编译报错高频模式(先修根因再重编)
|
|
20
|
+
│ └── arkts-vector-gate.md 算法域向量门(ArkTS Local Test)
|
|
21
|
+
└── topics/ 按场景取件的专题(原独立技能已吸收,不再单独注册)
|
|
22
|
+
├── app-identity.md 应用名称 / 包名 / 图标规格
|
|
23
|
+
├── icon-sizing.md 位图图标丢尺寸导致过大/拉伸的检测→测量→修复
|
|
24
|
+
├── env-doctor.md 环境体检(SDK/hdc/签名/设备)
|
|
25
|
+
├── i18n/ 多语言迁移(README 起步,references/ 八专题按需)
|
|
26
|
+
├── library-migration/ 三方库替代:映射表(L1 ohpm / L2 原生 / L3 NAPI / L4 Web / L5 自研)、ohpm 搜索、闭源 SDK、NAPI 编译、标准库原语查法
|
|
27
|
+
├── system-capabilities/ 系统能力对照(电话/后台/权限/相机/文件/分享/设置/打印/播放/Web…)
|
|
28
|
+
├── ui-alignment/ UI 还原对齐(layout-mapping / visual-patterns / dark-mode)
|
|
29
|
+
└── resources/ 资源侧细则:转换规则、依赖分析、Material 图标、SVG 修复、XML drawable→SVG、AAR 反编
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
派生件:`topics/library-migration/library-mapping-table.md` → `scripts/feasibility/tables/imported-arkts-core.json`(改表后重跑套件仓 `devtools/import-arkts-core-map.mjs`,研发面,不随技能分发);`topics/system-capabilities/` 是 `scripts/feasibility/tables/system-capabilities.json` 的知识源与扩表依据。
|
|
33
|
+
|
|
34
|
+
## 验证位机制
|
|
35
|
+
|
|
36
|
+
每条可判定的语料条目(映射规则、API 用法、陷阱)携带三件套:
|
|
37
|
+
- **出处**(官方表/实证案例/裁决记录)
|
|
38
|
+
- **验证方法**(编译探针 / SDK 声明文件核对 / 真机行为)
|
|
39
|
+
- **验证状态**:`已验@API版本` / `未验` / `已证伪→修正版`
|
|
40
|
+
|
|
41
|
+
使用纪律:默认直接用(缓存命中零成本);只在四个触发点验证且**一条终身一次**——①新条目入库;②SDK 大版本升级(批量编译探针,脚本活);③运行中被证伪(降级并修正,不许只在旁边贴"作废");④高风险场景使用。
|
|
42
|
+
|
|
43
|
+
## 回流
|
|
44
|
+
|
|
45
|
+
每次迁移同时是一次语料测试:运行确证的新坑 → 判"能否写成机器规则"——能则进 `scripts/` 的数据表(idioms/等价缓存/能力表),不能则进本目录条目;被证伪的当场修正。所有修订过 `benchmarks/run.mjs` 才提交。
|
|
46
|
+
|
|
47
|
+
## 加载索引(渐进式披露的目录页——流程未点名的场景按此取件,不整目录搬运)
|
|
48
|
+
|
|
49
|
+
| 场景/信号 | 取件 |
|
|
50
|
+
|---|---|
|
|
51
|
+
| 线程/后台/生命周期不 1:1(平台模型差异) | flow/platform-model-gaps.md |
|
|
52
|
+
| 构建/编译报错修不动 | flow/build-error-patterns.md(高频实证模式,先修根因再重编) |
|
|
53
|
+
| 会抛 API / 权限 / 运行期守卫 | flow/platform-api-guards.md |
|
|
54
|
+
| 三方库找替代 / ohpm 搜索 / 标准库原语等价 | topics/library-migration/README.md 起步 → 同目录专题 |
|
|
55
|
+
| 系统能力(权限/相册/文件/后台/分享/拨号/打印/设置…) | topics/system-capabilities/README.md 起步 → 同目录专题 |
|
|
56
|
+
| 音频播放 / 视频播放 / Web 容器 | topics/system-capabilities/avplayer-guide.md / video-playback.md / webview-patterns.md |
|
|
57
|
+
| 多语言应用(源含多 locale values-*) | topics/i18n/README.md(references/ 四专题按需) |
|
|
58
|
+
| 应用名称 / 包名 / 图标规格 | topics/app-identity.md |
|
|
59
|
+
| 图标过大 / 拉伸 / 比例异常 | topics/icon-sizing.md |
|
|
60
|
+
| UI 还原对不齐 / 深色模式失效 | topics/ui-alignment/README.md → references/(layout-mapping / visual-patterns / dark-mode) |
|
|
61
|
+
| 资源转换细节 / SVG 修不好 / AAR 里的资源 | flow/resource-conversion.md → topics/resources/ |
|
|
62
|
+
| 环境体检(SDK/hdc/签名/设备) | topics/env-doctor.md |
|
|
63
|
+
| 宿主能力实测清单 | host-capabilities.md |
|
|
64
|
+
| 打法沿革与语料治理(人读;不随安装分发) | 套件仓 docs/legacy-playbook-android-to-harmony.md、docs/corpus-absorption.md |
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ArkTS 黄金向量门(纯 ArkTS + Local Test)
|
|
2
|
+
|
|
3
|
+
算法/协议密集域的确定性向量门。逻辑是纯 ArkTS `.ets`,向量门用 HarmonyOS **Local Test**(Hypium)——**主机侧跑、不上设备/模拟器、~5s、退出码经 wrapper 裁决**。替代旧的 node `run-vectors.mjs`(逻辑改 `.ets` 后 node 跑不了)。
|
|
4
|
+
|
|
5
|
+
## 产出(每个算法域三件,均在 `entry/src/test/`)
|
|
6
|
+
|
|
7
|
+
### 1. 向量数据模块 `<域>Vectors.ets`(从 `spec/vectors/*.json` 生成,纯数据)
|
|
8
|
+
```ets
|
|
9
|
+
export interface FmtCase { name: string; value: string; dec: string; grp: string; ns: string; exp: string }
|
|
10
|
+
export const FMT_VECTORS: FmtCase[] = [
|
|
11
|
+
{ name: 'formatter_decimal_number', value: '12345', dec: '.', grp: ',', ns: 'INTERNATIONAL', exp: '12,345' },
|
|
12
|
+
// …从源单元测试逐条提取,untrimmed
|
|
13
|
+
];
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 2. 固定 data-driven harness `<域>.test.ets`(写一次,逻辑就一个循环)
|
|
17
|
+
```ets
|
|
18
|
+
import { describe, it, expect } from '@ohos/hypium';
|
|
19
|
+
import { format, NumberingSystem } from '../main/ets/calculator/NumberFormatter';
|
|
20
|
+
import { FMT_VECTORS, FmtCase } from '../main/ets/calculator/... '; // 或本 test 目录同放数据模块
|
|
21
|
+
|
|
22
|
+
export default function numberFormatterGate() {
|
|
23
|
+
describe('NumberFormatterGate', () => { // ← suite 名即 --scope 值
|
|
24
|
+
FMT_VECTORS.forEach((v: FmtCase, i: number) => {
|
|
25
|
+
it(v.name, 0, () => {
|
|
26
|
+
const ns: NumberingSystem = v.ns === 'INDIAN' ? 'INDIAN' : 'INTERNATIONAL';
|
|
27
|
+
expect(format(v.value, v.dec, v.grp, ns)).assertEqual(v.exp);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
> harness 不含业务判断——只 import 逻辑 + 逐向量 `assertEqual`。测试正确性负担锁在这一个循环,不随迁移增长。
|
|
34
|
+
|
|
35
|
+
### 3. 注册 `List.test.ets`
|
|
36
|
+
```ets
|
|
37
|
+
import numberFormatterGate from './NumberFormatter.test';
|
|
38
|
+
export default function testsuite() { numberFormatterGate(); }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 门命令(selfTest / gate 里写这条)
|
|
42
|
+
```
|
|
43
|
+
$ node $HMIG/scripts/vectors/run-arkts-vectors.mjs --project $DST --module entry --scope <域>Gate
|
|
44
|
+
```
|
|
45
|
+
- 内部跑 `hvigorw test -p module=entry -p coverage=false --no-daemon`(主机侧、不上设备)。
|
|
46
|
+
- **退出码经 wrapper 裁决**:解析 `entry/.test/default/intermediates/test/coverage_data/test_result.txt` 的 `Tests run: N, Failure: F, Error: E`——`F=E=0 且 N>0` → 0,否则 1(点名失败用例)。**不能信 hvigorw 自身退出码**(用例失败它仍 `BUILD SUCCESSFUL`、退出 0)。
|
|
47
|
+
- 省 `--scope` 跑该模块全部 test;多域用逗号。
|
|
48
|
+
|
|
49
|
+
## 脚手架前置(工程模板需带)
|
|
50
|
+
- `entry/oh-package.json5` devDependencies 加 `@ohos/hypium`(`@ohos/hamock` 随之)。
|
|
51
|
+
- `entry/src/test/` 目录 + `List.test.ets`。
|
|
52
|
+
- **离线兜底**:`ohpm install` 若连不上 `repo.harmonyos.com`,从 SDK(`command-line-tools/sdk/default/openharmony/ets/build-tools/ets-loader/node_modules/@ohos/hypium`)或既有工程 `oh_modules/.ohpm/@ohos+hypium@*` 链入 `entry/oh_modules/@ohos/`。
|
|
53
|
+
|
|
54
|
+
## 为什么不是 .ts
|
|
55
|
+
`.ts` 不能 import `.ets`/@ohos(官方 FAQ faqs-arkts-82/ability-98),一旦逻辑要碰系统能力就撞墙;且 `.ets` 静态检查更强、性能更好(API10+ 官方建议 .ets)。套件的".ts 可擦除子集"本就是 ArkTS 子集,`.ts→.ets` 适配成本≈0(NumberFormatter 一字不改直编直过、23 向量全绿实测)。
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# 编译错误模式速查(build-fix 循环用)
|
|
2
|
+
|
|
3
|
+
> ⚠ 本文样例中 `getContext(this)` 为已废弃写法:组件内一律改 `this.getUIContext().getHostContext()`;Ability 内用 `this.context`。
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
> 兼官方 hmos-arkts-syntax-checker / deprecated-interface-checker 蒸馏落点。
|
|
7
|
+
> 修错纪律:只修编译器报的错,不顺手重构;一个根因(如缺 interface)常消一片报错——修根因后先重建再继续;上限 20 轮,超限带残余错误清单上报。
|
|
8
|
+
|
|
9
|
+
## 1. 错误码 → 确定性修法
|
|
10
|
+
|
|
11
|
+
| 错误码/模式 | 报错 | 修法 |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `arkts-limited-throw` | throw 不接受任意类型 | `throw (err instanceof Error) ? err : new Error(String(err))` |
|
|
14
|
+
| `arkts-no-obj-literals-as-types` | 对象字面量不能作类型声明 | 定义具名 `interface`,禁内联 `{ key: Type }` |
|
|
15
|
+
| `arkts-no-untyped-obj-literals` | 字面量须对应显式类/接口 | 先赋给带类型变量再用:`const r: MyIface = {...}` |
|
|
16
|
+
| `arkts-no-any-unknown` | 禁 any / unknown | 换具体类型或 `object` |
|
|
17
|
+
| `arkts-no-var` | 禁 var | `let`/`const` |
|
|
18
|
+
| `10903329` | Unknown resource name | 资源须真存在于 `resources/base/`;`$r('sys.media.ohos_ic_public_*')` 系统图标名随 SDK 变、可能不存在——改 `$r('app.media.ic_public_*')` 并补图入 media |
|
|
19
|
+
| `10505001` | Resource[] 不能赋 ResourceColor | 去掉数组括号:`.fontColor($r('app.color.x'))`(SymbolGlyph 例外) |
|
|
20
|
+
| `00303221`(hvigor 校验码,官方错误码文档未收录) | permission 必须是 SDK 预定义值 | 删非法权限;`ohos.permission.NOTIFICATION` 不存在,拿不准就不声明;权限名以 SDK `ets/api/permissions.d.ts` 为准 |
|
|
21
|
+
| Cannot find name 'xxx' | 缺 import | 按 Kit 聚合补 import(见 §3);ArkUI 内置组件不 import |
|
|
22
|
+
| await 在非 async 函数 | — | 外层函数补 `async` |
|
|
23
|
+
| 缺 build() | @ComponentV2/@Component 必须有 build() | 补 `build() {}` |
|
|
24
|
+
| Duplicate identifier | 重复声明 | 删除或改名 |
|
|
25
|
+
|
|
26
|
+
不在表内的错:读报错原文+源文件上下文,按 ArkTS 严格模式规则修。
|
|
27
|
+
|
|
28
|
+
## 2. 严格模式高频根因(写码前过一遍)
|
|
29
|
+
|
|
30
|
+
- 禁 `any`/`var`/动态属性访问(typed 对象上禁 `obj['key']`)。
|
|
31
|
+
- throw 只接 Error 实例;对象字面量必须匹配已声明接口;禁内联字面量返回类型。
|
|
32
|
+
- `$r()` 编译期校验,引用的资源必须存在。
|
|
33
|
+
- module.json5 权限名必须 SDK 预定义。
|
|
34
|
+
|
|
35
|
+
## 3. 常用 Kit 导入速查
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { http } from '@kit.NetworkKit';
|
|
39
|
+
import { preferences, relationalStore } from '@kit.ArkData';
|
|
40
|
+
import { promptAction } from '@kit.ArkUI';
|
|
41
|
+
import { UIAbility, common, Want } from '@kit.AbilityKit';
|
|
42
|
+
import { fileIo } from '@kit.CoreFileKit';
|
|
43
|
+
import { hilog } from '@kit.PerformanceAnalysisKit';
|
|
44
|
+
// JSON 内建;ArkUI 内置组件(Text/Column/List…)不需要 import
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 4. 废弃 API
|
|
48
|
+
|
|
49
|
+
废弃 API → 替代写法查数据表 `scripts/feasibility/tables/deprecated-api.json`(@ohos.router→Navigation、getContext(this)→getUIContext().getHostContext()、全局弹窗→UIContext 实例形、@ohos.* 裸导入→@kit 聚合)。
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
蒸馏自 corpus/candidates/hmos_fix_build_errors,2026-09 吸收
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# conventions.md 模板(2b 用)
|
|
2
|
+
|
|
3
|
+
**产出体量 ≤8K**(全文会机械注入每片实现 prompt——规则行保留、解释与示例砍到最短;工程无关节整节不写)。
|
|
4
|
+
|
|
5
|
+
conventions.md 是**每个实现单元的第一必读文件**,决定并行子代理写出的代码是否互相兼容。用下面骨架起步,`<>` 处按项目填;生成后随波次把新学到的坑追加进 §J。
|
|
6
|
+
|
|
7
|
+
```markdown
|
|
8
|
+
# conventions.md — <目标工程> 实现约定(每个实现单元必读)
|
|
9
|
+
|
|
10
|
+
> 源码绝对真源:<$SRC>
|
|
11
|
+
> 目标:<$DST>(<模块形态、API 版本>)
|
|
12
|
+
> 工单卡片已内嵌本约定与你单元的 SECTION——按卡执行,另需背景再查 spec/ 下探索产物。
|
|
13
|
+
> 只允许读写 <$SRC>、<$DST> 与 .deveco/ 工作目录,禁止网络访问。
|
|
14
|
+
|
|
15
|
+
## A. ArkTS 硬性规则(违反=编译失败;写每个文件前过一遍)
|
|
16
|
+
|
|
17
|
+
<用 skill 工具加载 arkts-grammar-standards,只提炼最常踩的那批(以常踩为准,不是配额),逐条写成"禁…→改用…"。
|
|
18
|
+
必含:禁 any/unknown;禁解构;对象字面量处处要类型(嵌套不继承外层);**禁计算属性名**
|
|
19
|
+
(`{[key]: v}` 一律改显式 key 或 Map——ValuesBucket 组装是最高频踩点);类字面量列全字段或 static of();
|
|
20
|
+
禁 index signature(Record 用引号 key);async 显式 Promise<T>;禁函数表达式/嵌套函数声明;
|
|
21
|
+
null 安全窄化;禁 delete/in/for-in/对象 spread/正则字面量/@ts-ignore;`as T` 类型断言合法
|
|
22
|
+
(只有 `as const` 被禁,不要把 `as` 一刀切改写);UI 用 V2 装饰器一族
|
|
23
|
+
(@ComponentV2/@Local/@Param/@Monitor,禁混 V1);@Builder 内只能 UI 语法。
|
|
24
|
+
弹窗一律 this.getUIContext().getPromptAction() 的 openCustomDialog/showDialog 或 bindSheet,
|
|
25
|
+
**禁 @CustomDialog + CustomDialogController**(V1 机制,@ComponentV2 宿主内 controller 不绑定,
|
|
26
|
+
运行时 close() 必崩——编译与静态检查均不报)。>
|
|
27
|
+
|
|
28
|
+
<再补两张表:本项目会用到的 Kit 导入表(模块名→@kit.*);常用组件真实签名与常用枚举成员
|
|
29
|
+
(从 explore 结果和 arkts-grammar-standards 里抄实际用得到的,不要全抄)。>
|
|
30
|
+
|
|
31
|
+
**§A 增补块**(逐条照录进生成的 §A):
|
|
32
|
+
· **import 纪律**:ArkUI 组件/容器/枚举/装饰器/组件控制器(Navigation、NavPathStack、LaunchMode、
|
|
33
|
+
@Local、Scroller、TabsController…)一律 ambient 裸用**禁 import**(报 has no exported member);
|
|
34
|
+
用作**值**的真实导出必须逐文件 import——AppStorageV2/PersistenceV2/ComponentContent/LengthMetrics/
|
|
35
|
+
curves/window(漏则 Cannot find name)。V2 存储只有 AppStorageV2/PersistenceV2,
|
|
36
|
+
**不存在 LocalStorageV2**;子树共享用 @Provider/@Consumer。
|
|
37
|
+
· **V2 细则**:@Param 必须默认值、@Event 给默认空实现;@Provider()/@Consumer() 必须带括号且
|
|
38
|
+
@Consumer 给默认值;@ReusableV2 的 aboutToReuse() 无入参、禁给 @Param 赋值;
|
|
39
|
+
keyframeAnimateTo 是 UIContext 成员(无全局函数)[待查证]。
|
|
40
|
+
· **字段撞名**:组件 struct 的 @Local/@Param/@Event/getter 名禁撞 CustomComponent 内置成员
|
|
41
|
+
(id/title/content/scale/rotate/translate/opacity/position/visibility/width/height/enabled/zIndex/
|
|
42
|
+
onClick/borderRadius/aspectRatio/dragPreview…)→ 加域前缀(itemId/coverVisible);纯数据类不受限。
|
|
43
|
+
· **路由传参三步式**:param 声明为**类**、`new` 后逐字段赋值传实例(禁 `{...} as Record`、
|
|
44
|
+
禁 `{} as object`;空参用 `class NoParam {}`;单基元值可直传);取参 `ctx.pathInfo.param as Object`
|
|
45
|
+
widening 后 `instanceof` 窄化(裸 unknown 或注解赋值均编译失败);`getIndexByName` 返回
|
|
46
|
+
**`number[]`**(判存在用 `.length>0`,禁与数字比较)。
|
|
47
|
+
· **throw/catch**:throw 只接 Error 及子类(自定义错误 extends Error + super(message));
|
|
48
|
+
catch 形参**不标类型**;error 下传用 Object 再 instanceof/as 收窄。
|
|
49
|
+
· **受限 stdlib**:禁 Object.assign 造实例、禁 `{} as T`("空/缺省"用 `T | null`);无全局
|
|
50
|
+
TextEncoder/TextDecoder/URL(用 @kit.ArkTS 的 util/url 模块)[待查证];margin/padding 简写无
|
|
51
|
+
vertical/horizontal 键(展开为 top/bottom、left/right);List/Grid 直接子组件只能
|
|
52
|
+
ListItem/GridItem——分隔线用 List 的 `.divider()` 属性,禁平铺 Divider。
|
|
53
|
+
· **尾随闭包**:自定义组件 `Comp(){...}` 后禁链通用属性——外层套内置容器、属性加容器上。
|
|
54
|
+
|
|
55
|
+
## A2. 运行期硬规则(能编译但用不了;逐条按本工程 SDK 查证后保留,再增补工程特有项)
|
|
56
|
+
|
|
57
|
+
0. **自定义构建参数必须 @Builder**:组件参数传内容时,禁把普通 lambda 当 builder——
|
|
58
|
+
`xxx: () => { SomeComp({...}) }` 能编译但运行必崩(`TypeError: class constructor cannot
|
|
59
|
+
called without 'new'`——组件构造只合法于 build()/@Builder 语境)。正确形态:组件内定义
|
|
60
|
+
`@Builder xxxBuilder() { SomeComp({...}) }`,接收侧声明 `@BuilderParam`。传法(官方 @BuilderParam 指南与 FAQ):
|
|
61
|
+
**首选 ② 箭头包裹** `xxx: (): void => { this.xxxBuilder() }`——this 指向宿主,父组件状态/方法可用;
|
|
62
|
+
① 裸引用 `xxx: this.xxxBuilder` 时 builder 体内 this 指向**接收方**子组件,体内一用 this.成员/方法即 TypeError(`xxx is not callable`),
|
|
63
|
+
只有体内零 this 依赖才可用;③ `.bind(this)` 可用但触 ArkTS 约束 arkts-no-func-apply-bind-call(lint 告警),不推荐。
|
|
64
|
+
闭合门:① 且体内有 this = 硬违规(改 ②)。需要按条件选 builder 的,把条件逻辑写进 @Builder 方法体内。
|
|
65
|
+
1. 弹窗:页面侧 `@Builder` 包裹内容组件,经 `ComponentContent` + UIContext 打开/关闭/更新;
|
|
66
|
+
内容变化必须调 `ComponentContent.update()`。不自创弹窗封装层;确需封装先在单页真机验证再推广。
|
|
67
|
+
```ts
|
|
68
|
+
import { ComponentContent } from '@kit.ArkUI';
|
|
69
|
+
private dlg?: ComponentContent<Params>;
|
|
70
|
+
show(p: Params) { const ui = this.getUIContext();
|
|
71
|
+
this.dlg = new ComponentContent(ui, wrapBuilder(buildXxxDialog), p);
|
|
72
|
+
ui.getPromptAction().openCustomDialog(this.dlg); }
|
|
73
|
+
close() { if (this.dlg) { this.getUIContext().getPromptAction().closeCustomDialog(this.dlg); } }
|
|
74
|
+
```
|
|
75
|
+
2. 浮层/半模态一律官方通道(`bindSheet`/`bindContentCover`/`openCustomDialog`);
|
|
76
|
+
禁在既有节点上链式挂载自绘遮罩。
|
|
77
|
+
3. `build()`/`@Builder` 内禁 `throw` 与副作用;取不到的值给显式兜底渲染。
|
|
78
|
+
4. 首屏异步数据:`aboutToAppear` 异步返回后必须写入 `@Local`/`@Trace` 状态驱动渲染;
|
|
79
|
+
`build()` 内禁读非状态成员充当首帧内容。
|
|
80
|
+
5. 定时器/事件订阅在 `aboutToDisappear` 对称清理;页面退出后的回调禁触碰状态。
|
|
81
|
+
6. (仅 V1 工程)`@Observed`+`@Track` 类禁向 UI 暴露 getter(首帧崩);派生值用普通方法或
|
|
82
|
+
提升为 `@Track` 字段。
|
|
83
|
+
7. 弹窗默认全局层级,路由跳转后仍盖在新页上(Android 的页面级 Dialog 语义不成立):
|
|
84
|
+
弹窗内要 push 新页的,`openCustomDialog` 配 `levelMode: LevelMode.EMBEDDED`(API 15+),
|
|
85
|
+
低版本用透明页 `NavDestinationMode.DIALOG`;用完即关的普通确认框不需要。
|
|
86
|
+
8. 层叠/浮层两条独立决策:**几何 = 源布局里它覆盖的区域**(该多大就多大),
|
|
87
|
+
**挡不挡触摸 = hitTestBehavior**(纯展示层 `HitTestMode.Transparent`,蒙层默认 Block)。
|
|
88
|
+
禁止为"别挡触摸"缩小盒子、为"贴边"铺满全屏——盒子过大缺 Transparent = 底层点不动,
|
|
89
|
+
过小 = 盖不住、两态混叠。
|
|
90
|
+
9. 子节点 `width/height('100%')` 解析到**最近有确定尺寸的祖先**,父级 wrap-content 时直接撑满
|
|
91
|
+
可用空间(列表行被撑成整屏、chip 变大色块)。背景/描边画在容器自身
|
|
92
|
+
(`.backgroundColor()/.border()/.linearGradient()`),不用 100% 子层填父。
|
|
93
|
+
10. 滚动容器(List/Scroll/Grid)**不随内容定尺寸**:横向必须显式 `.height(最高子项+边距)`,
|
|
94
|
+
纵向同理显式宽;Scroll 嵌 List 不给内层定高 = 子项全量创建、LazyForEach 懒加载失效。
|
|
95
|
+
11. 同向嵌套滚动默认不联动(内层滑不动、手势被外层抢):内层可滚组件显式
|
|
96
|
+
`.nestedScroll(SELF_FIRST)` 并有界外尺寸;**不同向**嵌套不用配;下拉刷新头这类外层先滚
|
|
97
|
+
的用 `PARENT_FIRST`。点击被抢是另一回事:子元素 `parallelGesture`/`priorityGesture` 处理。
|
|
98
|
+
12. `@Builder` 值参按调用时刻快照传递:**动态**值(会随 `@Local`/`@Trace` 变且要刷 UI 的)
|
|
99
|
+
禁走值参——builder 内直读 `this.` 状态;静态标签/常量走值参完全正常,不要为此拆组件。
|
|
100
|
+
13. `@ObservedV2`/`@Trace`/`@Observed` 实例禁直接 `JSON.stringify`(访问器字段枚举不到,
|
|
101
|
+
产出空 JSON 或 key 全变 `__ob_` 前缀、后端不识别):模型写配对的 `toJson()/fromJson()`,
|
|
102
|
+
跨页传参/持久化/跨线程一律走它们。
|
|
103
|
+
14. ForEach/LazyForEach 的 key 必须含**所有影响显示的可变字段**(`id + '_' + count`);
|
|
104
|
+
或保持对象引用稳定、用 `@ObservedV2`+`@Trace` 原地改字段——"同 id 新对象"会被判同项、
|
|
105
|
+
旧子组件复用、`@Param` 不重派,数值停在旧值。
|
|
106
|
+
15. 布局两则免踩空修:Tabs 底栏不可见的根因是**没拿到确定高度**(安全形态
|
|
107
|
+
`Column(){ Tabs(...).layoutWeight(1) }`),不存在"必须放 Column"的容器约束;
|
|
108
|
+
Text **默认自动折行**,不折行的真因是横向无界父级(先给宽度约束),
|
|
109
|
+
别一见不折行就加 `.maxLines(1)` 反把多行内容截断。
|
|
110
|
+
16. V1/V2 状态体系禁混用:全工程统一 V2(`@ComponentV2` + `@Local/@Param/@Event/
|
|
111
|
+
@Provider/@Consumer/@ObservedV2/@Trace`),V1 装饰器(`@State/@Link/@Prop/@Provide/
|
|
112
|
+
@Observed`)一律不出现;混用是崩溃与不刷新的高发源。
|
|
113
|
+
〔来源:官方 hmos-arkui-statemgt-migration;按本工程 SDK 首用查证〕
|
|
114
|
+
17. `@Local` 禁止外部初始化:父传子初值用 `@Param`;"父给初值、子可自改"用
|
|
115
|
+
`@Param @Once`,子改需回传父用 `@Event` 回调,不许直接改 `@Param`。
|
|
116
|
+
〔来源:官方 statemgt SKILL 装饰器对照表;首用查证〕
|
|
117
|
+
18. `@ObservedV2`/`@Trace` 实例**禁存 V1 存储**(AppStorage/LocalStorage/PersistentStorage,V1/V2 混用即崩或不刷);
|
|
118
|
+
V2 存储 `AppStorageV2`/`PersistenceV2` **支持** @ObservedV2 对象——官方 PersistenceV2 指南明写"关联对象的 @Trace 属性变化
|
|
119
|
+
触发整个对象自动持久化",限制是:持久化的类属性必须有初值、不存 PixelMap/Native 类型、API 23 前不存容器/@Sendable
|
|
120
|
+
(23 起 `globalConnect` 支持)。跨页共享也可导出 `@ObservedV2` 单例模块。
|
|
121
|
+
〔官方指南 arkts-new-persistencev2 可证;早期"实测崩溃"的现场是 V2 对象进了 V1 AppStorage,不是 V2 存储本身〕
|
|
122
|
+
19. `@Monitor` 必须列出**全部**驱动重绘/联动的字段,漏列字段的变化不触发回调;
|
|
123
|
+
自绘组件的动画相位类字段尤其易漏。
|
|
124
|
+
〔已验——实测应用自绘环暂停冻结即只挂 progress 漏相位字段〕
|
|
125
|
+
20. 应用退后台后 `setTimeout/setInterval` 被系统减速(实测 ~4×:45s 轮询窗拖到 180s):
|
|
126
|
+
拉起外部 App 等回跳(支付/签约/授权)的时限逻辑禁纯定时器驱动,必须在
|
|
127
|
+
`onForeground`/页面 onPageShow 回调里立即补查并续跑。
|
|
128
|
+
〔生产实测;系统策略随版本可变,首用复核〕
|
|
129
|
+
21. `Image(url)` 的 `onComplete` 在图源已在缓存时会在**节点创建当下同步回调**(loadingStatus 0=数据就绪、1=解码完成,SDK 明写两个状态值,两个阶段各回调一次),
|
|
130
|
+
列表视口外的缓存项(`cachedCount`,默认=可见项数)在空闲帧预构建时创建节点,此时同步写 `@Local/@Trace` 状态会让状态更新
|
|
131
|
+
重入正在构建的组件,渲染层 `RenderCustomChild` 遍历到已释放子节点 → cppcrash(SIGSEGV 地址随机)。规则:Image 回调里
|
|
132
|
+
**不同步写状态**——加载占位用 `.alt()`,确需状态只认 `loadingStatus === 1`(解码完成,异步)或把写推到 `setTimeout(…, 0)`;
|
|
133
|
+
同一 URL 在页面内出现两次以上(横滑区与网格共用商品图)就会触发,与图片格式无关。
|
|
134
|
+
〔已验:coolmall 模拟器四变量二分——关 cachedCount / 去掉状态写 / 去掉共用图三者各自绿 2/2,基线崩 2/2;
|
|
135
|
+
只认 status 1 与 setTimeout 0 两种修法各绿 2/2;改成 http+PixelMap 也绿但丢缓存降采样且泄漏,不是修法〕
|
|
136
|
+
|
|
137
|
+
22. 封装组件(AppText/Icon/Label 这类)**禁止无条件绑定 `.onClick` 再在回调里判 flag**:绑定即挂点击识别器,手势裁决
|
|
138
|
+
子先于父,父级行/卡片/芯片的 `onClick` 对该区域永不触发,回调里的 `if (this.clickable)` 挡不住吞点击;控件树 dump 把该
|
|
139
|
+
Text 标成可点击也是假象。可点击性必须是结构性的:不可点击时不绑(`if` 分支或 AttributeModifier 按条件挂),或
|
|
140
|
+
`.hitTestBehavior(this.clickable ? HitTestMode.Default : HitTestMode.None)`(None=自身不参与命中测试、子节点仍参与,
|
|
141
|
+
enums.d.ts 明写)。〔已验:coolmall 全应用"滚动容器内点击死"根因即 AppText 无条件 onClick,hilog 手势裁决日志
|
|
142
|
+
`CLK RACC, T: Text` 后无导航;加 hitTestBehavior 一行后列表项/搜索芯片/我的页登录行全部恢复〕
|
|
143
|
+
|
|
144
|
+
## A3. 待验条目(**不注入实现 prompt**;涉及场景时先按本工程 SDK 查证,真机验证后升 A2 并标"验证于")
|
|
145
|
+
|
|
146
|
+
· multipart 上传的文件项 `filePath` 只认应用沙箱路径:选择器/外部 uri 先 `fileIo.copyFile`
|
|
147
|
+
拷入 cacheDir/filesDir 再传,文本项走 `data`。〔未验〕
|
|
148
|
+
· 动画三则:`.transition()` 出现/消失须由 `animateTo` 包裹的状态变化触发;循环动画初值须≠目标值;
|
|
149
|
+
手势跟随 `onActionUpdate` 直赋值、仅 `onActionEnd` 用 `animateTo` 收尾。〔未验〕
|
|
150
|
+
· 底部导航/顶层入口在子页也可见时,点击必须切到目标页(`replace`/`popTo` 到该 tab),禁用"当前 tab 已激活则不动作"短路——
|
|
151
|
+
子页继承父 tab 的激活态,从子页返回后点导航会无反应。〔一例实证:评测 100 场景 16 条因此失败;第二例后升 §E〕
|
|
152
|
+
· 滚动联动的折叠头(M3 `enterAlwaysScrollBehavior`/`exitUntilCollapsed`、`AppBarLayout` scroll flags)用列表自身的滚动回调驱动:
|
|
153
|
+
`List/WaterFlow/Grid.onDidScroll((offset, state) => …)` 按增量收放头高(API 12),要先吃掉位移再滚列表用 `onScrollFrameBegin`;
|
|
154
|
+
禁止给首项包 `if (index === 0)` 分支 + `onAreaChange` 当滚动传感器——首项被复用/重建即失灵,分支类型随索引翻转会整棵拆装,
|
|
155
|
+
每帧回调写状态还会触发布局回环。〔SDK d.ts 可证 API 存在;实证一轮该写法被当成崩溃嫌疑整段删除、折叠头退化为静态,未验〕
|
|
156
|
+
|
|
157
|
+
## B. 项目结构与命名
|
|
158
|
+
- 目录结构见 design.md;文件名 = 主类名.ets(PascalCase);源类→目标类同名。
|
|
159
|
+
- 单文件过长按源码的拆分习惯拆(给个默认上限写进本节,如几百行,按项目调)。
|
|
160
|
+
- 常量:static readonly 或 export enum。
|
|
161
|
+
|
|
162
|
+
## C. 数据层约定(有 DB/偏好才写)
|
|
163
|
+
- model 字段名/类型/语义与源一致;类型映射 long→number、Date→number(epoch ms)、可空引用 T | undefined。
|
|
164
|
+
- **出站 JSON 的空值照源序列化器的默认行为,不写 `undefined ? null`**:Gson/Moshi 默认 `serializeNulls` 关(null 字段整个省略);
|
|
165
|
+
kotlinx.serialization 默认 `encodeDefaults=false`——**等于默认值的字段省略**(含默认为 null 的可空字段、默认 "" 的字符串),
|
|
166
|
+
值为 null 且**不等于默认值**时按 `explicitNulls`(默认 true)写 `null`。`toJson()` 照此写:等于模型默认值/undefined 的键
|
|
167
|
+
**不放进对象**,只有"源会写 null"的情形才写 `null`;源显式开了 `encodeDefaults=true`/`serializeNulls()` 才全量写。
|
|
168
|
+
〔已核 kotlinx-serialization-json 1.10.0 源码 JsonConfiguration(encodeDefaults=false、explicitNulls=true)与 Json.kt 注释;
|
|
169
|
+
实证一轮 33 个模型 153 处写成 `x === undefined ? null : x`,与源的省略语义不同〕
|
|
170
|
+
- 表/列名、preferences 文件名与 key **逐字照搬**(大小写敏感)。
|
|
171
|
+
- 写库统一走单例 + 串行队列防并发写;写完发对应事件(发布点照源)。
|
|
172
|
+
- Preferences:`put` 后必须 `await flush()`(不 flush 进程被杀即丢);BasicDataSource 更新用
|
|
173
|
+
notifyDataAdd/Change/Delete 增量通知,禁整体 reload。
|
|
174
|
+
|
|
175
|
+
## D. 事件约定(有事件总线才写)
|
|
176
|
+
- <事件 API 形状、回调线程约定、订阅/反订阅位置、粘性语义>。
|
|
177
|
+
|
|
178
|
+
## E. UI 约定
|
|
179
|
+
- 页面 = @ComponentV2 struct + 文件尾部路由自注册;文件放哪按 design.md 的目录结构。
|
|
180
|
+
- 封装组件的用户可见文本参数一律 `ResourceStr`(`string | Resource`),禁只收 `string`——否则调用方只能塞中文常量,
|
|
181
|
+
已转出的 `$r('app.string.x')` 进不去,多语言在这些位点静默失效(实证一轮 AppScaffold/AppListItem 标题只收 string,
|
|
182
|
+
多个页面标题写死中文并各登记一条降级)。
|
|
183
|
+
- 页面必填参数拿不到时**不得抛错**(`throw new Error('No … ID')` 类直接杀进程),也**不要自动 `pop()` 返回**:
|
|
184
|
+
留在本页显示空态(提示 + 返回按钮)。深链、通知点入、系统恢复、波级冒烟直达都会无参进入页面,
|
|
185
|
+
无参必须是可到达、可观测的稳定态(自动返回会让冒烟判"着陆错页"、深链落不到页)。
|
|
186
|
+
- 复用 item 组件独立成文件放 ui/common/,**唯一实现**,其他单元只 import。
|
|
187
|
+
- 新增与编辑复用同一表单页时,保存后的去向与复位范围**照源码**:源保存后停留并回到新增态(清表单、编辑 id 置空)就必须清空,
|
|
188
|
+
否则第二次保存走编辑路径覆盖上一条(实证:连续新建两条只剩一条);源保存后 pop 就 pop。复位类操作(删除/清空)只复位源码复位的字段,不多不少。
|
|
189
|
+
- 布局还原以 explore 文档的 XML 层级为规格:尺寸字号颜色 1:1(dp→vp)。
|
|
190
|
+
- UI 单元必读 `spec/ui-layout-semantics.md`(两端布局默认值相反:Column/Row/Stack 默认居中、
|
|
191
|
+
Image 默认裁剪,源没写 gravity/scaleType 目标也必须显式写),写完过其 §5 自查门;非 UI 单元忽略本条。
|
|
192
|
+
- 图标 $r('app.media.ic_*');缺图标记录到 $SPEC/front/missing-icons.txt 并用相近替代,不许编造资源名。
|
|
193
|
+
- 颜色走主题 token(随深浅色);固定色用 $r('app.color.*')。
|
|
194
|
+
- 全屏沉浸式按**分层契约**做,层与层不越界(细节按 hmos-multidevice-avoid-areas 技能);
|
|
195
|
+
弹窗/半模态不做:
|
|
196
|
+
① 窗口层(EntryAbility.onWindowStageCreate):`setWindowLayoutFullScreen(true)` +
|
|
197
|
+
`getWindowAvoidArea`(SYSTEM 与 NAVIGATION_INDICATOR)存入全局状态,并订阅
|
|
198
|
+
`on('avoidAreaChange')` 刷新(折叠屏/转屏后避让值会变;`windowSizeChange` 只报窗口尺寸不报避让区);
|
|
199
|
+
② 容器层:`expandSafeArea` 只加在**根 Navigation/根容器一处**;
|
|
200
|
+
③ 页面层:各页从全局状态读避让值手动 padding,**禁止再自行 expandSafeArea**——
|
|
201
|
+
重复声明与只 expand 不避让是两类最常见的重叠/留黑根因;
|
|
202
|
+
子组件/浮层一律不碰安全区(宿主负责)。集成闭合的 safearea 静态门逐页核查(closure-suite)。
|
|
203
|
+
- 系统符号 `$r('sys.symbol.*')` 名称必须经 SDK 符号表/官方文档核实后用(高频幻觉名:
|
|
204
|
+
music_note/copy/doc_on_doc/square_and_arrow_up 均不存在);无对应符号改用工程 media 资源,
|
|
205
|
+
不许猜名。[待查证——符号集随 SDK 版本变化]
|
|
206
|
+
- (支持深浅色的工程)状态栏/导航栏前景色**不随主题自动切换**:主题变化回调里显式调
|
|
207
|
+
`window.setWindowSystemBarProperties`;Canvas/Web/XComponent 自绘内容订阅
|
|
208
|
+
`mediaquery.matchMediaSync('(dark-mode: true)')` 触发重绘。细则见 ui-alignment/references/dark-mode.md。
|
|
209
|
+
|
|
210
|
+
- M3 视觉近似**批次级登记**:elevation/tonal/形态学动画类无 API 等价的,用一条批次 degradation
|
|
211
|
+
("M3 elevation/形态动画→surfaceContainer 色阶/静态近似")统一豁免,禁逐组件立案碎化台账。
|
|
212
|
+
- SymbolGlyph 符号名禁凭记忆写:只用 ui-alignment/README 已验证名单内的名(生成本节时把名单段抄入);
|
|
213
|
+
名单外的先 grep SDK sys.symbol 声明证实存在再用——符号名幻觉是高频编译失败源。
|
|
214
|
+
|
|
215
|
+
## F. 网络/下载约定(有网络才写)
|
|
216
|
+
- <统一 HttpClient 的 UA/超时/认证语义;下载走 request.agent 还是 http;解析器选型>。
|
|
217
|
+
|
|
218
|
+
## G. <按应用类型追加的领域约定:播放/地图/IM…>
|
|
219
|
+
|
|
220
|
+
## H. 验证义务(每单元完成前)
|
|
221
|
+
1. 写完每个 .ets 自查 §A 全条目。
|
|
222
|
+
2. 不跑整工程构建(gate 统一跑);可用 ls/grep 自查 import/export 配对。
|
|
223
|
+
3. 单元 summary 必须列:产出文件清单 + 每文件导出符号(供下游 import 核对)+ 未实现/降级点。
|
|
224
|
+
4. 事件回调禁空注释体(`onClick(() => { /* 待接 */ })` 不可 grep、必成漏网缺陷):能接真实
|
|
225
|
+
Controller/事件就接;接不了写可扫描占位 `console.info('TODO:<动作>:<组件>')` 并列入
|
|
226
|
+
单元 summary 的未实现清单。
|
|
227
|
+
|
|
228
|
+
## I. 共享文件与独占权(避免并发冲突)
|
|
229
|
+
- <壳文件(Index.ets 等)>:仅 <单元 id> 可编辑。
|
|
230
|
+
- <module.json5 / main_pages.json>:仅 <集成单元> 可编辑。
|
|
231
|
+
- <路由/事件总线/主题等公共模块>:各自单元独占创建,其余只 import。
|
|
232
|
+
- 新增公共组件放自己单元目录内,避免冲突。
|
|
233
|
+
|
|
234
|
+
## J. 运行中追加的约定(每波结束把 debt/envNotes 固化到这里,后续单元必须遵守)
|
|
235
|
+
<空,随波次追加。写清楚:"<结论>,由 <单元> 在 Run<N> 确认">
|
|
236
|
+
<写入门槛:写法/idiom 类条目须附 SKILL 4b 第 5 条的 ①复现 ≥2 + ②逐条件反转各 ≥2;要下"平台级缺陷、禁用 X"结论的还须
|
|
237
|
+
③空工程隔离复现。证据不齐只能写成"实测缓解(机制未证):<改动>,仅涉事片",不得据此降级契约、改写全局组件或进语料回流>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
要点提醒:
|
|
241
|
+
- §A 不要整段复制 arkts-grammar-standards——提炼常踩项即可,全文让需要深挖的单元自己加载技能。
|
|
242
|
+
- §I 的独占清单必须与 implement 调用的 `sharedFiles` 一致。
|
|
243
|
+
- §J 是并行波次之间传递经验的唯一通道,波结束不写 = 下一波重复踩坑。
|
|
244
|
+
- 反向依赖用注入回调解耦(例:底层 Writer 需要触发上层逻辑时,提供 `setXxxHook(cb)` 由集成单元注册),把这类钩子约定写进 §C/§G。
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# 导航迁移:形态画像 → 路由真源三轨 → 目标注册形态
|
|
2
|
+
|
|
3
|
+
> 供 wire-routes/规划/裁决引用。画像来自 `spec/feasibility.json` 的 `profile` 字段(体检产出)。
|
|
4
|
+
|
|
5
|
+
## 一、路由真源按应用形态分轨(wire-routes --src 自动全跑合并)
|
|
6
|
+
|
|
7
|
+
| 安卓形态 | 路由真源 | 提取方式 | 备注 |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| 多 Activity(经典/XML/MVP) | AndroidManifest `<activity>` | manifest 轨 | launcher 按 MAIN+LAUNCHER 且非 watch/TV 语义选 |
|
|
10
|
+
| Jetpack Navigation(Fragment/XML) | `res/navigation/*.xml` | navGraph 轨 | 配置化最可靠;startDestination=首页 |
|
|
11
|
+
| 单 Activity + Compose | `NavHost { composable(...) }` 代码 | Compose 轨(工程级两遍:先收 `const val` 常量表再解引用) | manifest 只见得到壳——壳自动让位为 Index 容器 |
|
|
12
|
+
| 单 Activity + Navigation 3 | `entry<XxxNavKey> { }`(EntryProviderScope) | Compose 轨同解析(typed 形态,NavKey/Route 后缀剥离成页名) | Google 官方样板现行形态 |
|
|
13
|
+
| Compose + 第三方导航(Voyager/Decompose 等) | Screen/Component 类栈式,无静态注册 | **无轨**——2b 页面账 + 2c `--from-ledger` 兜底 | 画像信号:compose 但 compose 轨 0 路由 |
|
|
14
|
+
| Fragment 手动事务/自研跳转 | 无静态真源 | **台账兜底**:2b 页面账定型后 `--from-ledger` 补漏 | 散代码不穷举,页面账是分母 |
|
|
15
|
+
|
|
16
|
+
## 二、目标侧注册形态(固定,禁改)
|
|
17
|
+
|
|
18
|
+
- **单一注册**:`resources/base/profile/route_map.json`(官方 systemRouterMap)是**唯一真源**——首跑全集、
|
|
19
|
+
`--from-ledger` 二跑新页只经此注册即可被 `pushPathByName` 加载(模拟器实证:无 navDestination 的 Navigation 壳
|
|
20
|
+
直达 route_map-only 页全部着陆)。Index 不再有 pageMap 比较分支:两份真源要模型同步维护,实测模型重写 Index 后
|
|
21
|
+
pageMap 丢失/只剩首页;官方文档只说"支持混用",未写混用时的查找顺序。hvigor ProcessRouterMap
|
|
22
|
+
(pageSourceFile/buildFunction 缺失即构建失败)成为免费的注册门。
|
|
23
|
+
- **注册页文件 = 交付文件**:`pageSourceFile` 指向的 `pages/<Route>Page.ets` 就是该页的实现落点,切片 targetPath 必须含它
|
|
24
|
+
(模板启动按 route_map 校验归属);不另起壳外实现目录再"挂载"——实证壳从未挂载、三道防线全漏。
|
|
25
|
+
- Index 壳是冻结件:冷启入栈 launcher、`smokeRoute` 直达钩子(`AppRouter.pushName`)、`hmigSmoke` 着陆日志
|
|
26
|
+
(`NavPathStack.setInterception.didShow` 打 hilog,波级冒烟据此核对着陆页名);EntryAbility 的 `smokeRoute` 透传由
|
|
27
|
+
wire-routes 幂等补丁写入,工程怎么建都一样。
|
|
28
|
+
- 跳转唯一通道 `AppRouter`(Routes 常量),页面禁散落 pushPathByName。
|
|
29
|
+
- **铁律:禁自研动态注册表**(Map.set/数组 push 运行时注册)——静态检查器查不了、注册依赖 import
|
|
30
|
+
时序(页面未被引用即未注册→跳转白屏,真机实证且症状隐晦难 debug)。并行切片的"都要加注册"矛盾
|
|
31
|
+
已由"前场全预制 + 台账补漏重跑"解决,没有任何理由手改注册。
|
|
32
|
+
- `nav_registry_unparseable` 立案的裁决门槛:改回标准注册,或附逐目的地真机跳转证据;静态核对不算数。
|
|
33
|
+
|
|
34
|
+
## 三、画像分派(archHints/uiParadigm → 语料与关注点)
|
|
35
|
+
|
|
36
|
+
| 画像 | 分派 |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `uiParadigm: compose` | 布局走 fact-tree 旁路(不经 XML 转译器);UI 语料 `ui-alignment/` 按需 |
|
|
39
|
+
| `uiParadigm: xml` | 布局走 transpile-layout 转译器 + `ui-layout-semantics.md` §5 自查门 |
|
|
40
|
+
| `archHints: mvp` | Presenter 契约=行为断言主源(View 接口即页面卡断言骨架;P 层逻辑归 backend 片) |
|
|
41
|
+
| `archHints: mvvm` | 状态语义照 conventions §A2 16-19(StateFlow/LiveData→@Local/@Param 映射) |
|
|
42
|
+
| `fragments > 0` 且无 navgraph | Fragment 事务跳转按页面账兜底,跳转语义逐条入断言(无静态账可对) |
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# 平台 API 防护与权限映射(崩溃防护 + 权限完整性)
|
|
2
|
+
|
|
3
|
+
安卓很多"尽力而为"能力(触感/后台/传感器)在设备不支持时**静默 no-op**;鸿蒙对应 API 常**同步抛异常或异步拒绝**。
|
|
4
|
+
迁移若照搬调用而不防护,未捕获异常直接**杀进程**(JS_ERROR),且缺权限时真机能力静默失效。两条铁律:
|
|
5
|
+
|
|
6
|
+
1. **会抛的调用必须 try/catch**,失败降级为 no-op(对齐安卓静默行为)。注意区分:
|
|
7
|
+
- **同步会抛**(`isSupportEffectSync`、`getXxxSync`、构造器)→ 必须包 try/catch,`.catch()` 接不住同步抛。
|
|
8
|
+
- **异步返回 Promise** → `.catch()` 或 `await`+try/catch。
|
|
9
|
+
2. **用能力必声明权限**:module.json5 `requestPermissions` 缺失 → 真机失效。`check-permissions.mjs` 已确定性拦截。
|
|
10
|
+
|
|
11
|
+
## 常见"会抛"API × 防护 × 权限
|
|
12
|
+
|
|
13
|
+
| 能力 | 鸿蒙 API | 会抛点 | 防护 | 所需权限 |
|
|
14
|
+
|---|---|---|---|---|
|
|
15
|
+
| 振动/触感 | `vibrator.startVibration` / `isSupportEffectSync` | 无振动器(模拟器)同步抛 "Device operation failed" | 整体 try/catch → no-op | `ohos.permission.VIBRATE` |
|
|
16
|
+
| 长时后台 | `backgroundTaskManager.startBackgroundRunning` | 无权限/超额 reject | `.catch` + 台账登记降级 | `ohos.permission.KEEP_BACKGROUND_RUNNING` |
|
|
17
|
+
| 提醒/闹钟 | `reminderAgentManager.publishReminder` | 系统限制 reject | `.catch` | `ohos.permission.PUBLISH_AGENT_REMINDER` |
|
|
18
|
+
| 定位 | `geoLocationManager.getCurrentLocation` | 无权限/无 GPS reject | `.catch` + 降级 | `ohos.permission.APPROXIMATELY_LOCATION` |
|
|
19
|
+
| 相机 | `camera.getCameraManager` | 无设备/无权限抛 | try/catch | `ohos.permission.CAMERA` |
|
|
20
|
+
| 录音 | `createAudioCapturer` | 无权限抛 | try/catch | `ohos.permission.MICROPHONE` |
|
|
21
|
+
| 数据库事务 | `rdbStore.beginTransaction` | 库/表被锁同步抛 14800024 / 14800025(801 是"能力不支持",不是锁错误) | try/catch + 回退非事务 | — |
|
|
22
|
+
| 文件同步读写 | `fileIo.openSync/statSync/mkdirSync/readSync/writeSync/listFileSync…` | 路径不存在/无权限/只读同步抛 13900002/13900012 等 | try/catch → 转成明确错误出口(onFail/错误对话框),不能裸调 | — |
|
|
23
|
+
| 网络 | `http.createHttp().request` | 无网络 reject | `.catch` | `ohos.permission.INTERNET` |
|
|
24
|
+
| 通讯录 | `contact.queryContacts / addContact…` | 受限/ACL 抛 | 三方应用改 `contact.selectContacts`(系统选人,免权限);直查只在有 ACL 时 | `READ_CONTACTS` / `WRITE_CONTACTS`(**受限开放**,须 ACL) |
|
|
25
|
+
| 媒体库 | `getAssets` / `createAsset` / `SaveButton` 外的 `applyChanges` | 受限/ACL 抛 | 选图走 `PhotoViewPicker`,存图走 `SaveButton.onClick` 内 `applyChanges` 或 `showAssetsCreationDialog`(均免权限) | `READ_IMAGEVIDEO` / `WRITE_IMAGEVIDEO`(受限开放,须 ACL) |
|
|
26
|
+
|
|
27
|
+
## 防护范式(同步会抛的触感为例)
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
heavyClick(): void {
|
|
31
|
+
try { // 整体包裹——isSupportEffectSync 同步抛,.catch 接不住
|
|
32
|
+
if (vibrator.isSupportEffectSync(id)) {
|
|
33
|
+
vibrator.startVibration(preset, attr).catch(() => {}) // 异步再 .catch
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
vibrator.startVibration(fallback, attr).catch(() => {})
|
|
37
|
+
} catch (e) { /* 设备不支持——静默忽略,绝不外抛 */ }
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 哪种栈上抛才杀进程(门禁分档依据,模拟器实测)
|
|
42
|
+
|
|
43
|
+
- **同步栈**(事件回调、定时器、生命周期、UI 处理器、`on(...)` 回调)上的未捕获异常 → 进程被杀,进 faultlog。
|
|
44
|
+
- **async 函数体**内的同步抛、`.then/.catch/.finally` 回调内的抛、无人接的 `Promise.reject` → 变成未处理拒绝:**不杀进程,也不进 faultlog**,但操作静默失败、无任何错误出口。
|
|
45
|
+
- 门禁据此分档:同步栈上裸调=违规;async 体/Promise 回调内裸调=立案(要么 try/catch 转成明确错误出口,要么证明调用链已 catch)。
|
|
46
|
+
async 方法里注册的普通回调(`req.on('data', (c) => { … })`)仍是同步栈——这正是下载回调里裸调 openSync 杀进程的形态。
|
|
47
|
+
|
|
48
|
+
## 同名不同义的同步 API(语义误用,门禁规则③)
|
|
49
|
+
|
|
50
|
+
Java 语义套到鸿蒙同名 API 上,静态类型全对、运行时全错,只在下游以别的错误露头:
|
|
51
|
+
|
|
52
|
+
- **`fileIo.accessSync` 返回布尔,不存在时返回 `false`、不抛**。写成 `try { accessSync(p) } catch { mkdirSync(p) }` 是 `File.exists()` 的错译:不存在分支永远不执行,目录永远建不出来。门禁:返回值弃置=违规。正确写法 `if (!fileIo.accessSync(p)) fileIo.mkdirSync(p, true)`。
|
|
53
|
+
- **`writeSync/readSync` 的 `offset` 选项是定位读写(pwrite/pread),不移动文件游标**。首块带 offset、后续块不带,第二块会从游标 0 覆盖第一块;分块顺序写要么全程不带 offset,要么每块显式 `{ offset: cursor }` 并自行推进 cursor(`RandomAccessFile.seek` + 顺序写的等价写法)。多块下载、断点续传、归档读取都在这一类。
|
|
54
|
+
|
|
55
|
+
## 结果契约视角
|
|
56
|
+
|
|
57
|
+
这些是**平台模型差异**(见 [[platform-model-gaps]]):断言抓**结果契约**("设备支持则震、不支持则静默不崩"),
|
|
58
|
+
实现按鸿蒙 idiom 防护重写,无等价的进 `degradations`。运行时走查须**枚举触发这些交互**(长按/后台/传感器),
|
|
59
|
+
不能只跑 happy path——未触发的交互正是未防护 API 崩溃的藏身处。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 安卓 ↔ 鸿蒙 平台模型差异(不可 1:1,须重架构到鸿蒙 idiom)
|
|
2
|
+
|
|
3
|
+
源码是"该做什么"(用户可见结果)的真源,**不是"怎么做"(机制)的真源**。平台模型有本质差异的地方,
|
|
4
|
+
断言抓**结果/契约**(如"退后台计时继续、回前台进度对齐"),实现**重架构到鸿蒙 idiom**,禁止逐行照搬安卓机制。
|
|
5
|
+
|
|
6
|
+
## 线程 / 并发
|
|
7
|
+
|
|
8
|
+
| 安卓 | 鸿蒙 | 迁移要点 |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| Thread / Handler / Looper | Worker(独立线程实例)/ emitter | UI 线程不可阻塞;跨线程通信走 emitter/MessageChannel |
|
|
11
|
+
| AsyncTask / Executor | `@ohos.taskpool`(TaskPool,任务粒度并发) | 短任务用 TaskPool;长驻用 Worker |
|
|
12
|
+
| Kotlin 协程 / Flow | async/await + Promise;状态用 @ObservedV2/@Trace | 协程的"结构化并发"无直接等价,按 async 链 + AbortController 重写 |
|
|
13
|
+
| LiveData / StateFlow observe | @ObservedV2 + @Trace / AppStorageV2 订阅 | observe 语义→ArkUI 响应式状态;断言抓"值变→UI 变",不抓 observe 机制 |
|
|
14
|
+
| synchronized / 锁 | ArkTS 单线程内无需锁;跨 Worker 用消息传递 | 别移植锁——鸿蒙并发模型是消息传递,共享内存受限 |
|
|
15
|
+
|
|
16
|
+
## 后台执行
|
|
17
|
+
|
|
18
|
+
| 安卓 | 鸿蒙 | 迁移要点 |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| Service(前台/后台) | ContinuousTask(长时任务,须通知栏 + 类型申报) | 后台保活强约束;断言抓"后台X能继续",实现走 ContinuousTask 且注明限制 |
|
|
21
|
+
| WorkManager / JobScheduler | `@ohos.resourceschedule.workScheduler`(延迟任务) | 周期/约束任务映射 workScheduler;即时保活映射 ContinuousTask |
|
|
22
|
+
| BroadcastReceiver | commonEventManager 订阅 | 静态广播多数无等价,改运行时订阅或事件总线 |
|
|
23
|
+
| AlarmManager 精确闹钟 | reminderAgentManager(提醒代理) | 精确定时受系统限制,用提醒代理或后台任务近似 |
|
|
24
|
+
|
|
25
|
+
## 生命周期 / 导航
|
|
26
|
+
|
|
27
|
+
| 安卓 | 鸿蒙 | 迁移要点 |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| Activity / Fragment | UIAbility / @Entry 页 + NavDestination | Activity 栈→NavPathStack;Fragment→组件/子页 |
|
|
30
|
+
| onCreate/onResume/onPause | UIAbility onCreate/onForeground/onBackground + 组件 aboutToAppear/onPageShow | 生命周期回调不一一对应,按"何时该发生什么"重挂 |
|
|
31
|
+
| savedInstanceState 进程重建 | 无自动等价——须显式持久化 + 恢复 | 进程重建态靠 preferences/文件显式存恢复,别指望系统托管 |
|
|
32
|
+
| Intent / startActivity | want / router / NavPathStack.pushPathByName | 显式 Intent→路由名;隐式 Intent/scheme→module.json5 skills.uris |
|
|
33
|
+
|
|
34
|
+
## 权限 / 系统能力
|
|
35
|
+
|
|
36
|
+
- 安卓 runtime 权限 → 鸿蒙 `abilityAccessCtrl`;部分权限鸿蒙为**受限/ACL**(通讯录/短信/精确定位),读取面收窄或走 picker。
|
|
37
|
+
- AppOps 探测(PiP 等)→ 鸿蒙对应能力查询 API 或直接能力申报。
|
|
38
|
+
|
|
39
|
+
## 断言纪律(写进探索卡)
|
|
40
|
+
|
|
41
|
+
- 平台模型差异点的断言,标注 `kind: backend` 且在文本里写"**结果契约**:<用户可见行为>;**鸿蒙实现**:<idiom>;**差异**:<不可 1:1 的点>"。
|
|
42
|
+
- 验收(verify/探针)核对**结果契约**,不核对是否照搬了安卓机制——重架构实现只要满足结果契约即通过。
|
|
43
|
+
- 完全无鸿蒙等价的(如某些精确后台保活)→ 台账 `degradations` 登记,做显式降级态 + 说明,不假装 1:1。
|