Compare commits

...

15 Commits

Author SHA1 Message Date
cheliangzhao
c0f1574e47 docs: remove generic skills search section from Quick Deploy 2026-02-11 10:21:21 +08:00
cheliangzhao
b7ebfea749 docs: rename README title to AI Agent Skills 2026-02-11 10:20:17 +08:00
cheliangzhao
172531e473 docs: add version sync rule to AGENTS.md and sync README to v1.0.1 2026-02-11 10:13:52 +08:00
cheliangzhao
0b6a0eff34 feat: add module discovery and build output path documentation 2026-02-11 10:11:59 +08:00
cheliangzhao
61fa472a88 docs: add version 1.0.0 badge, CHANGELOG, and changelog maintenance rule 2026-02-10 21:14:49 +08:00
cheliangzhao
5ee63b901a docs: simplify AGENTS.md by removing git workflow examples 2026-02-10 21:12:49 +08:00
cheliangzhao
19125ca794 docs: add README maintenance guidelines to AGENTS.md
- Add clear rules for when to update README
- Add checklist for documentation updates
- Include workflow for pushing to both remotes (origin + gitea)
- Distinguish between changes that require README updates vs those that don't
2026-02-10 21:12:13 +08:00
cheliangzhao
87993a5a2c docs: update README with State Management V2 and new files
- Add state-management-v2.md and state-management-v2-examples.ets to file tree
- Update arkts-development coverage to include V2 decorators
- Add mention of code obfuscation, linting, and debugging tools
2026-02-10 21:08:52 +08:00
cheliangzhao
7f1ff2df6b feat: add comprehensive State Management V2 documentation
- Add complete V2 decorators guide (state-management-v2.md, 38KB)
  - @ComponentV2, @Local, @Param, @Event
  - @ObservedV2, @Trace, @Computed, @Monitor
  - @Provider/@Consumer for cross-level sync
  - V1 vs V2 comparison and migration guide
  - Best practices and troubleshooting

- Add 9 practical V2 code examples (state-management-v2-examples.ets, 13KB)
  - Basic components, nested objects, parent-child communication
  - Computed properties, monitors, providers/consumers
  - Real-world patterns: forms, todo lists, collections

- Update SKILL.md with V2 decorators reference table
- Source: Official OpenHarmony documentation (API 12+)
2026-02-10 21:00:52 +08:00
cheliangzhao
9d902b1074 fix: improve type safety and error handling in arkts-development
- Replace Record<string, Object> with specific interface types for router params
- Add error handling catch block to list-page-template.ets
- Add clarification note for router.back() with params
- Improve type safety following ArkTS best practices
2026-02-10 20:52:17 +08:00
cheliangzhao
81b3c82d16 docs: translate reference documentation from Chinese to English
- Translate arkguard-obfuscation.md (ArkGuard code obfuscation guide)
- Translate hstack.md (stack trace analysis tool)
- Translate codelinter.md (code linting tool)
- Translate hvigor-commandline.md (Hvigor build tool)
- Fix capitalization: ArkTs -> ArkTS in README title
2026-02-10 20:38:13 +08:00
cheliangzhao
a68f3d1a7d fix: remove platform-specific references from README 2026-02-10 20:29:39 +08:00
cheliangzhao
4565396825 fix: clean up README and add .gitignore
- Translate Quick Deploy section from Chinese to English
- Fix repo slug from my-skills to arkts_skills
- Add --skill flag usage examples for single skill install
- Update directory tree to match actual repo structure
- Add .gitignore for temp files, OS files, and editor files
- Remove junk temp files from working tree
2026-02-10 20:16:13 +08:00
cheliangzhao
a984e27246 fix: move skill directories to repo root and update AGENTS.md structure diagram 2026-02-10 20:04:27 +08:00
cheliangzhao
2378b310a5 Reorganize skill files into skills/ subdirectory 2026-02-10 16:59:56 +08:00
17 changed files with 4563 additions and 1983 deletions

18
.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
# Temp files
*.tmp
*.bak
*~
# OS files
Thumbs.db
Desktop.ini
.DS_Store
# Editor files
.vscode/
.idea/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

289
AGENTS.md
View File

@@ -1,115 +1,126 @@
# AGENTS.md - OpenCode Skills Repository
This repository contains AI coding agent skills for HarmonyOS/ArkTS development. Skills teach AI assistants how to build, deploy, and develop HarmonyOS applications.
AI coding agent skills for HarmonyOS/ArkTS development. This is a **documentation-only** repo -- no build system for the repo itself. The skills teach agents how to build, deploy, and develop HarmonyOS apps.
## Repository Structure
```
skills/
├── arkts-development/ # ArkTS/ArkUI development skill
│ ├── SKILL.md # Main skill definition
│ ├── assets/ # Code templates (.ets files)
│ └── references/ # API docs, migration guide, patterns
── harmonyos-build-deploy/ # Build & deploy skill
├── SKILL.md # Main skill definition
└── references/ # Device installation guide
arkts-development/ # ArkTS/ArkUI development skill
├── SKILL.md # Main skill definition (loaded by agent)
├── assets/*.ets # Code templates
└── references/*.md # API docs, migration guide, patterns
harmonyos-build-deploy/ # Build & deploy skill
── SKILL.md # Main skill definition (loaded by agent)
└── references/*.md # Device installation guide
```
## Build/Lint/Test Commands
## Build / Lint / Test Commands
This is a documentation repository - no build system for the repo itself. However, the skills document these HarmonyOS CLI tools:
These commands apply to **HarmonyOS projects** that the skills describe, not this repo.
### Build Commands (hvigorw)
### Build (hvigorw)
```bash
# Incremental build (default for development)
hvigorw assembleApp --mode project -p product=default -p buildMode=release --no-daemon
# Clean build
hvigorw clean --no-daemon
hvigorw assembleApp --mode project -p product=default -p buildMode=release --no-daemon
# Build single module (faster iteration)
hvigorw assembleHap -p module=entry@default --mode module -p buildMode=release --no-daemon
# Run tests
hvigorw onDeviceTest -p module=entry -p coverage=true # On-device test
hvigorw test -p module=entry # Local test
hvigorw assembleApp --mode project -p product=default -p buildMode=release --no-daemon # Full app
hvigorw assembleHap -p module=entry@default --mode module -p buildMode=release --no-daemon # Single module (faster)
hvigorw clean --no-daemon # Clean artifacts
hvigorw --sync -p product=default -p buildMode=release --no-daemon # Sync after config change
```
### Package Manager (ohpm)
### Test
```bash
ohpm install --all # Install all dependencies
ohpm clean && ohpm cache clean # Deep clean
# Run all tests for a module (on-device)
hvigorw onDeviceTest -p module=entry -p coverage=true --no-daemon
# Run all tests for a module (local / host-side)
hvigorw test -p module=entry --no-daemon
# Run a single test suite or single test (on-device) -- use -p testParam
hvigorw onDeviceTest -p module=entry -p testParam="{\"unittest\":\"TestClassName\"}" --no-daemon
hvigorw onDeviceTest -p module=entry -p testParam="{\"unittest\":\"TestClassName#testMethodName\"}" --no-daemon
```
### Code Linter (codelinter)
### Dependencies (ohpm)
```bash
codelinter # Check current project
codelinter --fix # Check and auto-fix
codelinter -f json -o report.json # JSON output
codelinter -i # Incremental (Git changes only)
codelinter --exit-on error,warn # CI/CD with exit codes
ohpm install --all # Install all deps
ohpm clean && ohpm cache clean # Deep clean
```
### Device Commands (hdc)
### Lint (codelinter)
```bash
hdc list targets # List devices (returns UDID)
hdc -t <UDID> file send <local> <remote> # Push files
hdc -t <UDID> shell "bm install -p <path>" # Install app
hdc -t <UDID> shell "aa start -a EntryAbility -b <bundleName>" # Launch app
codelinter # Check project
codelinter --fix # Auto-fix
codelinter -i # Incremental (git changes only)
codelinter --exit-on error,warn # CI mode (non-zero exit on issues)
codelinter -f json -o report.json # JSON report
```
### Device (hdc)
```bash
hdc list targets # List devices (returns UDID)
hdc -t <UDID> file send <local_path> <device_path> # Push files
hdc -t <UDID> shell "bm install -p <dir>" # Install app
hdc -t <UDID> shell "aa start -a EntryAbility -b <bundleName>" # Launch app
```
## Code Style Guidelines
### ArkTS Language Constraints
ArkTS is stricter than TypeScript. These are prohibited:
ArkTS is stricter than TypeScript. These features are **prohibited**:
| Prohibited | Use Instead |
|------------|-------------|
| `any`, `unknown` | Explicit types, interfaces |
| `var` | `let`, `const` |
| Dynamic property `obj['key']` | Fixed object structure |
| `for...in`, `delete`, `with` | `for...of`, array methods |
| `#privateField` | `private` keyword |
| Structural typing | Explicit `implements`/`extends` |
| Prohibited | Use Instead |
|-------------------------|------------------------------------|
| `any`, `unknown` | Explicit types, interfaces |
| `var` | `let`, `const` |
| `obj['key']` (dynamic) | Fixed object structure |
| `for...in` | `for...of`, array methods |
| `delete`, `with` | Optional properties, explicit refs |
| `#privateField` | `private` keyword |
| Structural typing | Explicit `implements` / `extends` |
| `eval()`, `Function()` | Arrow functions, static code |
### Naming Conventions
| Element | Convention | Example |
|---------|------------|---------|
| Components (struct) | PascalCase | `HomePage`, `UserCard` |
| Methods | camelCase | `loadData()`, `handleClick()` |
| Properties/Variables | camelCase | `isLoading`, `userName` |
| Interfaces | PascalCase | `UserInfo`, `ApiResponse` |
| Constants | camelCase or UPPER_SNAKE | `maxRetries`, `API_URL` |
| Files (skill docs) | kebab-case | `api-reference.md` |
| Skill directories | kebab-case | `arkts-development` |
| Element | Convention | Example |
|-----------------------|-----------------|-----------------------------|
| Components (struct) | PascalCase | `HomePage`, `UserCard` |
| Interfaces | PascalCase | `UserInfo`, `ApiResponse` |
| Methods / Functions | camelCase | `loadData()`, `handleClick` |
| Properties / Variables| camelCase | `isLoading`, `userName` |
| Constants | camelCase or UPPER_SNAKE | `maxRetries`, `API_URL` |
| Skill directories | kebab-case | `arkts-development` |
| Filenames (docs) | kebab-case | `api-reference.md` |
### Type Annotations
Always use explicit type annotations:
Always use **explicit** type annotations on declarations, parameters, and return types:
```typescript
// Required: Explicit types on declarations
@State isLoading: boolean = false;
@State items: ItemType[] = [];
// Required: Return types on methods
async loadData(): Promise<void> { }
navigateToDetail(item: ItemType): void { }
// Required: Parameter types
ForEach(this.items, (item: ItemType) => { ... }, (item: ItemType) => item.id)
```
### Imports
Use HarmonyOS Kit-style imports:
```typescript
import { router } from '@kit.ArkUI';
import { http } from '@kit.NetworkKit';
import { preferences } from '@kit.ArkData';
```
### Component Structure
Follow this standard component layout:
Follow this ordering inside every `@Component` struct:
```typescript
@Entry
@@ -118,23 +129,21 @@ struct ComponentName {
// 1. State decorators
@State isLoading: boolean = false;
@State errorMessage: string = '';
// 2. Lifecycle methods
// 2. Lifecycle
aboutToAppear(): void { this.loadData(); }
aboutToDisappear(): void { /* cleanup */ }
// 3. Business methods
async loadData(): Promise<void> { }
// 4. Builder methods (reusable UI blocks)
// 4. @Builder methods (reusable UI blocks)
@Builder ItemCard(item: ItemType) { }
// 5. Build method (always last)
// 5. build() -- always last
build() { }
}
```
### Error Handling Pattern
### Error Handling
Wrap async work in try/catch/finally with loading state:
```typescript
async loadData(): Promise<void> {
@@ -149,81 +158,71 @@ async loadData(): Promise<void> {
}
```
### Import Style
Use HarmonyOS Kit imports:
```typescript
import { router } from '@kit.ArkUI';
import { http } from '@kit.NetworkKit';
import { preferences } from '@kit.ArkData';
```
## Skill File Format
Each skill follows this structure:
```markdown
---
name: skill-name
description: Detailed description for AI agent matching
---
# Skill Title
## Quick Start / Quick Reference
## Main Content Sections
## Troubleshooting
## Reference Files
```
### YAML Frontmatter
Required fields:
- `name`: kebab-case identifier matching directory name
- `description`: Detailed description for AI agent matching (trigger keywords)
## Documentation Conventions
- Use tables for command references and parameters
- Include code blocks with language specifiers (`typescript`, `bash`)
- Cross-reference related skills and reference files
- Use bilingual content (Chinese/English) for HarmonyOS context
- Structure: Quick Reference first, then detailed sections
## File Organization
- Main skill definition: `SKILL.md` (uppercase)
- Code templates: `assets/*.ets`
- Reference documentation: `references/*.md`
- All filenames: kebab-case
## Common Patterns
### State Management Decorators
| Decorator | Direction | Usage |
|-----------|-----------|-------|
| `@State` | Internal | Component's own mutable state |
| `@Prop` | Parent → Child | One-way binding (child copy) |
| `@Link` | Parent ↔ Child | Two-way binding (pass with `$var`) |
| `@Provide/@Consume` | Ancestor → Descendant | Cross-level state sharing |
### Layout Components
```typescript
Column({ space: 10 }) { } // Vertical layout
Row({ space: 10 }) { } // Horizontal layout
Stack() { } // Overlay layout
List({ space: 10 }) { } // Scrollable list
```
### ForEach Pattern
Always provide explicit types and key generator:
### ForEach -- always provide types and a key generator
```typescript
ForEach(this.items, (item: ItemType) => {
ListItem() { Text(item.title) }
}, (item: ItemType) => item.id) // Key generator for efficient updates
}, (item: ItemType) => item.id)
```
## Skill File Format
Every skill directory contains a `SKILL.md` with YAML frontmatter:
```markdown
---
name: skill-name # kebab-case, matches directory name
description: ... # Detailed description for agent matching
---
# Skill Title
## Quick Start / Quick Reference
## Detailed Sections
## Troubleshooting
## Reference Files
```
### File Organization Rules
- Main definition: `SKILL.md` (uppercase)
- Code templates: `assets/*.ets`
- Reference docs: `references/*.md`
- All filenames: **kebab-case**
## Documentation Conventions
- Use **tables** for command references and parameter lists.
- Use **fenced code blocks** with language specifiers (`typescript`, `bash`).
- Structure content as: Quick Reference first, then detailed sections.
- Cross-reference related skills and reference files by relative path.
## Maintaining Documentation
### When to Update README.md
**ALWAYS check and update README.md after making changes to the repository.**
Update README when:
-**Adding/removing files** - Update the repository structure section
-**Adding new features/sections** - Update the "Covers" section of relevant skills
-**Modifying project structure** - Update the file tree diagram
-**Adding new skill directories** - Add to the "Available Skills" section
Do NOT update README for:
- ❌ Bug fixes that don't change functionality
- ❌ Content improvements that don't add new features
- ❌ Wording/typo corrections
- ❌ Internal refactoring without user-visible changes
### Update Checklist
When making changes, follow this checklist:
1. Make your changes to skill files
2. **Check**: Did I add/remove files? → Update file tree in README
3. **Check**: Did I add new functionality? → Update "Covers" section in README
4. Update README.md if needed
5. **ALWAYS** update CHANGELOG.md with a summary of changes for every commit
6. **ALWAYS** sync the version badge in README.md when CHANGELOG.md version changes

33
CHANGELOG.md Normal file
View File

@@ -0,0 +1,33 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [1.0.1] - 2026-02-11
### Added
- **harmonyos-build-deploy**: Finding Modules section (module definitions in build-profile.json5, module type identification)
- **harmonyos-build-deploy**: Finding Module Build Outputs section (output paths, signed/unsigned artifacts, search commands)
## [1.0.0] - 2026-02-10
### Added
- **arkts-development** skill
- ArkUI declarative UI framework guide
- State management V1 decorators (@State, @Prop, @Link, @Provide/@Consume, @Observed/@ObjectLink)
- State management V2 decorators (@ComponentV2, @Local, @Param, @Event, @ObservedV2, @Trace, @Computed, @Monitor, @Provider/@Consumer)
- Component lifecycle and navigation (router)
- Network requests (http) and local storage (preferences)
- TypeScript to ArkTS migration guide
- Code templates: component-template.ets, list-page-template.ets, state-management-v2-examples.ets
- Reference docs: API reference, component patterns, ArkGuard obfuscation, CodeLinter, hstack, hvigorw command-line
- **harmonyos-build-deploy** skill
- hvigorw build commands (assembleApp, assembleHap, assembleHsp, assembleHar)
- ohpm dependency management
- hdc device installation and management
- Troubleshooting common build/deploy errors
- Device installation reference guide
- **AGENTS.md** with coding conventions and documentation guidelines

View File

@@ -1,10 +1,12 @@
# OpenCode Skills - HarmonyOS Development
# AI Agent Skills - HarmonyOS/ArkTS Development
[![Version](https://img.shields.io/badge/version-1.0.1-blue.svg)](CHANGELOG.md)
AI coding agent skills for HarmonyOS/ArkTS application development.
## What are Skills?
Skills are structured documentation that teach AI coding assistants (like OpenCode) how to perform specific development tasks. Each skill contains:
Skills are structured documentation that teach AI coding assistants how to perform specific development tasks. Each skill contains:
- **SKILL.md** - Main skill definition with quick reference and detailed guides
- **assets/** - Code templates and examples
@@ -18,10 +20,11 @@ ArkTS/ArkUI development for HarmonyOS applications.
**Covers:**
- ArkUI declarative UI framework
- State management decorators (@State, @Prop, @Link)
- State management V1 (@State, @Prop, @Link) and V2 (@Local, @Param, @Event, @ObservedV2, @Trace)
- Component lifecycle and navigation
- Network requests and local storage
- TypeScript to ArkTS migration
- Code obfuscation, linting, and debugging tools
### harmonyos-build-deploy
@@ -33,9 +36,32 @@ Build, package, and deploy HarmonyOS applications.
- hdc device installation
- Troubleshooting common errors
## Quick Deploy
Use [skills.sh](https://skills.sh/) to deploy skills to your project with a single command.
### Install all skills
```bash
npx skills add FadingLight9291117/arkts_skills
```
### Install a single skill
You can install a specific skill from this repo using the `--skill` flag:
```bash
npx skills add https://github.com/FadingLight9291117/arkts_skills --skill harmonyos-build-deploy
npx skills add https://github.com/FadingLight9291117/arkts_skills --skill arkts-development
```
Once installed, the skill is automatically configured for your AI agent (supports Cursor, Claude Code, Copilot, and other major agents).
> No additional CLI installation required — `npx` downloads and runs it automatically. To disable anonymous telemetry, set the environment variable `DISABLE_TELEMETRY=1`.
## Usage
These skills are automatically loaded by OpenCode when relevant tasks are detected. The AI agent uses the skill documentation to:
These skills are automatically loaded by the AI agent when relevant tasks are detected. The agent uses the skill documentation to:
1. Follow correct build/deploy procedures
2. Write code following ArkTS conventions
@@ -45,25 +71,28 @@ These skills are automatically loaded by OpenCode when relevant tasks are detect
## Repository Structure
```
skills/
├── AGENTS.md # Guidelines for AI agents
├── README.md # This file
├── arkts-development/
├── SKILL.md
├── assets/
│ ├── component-template.ets
│ └── list-page-template.ets
│ └── references/
│ ├── api-reference.md
├── codelinter.md
├── component-patterns.md
├── hstack.md
├── hvigor-commandline.md
── migration-guide.md
└── harmonyos-build-deploy/
├── SKILL.md
└── references/
└── device-installation.md
AGENTS.md # Guidelines for AI agents
CHANGELOG.md # Version history
README.md # This file
arkts-development/
├── SKILL.md
├── assets/
│ ├── component-template.ets
── list-page-template.ets
│ └── state-management-v2-examples.ets
└── references/
├── api-reference.md
├── arkguard-obfuscation.md
├── codelinter.md
├── component-patterns.md
── hstack.md
├── hvigor-commandline.md
├── migration-guide.md
└── state-management-v2.md
harmonyos-build-deploy/
├── SKILL.md
└── references/
└── device-installation.md
```
## Contributing

View File

@@ -34,6 +34,8 @@ struct HelloWorld {
## State Management Decorators
### V1 (Traditional)
| Decorator | Usage | Description |
|-----------|-------|-------------|
| `@State` | `@State count: number = 0` | Component internal state |
@@ -42,6 +44,22 @@ struct HelloWorld {
| `@Provide/@Consume` | Cross-level | Ancestor → Descendant |
| `@Observed/@ObjectLink` | Nested objects | Deep object observation |
### V2 (Recommended - API 12+)
| Decorator | Usage | Description |
|-----------|-------|-------------|
| `@ComponentV2` | `@ComponentV2 struct MyComp` | Enable V2 state management |
| `@Local` | `@Local count: number = 0` | Internal state (no external init) |
| `@Param` | `@Param title: string = ""` | Parent → Child (one-way, efficient) |
| `@Event` | `@Event onChange: () => void` | Child → Parent (callback) |
| `@ObservedV2` | `@ObservedV2 class Data` | Class observation |
| `@Trace` | `@Trace name: string` | Property-level tracking |
| `@Computed` | `@Computed get value()` | Cached computed properties |
| `@Monitor` | `@Monitor('prop') onFn()` | Watch changes with before/after |
| `@Provider/@Consumer` | Cross-level | Two-way sync across tree |
**See [references/state-management-v2.md](references/state-management-v2.md) for complete V2 guide.**
## Common Layouts
```typescript
@@ -96,7 +114,11 @@ router.replaceUrl({ url: 'pages/New' });
router.back();
// Get params
const params = router.getParams() as Record<string, Object>;
interface RouteParams {
id: number;
title?: string;
}
const params = router.getParams() as RouteParams;
```
## Network Request
@@ -273,6 +295,7 @@ See [references/arkguard-obfuscation.md](references/arkguard-obfuscation.md) for
## Reference Files
- **State Management V2**: [references/state-management-v2.md](references/state-management-v2.md) - Complete guide to V2 state management (@ComponentV2, @Local, @Param, @Event, @ObservedV2, @Trace, @Computed, @Monitor, @Provider, @Consumer)
- **Migration Guide**: [references/migration-guide.md](references/migration-guide.md) - Complete TypeScript to ArkTS migration rules and examples
- **Component Patterns**: [references/component-patterns.md](references/component-patterns.md) - Advanced component patterns and best practices
- **API Reference**: [references/api-reference.md](references/api-reference.md) - Common HarmonyOS APIs

View File

@@ -1,57 +1,57 @@
// ArkTS Basic Component Template
// Replace {{ComponentName}} with your component name
@Entry
@Component
struct {{ComponentName}} {
// State management
@State isLoading: boolean = false;
@State errorMessage: string = '';
// Lifecycle
aboutToAppear(): void {
this.loadData();
}
aboutToDisappear(): void {
// Cleanup resources
}
// Methods
async loadData(): Promise<void> {
this.isLoading = true;
try {
// Load data here
} catch (error) {
this.errorMessage = 'Failed to load data';
} finally {
this.isLoading = false;
}
}
// Build
build() {
Column() {
if (this.isLoading) {
LoadingProgress()
.width(50)
.height(50)
} else if (this.errorMessage) {
Text(this.errorMessage)
.fontSize(16)
.fontColor(Color.Red)
Button('Retry')
.onClick(() => { this.loadData(); })
} else {
// Main content here
Text('{{ComponentName}}')
.fontSize(24)
.fontWeight(FontWeight.Bold)
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
// ArkTS Basic Component Template
// Replace {{ComponentName}} with your component name
@Entry
@Component
struct {{ComponentName}} {
// State management
@State isLoading: boolean = false;
@State errorMessage: string = '';
// Lifecycle
aboutToAppear(): void {
this.loadData();
}
aboutToDisappear(): void {
// Cleanup resources
}
// Methods
async loadData(): Promise<void> {
this.isLoading = true;
try {
// Load data here
} catch (error) {
this.errorMessage = 'Failed to load data';
} finally {
this.isLoading = false;
}
}
// Build
build() {
Column() {
if (this.isLoading) {
LoadingProgress()
.width(50)
.height(50)
} else if (this.errorMessage) {
Text(this.errorMessage)
.fontSize(16)
.fontColor(Color.Red)
Button('Retry')
.onClick(() => { this.loadData(); })
} else {
// Main content here
Text('{{ComponentName}}')
.fontSize(24)
.fontWeight(FontWeight.Bold)
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}

View File

@@ -1,25 +1,25 @@
// ArkTS List Page Template
// Replace {{PageName}}, {{ItemType}}, {{itemName}} with your values
import { router } from '@kit.ArkUI';
interface {{ItemType}} {
id: string;
title: string;
description: string;
}
@Entry
@Component
struct {{PageName}} {
@State items: {{ItemType}}[] = [];
@State isLoading: boolean = false;
@State isRefreshing: boolean = false;
aboutToAppear(): void {
this.loadItems();
}
// ArkTS List Page Template
// Replace {{PageName}}, {{ItemType}}, {{itemName}} with your values
import { router } from '@kit.ArkUI';
interface {{ItemType}} {
id: string;
title: string;
description: string;
}
@Entry
@Component
struct {{PageName}} {
@State items: {{ItemType}}[] = [];
@State isLoading: boolean = false;
@State isRefreshing: boolean = false;
aboutToAppear(): void {
this.loadItems();
}
async loadItems(): Promise<void> {
this.isLoading = true;
try {
@@ -28,102 +28,105 @@ struct {{PageName}} {
{ id: '1', title: 'Item 1', description: 'Description 1' },
{ id: '2', title: 'Item 2', description: 'Description 2' },
];
} catch (error) {
console.error('Failed to load items:', error);
// TODO: Show error message to user
} finally {
this.isLoading = false;
}
}
async refreshItems(): Promise<void> {
this.isRefreshing = true;
await this.loadItems();
this.isRefreshing = false;
}
navigateToDetail(item: {{ItemType}}): void {
router.pushUrl({
url: 'pages/{{PageName}}Detail',
params: { id: item.id }
});
}
@Builder
ItemCard(item: {{ItemType}}) {
Column() {
Text(item.title)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
Text(item.description)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.padding(16)
.width('100%')
.backgroundColor(Color.White)
.borderRadius(8)
.onClick(() => { this.navigateToDetail(item); })
}
build() {
Column() {
// Header
Row() {
Text('{{PageName}}')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Blank()
Button('Add')
.onClick(() => {
router.pushUrl({ url: 'pages/{{PageName}}Create' });
})
}
.width('100%')
.padding(16)
// Content
if (this.isLoading && this.items.length === 0) {
Column() {
LoadingProgress().width(50).height(50)
Text('Loading...').margin({ top: 16 })
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else if (this.items.length === 0) {
Column() {
Text('No items found')
.fontSize(16)
.fontColor('#999999')
Button('Refresh')
.margin({ top: 16 })
.onClick(() => { this.loadItems(); })
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else {
Refresh({ refreshing: $$this.isRefreshing }) {
List({ space: 12 }) {
ForEach(this.items, (item: {{ItemType}}) => {
ListItem() {
this.ItemCard(item)
}
}, (item: {{ItemType}}) => item.id)
}
.width('100%')
.padding({ left: 16, right: 16 })
}
.onRefreshing(() => { this.refreshItems(); })
.layoutWeight(1)
}
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5')
}
}
async refreshItems(): Promise<void> {
this.isRefreshing = true;
await this.loadItems();
this.isRefreshing = false;
}
navigateToDetail(item: {{ItemType}}): void {
router.pushUrl({
url: 'pages/{{PageName}}Detail',
params: { id: item.id }
});
}
@Builder
ItemCard(item: {{ItemType}}) {
Column() {
Text(item.title)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
Text(item.description)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.padding(16)
.width('100%')
.backgroundColor(Color.White)
.borderRadius(8)
.onClick(() => { this.navigateToDetail(item); })
}
build() {
Column() {
// Header
Row() {
Text('{{PageName}}')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Blank()
Button('Add')
.onClick(() => {
router.pushUrl({ url: 'pages/{{PageName}}Create' });
})
}
.width('100%')
.padding(16)
// Content
if (this.isLoading && this.items.length === 0) {
Column() {
LoadingProgress().width(50).height(50)
Text('Loading...').margin({ top: 16 })
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else if (this.items.length === 0) {
Column() {
Text('No items found')
.fontSize(16)
.fontColor('#999999')
Button('Refresh')
.margin({ top: 16 })
.onClick(() => { this.loadItems(); })
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else {
Refresh({ refreshing: $$this.isRefreshing }) {
List({ space: 12 }) {
ForEach(this.items, (item: {{ItemType}}) => {
ListItem() {
this.ItemCard(item)
}
}, (item: {{ItemType}}) => item.id)
}
.width('100%')
.padding({ left: 16, right: 16 })
}
.onRefreshing(() => { this.refreshItems(); })
.layoutWeight(1)
}
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5')
}
}

View File

@@ -0,0 +1,523 @@
// State Management V2 - Quick Reference Examples
// ============================================
// 1. BASIC COMPONENT WITH @Local STATE
// ============================================
@Entry
@ComponentV2
struct CounterApp {
@Local count: number = 0;
@Local step: number = 1;
build() {
Column({ space: 10 }) {
Text(`Count: ${this.count}`)
.fontSize(30)
Button(`+${this.step}`)
.onClick(() => this.count += this.step)
Button(`-${this.step}`)
.onClick(() => this.count -= this.step)
}
}
}
// ============================================
// 2. NESTED OBJECTS WITH @ObservedV2/@Trace
// ============================================
@ObservedV2
class Address {
@Trace city: string;
@Trace street: string;
constructor(city: string, street: string) {
this.city = city;
this.street = street;
}
}
@ObservedV2
class Person {
@Trace name: string;
@Trace age: number;
@Trace address: Address;
constructor(name: string, age: number, address: Address) {
this.name = name;
this.age = age;
this.address = address;
}
}
@Entry
@ComponentV2
struct PersonProfile {
person: Person = new Person(
"Tom",
25,
new Address("Beijing", "Main St")
);
build() {
Column({ space: 10 }) {
Text(`${this.person.name}, ${this.person.age}`)
Text(`${this.person.address.city}`)
Button('Birthday').onClick(() => {
this.person.age++; // Observable
})
Button('Move').onClick(() => {
this.person.address.city = "Shanghai"; // Observable
})
}
}
}
// ============================================
// 3. PARENT-CHILD WITH @Param/@Event
// ============================================
@ComponentV2
struct Counter {
@Param count: number = 0;
@Event onIncrement: () => void = () => {};
@Event onDecrement: () => void = () => {};
build() {
Row({ space: 10 }) {
Button('-').onClick(() => this.onDecrement())
Text(`${this.count}`).fontSize(30)
Button('+').onClick(() => this.onIncrement())
}
}
}
@Entry
@ComponentV2
struct ParentApp {
@Local count: number = 0;
build() {
Column({ space: 20 }) {
Text('Parent Count: ' + this.count)
Counter({
count: this.count,
onIncrement: () => this.count++,
onDecrement: () => this.count--
})
}
}
}
// ============================================
// 4. COMPUTED PROPERTIES
// ============================================
@ObservedV2
class CartItem {
@Trace name: string;
@Trace price: number;
@Trace quantity: number;
constructor(name: string, price: number, quantity: number) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
}
@Entry
@ComponentV2
struct ShoppingCart {
@Local items: CartItem[] = [
new CartItem("Apple", 5, 3),
new CartItem("Banana", 3, 5)
];
@Local taxRate: number = 0.1;
@Computed
get subtotal(): number {
console.log("Computing subtotal"); // Only logs when items change
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
@Computed
get tax(): number {
return this.subtotal * this.taxRate;
}
@Computed
get total(): number {
return this.subtotal + this.tax;
}
build() {
Column({ space: 10 }) {
ForEach(this.items, (item: CartItem) => {
Row() {
Text(`${item.name}: $${item.price} × ${item.quantity}`)
Button('+').onClick(() => item.quantity++)
}
}, (item: CartItem, idx: number) => item.name + idx)
Divider()
Text(`Subtotal: $${this.subtotal.toFixed(2)}`)
Text(`Tax (${this.taxRate * 100}%): $${this.tax.toFixed(2)}`)
Text(`Total: $${this.total.toFixed(2)}`).fontWeight(FontWeight.Bold)
}
}
}
// ============================================
// 5. MONITOR CHANGES
// ============================================
@ObservedV2
class Product {
@Trace name: string = "Laptop";
@Trace price: number = 1000;
@Trace stock: number = 10;
@Monitor('price')
onPriceChange(monitor: IMonitor) {
const change = monitor.value();
console.log(`Price changed from ${change?.before} to ${change?.now}`);
if (change && change.now > change.before) {
console.log(`Price increased by ${change.now - change.before}`);
}
}
@Monitor('stock')
onStockChange(monitor: IMonitor) {
const change = monitor.value();
if (change && change.now < 5) {
console.warn(`Low stock alert: ${change.now} items`);
}
}
}
@Entry
@ComponentV2
struct ProductManager {
product: Product = new Product();
@Monitor('product.price', 'product.stock')
onProductChange(monitor: IMonitor) {
console.log('Product properties changed:', monitor.dirty);
}
build() {
Column({ space: 10 }) {
Text(`${this.product.name}`)
Text(`Price: $${this.product.price}`)
Text(`Stock: ${this.product.stock}`)
Button('Increase Price').onClick(() => {
this.product.price += 100;
})
Button('Decrease Stock').onClick(() => {
this.product.stock--;
})
}
}
}
// ============================================
// 6. PROVIDER/CONSUMER (CROSS-LEVEL)
// ============================================
@Entry
@ComponentV2
struct AppRoot {
@Provider('app-theme') theme: string = 'light';
@Provider('user-name') userName: string = 'Alice';
build() {
Column({ space: 20 }) {
Text(`App Theme: ${this.theme}`)
Button('Toggle Theme').onClick(() => {
this.theme = this.theme === 'light' ? 'dark' : 'light';
})
MiddleComponent()
}
}
}
@ComponentV2
struct MiddleComponent {
build() {
Column({ space: 10 }) {
Text('Middle Component')
DeepNestedComponent()
}
}
}
@ComponentV2
struct DeepNestedComponent {
@Consumer('app-theme') theme: string = 'default';
@Consumer('user-name') userName: string = 'Guest';
build() {
Column({ space: 10 }) {
Text(`User: ${this.userName}`)
Text(`Theme: ${this.theme}`)
.backgroundColor(this.theme === 'dark' ? Color.Black : Color.White)
.fontColor(this.theme === 'dark' ? Color.White : Color.Black)
Button('Change from Deep Component').onClick(() => {
this.theme = 'custom'; // Updates provider in AppRoot
})
}
}
}
// ============================================
// 7. FORM INPUT PATTERN
// ============================================
@ObservedV2
class FormData {
@Trace username: string = "";
@Trace email: string = "";
@Trace password: string = "";
}
@ComponentV2
struct FormField {
@Param label: string = "";
@Param value: string = "";
@Param type: InputType = InputType.Normal;
@Event onChange: (text: string) => void = () => {};
build() {
Column({ space: 5 }) {
Text(this.label).fontSize(14)
TextInput({ text: this.value })
.type(this.type)
.onChange((text: string) => this.onChange(text))
}
}
}
@Entry
@ComponentV2
struct RegistrationForm {
@Local formData: FormData = new FormData();
@Local isValid: boolean = false;
@Monitor('formData.username', 'formData.email', 'formData.password')
validateForm(monitor: IMonitor) {
this.isValid =
this.formData.username.length >= 3 &&
this.formData.email.includes('@') &&
this.formData.password.length >= 8;
}
build() {
Column({ space: 15 }) {
Text('Registration').fontSize(24).fontWeight(FontWeight.Bold)
FormField({
label: 'Username',
value: this.formData.username,
onChange: (text: string) => {
this.formData.username = text;
}
})
FormField({
label: 'Email',
value: this.formData.email,
type: InputType.Email,
onChange: (text: string) => {
this.formData.email = text;
}
})
FormField({
label: 'Password',
value: this.formData.password,
type: InputType.Password,
onChange: (text: string) => {
this.formData.password = text;
}
})
Button('Submit')
.enabled(this.isValid)
.backgroundColor(this.isValid ? Color.Blue : Color.Gray)
.onClick(() => {
console.log('Form submitted:', this.formData);
})
}
.padding(20)
}
}
// ============================================
// 8. TODO LIST EXAMPLE
// ============================================
@ObservedV2
class TodoItem {
@Trace id: string;
@Trace title: string;
@Trace completed: boolean;
constructor(title: string) {
this.id = Date.now().toString() + Math.random();
this.title = title;
this.completed = false;
}
}
@ObservedV2
class TodoStore {
@Trace items: TodoItem[] = [];
addItem(title: string): void {
this.items.push(new TodoItem(title));
}
removeItem(id: string): void {
const index = this.items.findIndex(item => item.id === id);
if (index !== -1) {
this.items.splice(index, 1);
}
}
toggleItem(id: string): void {
const item = this.items.find(item => item.id === id);
if (item) {
item.completed = !item.completed;
}
}
}
@ComponentV2
struct TodoItemView {
@Param item: TodoItem = new TodoItem("");
@Event onToggle: () => void = () => {};
@Event onDelete: () => void = () => {};
build() {
Row({ space: 10 }) {
Checkbox({ select: this.item.completed })
.onChange(() => this.onToggle())
Text(this.item.title)
.decoration({ type: this.item.completed ? TextDecorationType.LineThrough : TextDecorationType.None })
.opacity(this.item.completed ? 0.5 : 1)
.layoutWeight(1)
Button('Delete')
.onClick(() => this.onDelete())
}
.padding(10)
.width('100%')
}
}
@Entry
@ComponentV2
struct TodoApp {
@Local store: TodoStore = new TodoStore();
@Local inputText: string = "";
@Computed
get completedCount(): number {
return this.store.items.filter(item => item.completed).length;
}
@Computed
get totalCount(): number {
return this.store.items.length;
}
build() {
Column({ space: 15 }) {
Text('Todo List').fontSize(30).fontWeight(FontWeight.Bold)
Text(`${this.completedCount} / ${this.totalCount} completed`)
Row({ space: 10 }) {
TextInput({ text: this.inputText, placeholder: 'New task...' })
.layoutWeight(1)
.onChange((text: string) => {
this.inputText = text;
})
Button('Add')
.enabled(this.inputText.trim().length > 0)
.onClick(() => {
if (this.inputText.trim()) {
this.store.addItem(this.inputText.trim());
this.inputText = "";
}
})
}
List({ space: 5 }) {
ForEach(this.store.items, (item: TodoItem) => {
ListItem() {
TodoItemView({
item: item,
onToggle: () => this.store.toggleItem(item.id),
onDelete: () => this.store.removeItem(item.id)
})
}
}, (item: TodoItem) => item.id)
}
.layoutWeight(1)
}
.padding(20)
.height('100%')
}
}
// ============================================
// 9. COLLECTION TYPES (Array, Map, Set)
// ============================================
@ObservedV2
class CollectionDemo {
@Trace numbers: number[] = [1, 2, 3];
@Trace userMap: Map<string, string> = new Map([['id1', 'Alice'], ['id2', 'Bob']]);
@Trace tags: Set<string> = new Set(['typescript', 'arkts', 'harmonyos']);
}
@Entry
@ComponentV2
struct CollectionsExample {
demo: CollectionDemo = new CollectionDemo();
build() {
Column({ space: 15 }) {
// Array
Text('Array:').fontWeight(FontWeight.Bold)
ForEach(this.demo.numbers, (num: number, idx: number) => {
Text(`[${idx}] = ${num}`)
}, (num: number, idx: number) => num.toString() + idx)
Button('Array.push').onClick(() => {
this.demo.numbers.push(Math.floor(Math.random() * 100));
})
Divider()
// Map
Text('Map:').fontWeight(FontWeight.Bold)
ForEach(Array.from(this.demo.userMap.entries()), (entry: [string, string]) => {
Text(`${entry[0]}: ${entry[1]}`)
}, (entry: [string, string]) => entry[0])
Button('Map.set').onClick(() => {
const id = 'id' + Date.now();
this.demo.userMap.set(id, 'New User');
})
Divider()
// Set
Text('Set:').fontWeight(FontWeight.Bold)
ForEach(Array.from(this.demo.tags.values()), (tag: string) => {
Text(`• ${tag}`)
}, (tag: string) => tag)
Button('Set.add').onClick(() => {
this.demo.tags.add('tag' + Date.now());
})
}
.padding(20)
}
}

View File

@@ -1,503 +1,510 @@
# HarmonyOS API Reference
Common HarmonyOS APIs for ArkTS development.
## Table of Contents
1. [Router Navigation](#router-navigation)
2. [HTTP Networking](#http-networking)
3. [Preferences Storage](#preferences-storage)
4. [File Operations](#file-operations)
5. [Device Info](#device-info)
6. [Prompt & Dialog](#prompt--dialog)
7. [Media](#media)
---
## Router Navigation
```typescript
import { router } from '@kit.ArkUI';
```
### Push Page
```typescript
router.pushUrl({
url: 'pages/DetailPage',
params: {
id: 123,
title: 'Detail'
}
});
```
### Push with Mode
```typescript
// Standard mode (default) - adds to stack
router.pushUrl({
url: 'pages/Page',
}, router.RouterMode.Standard);
// Single mode - reuses if exists
router.pushUrl({
url: 'pages/Page',
}, router.RouterMode.Single);
```
### Replace Page
```typescript
router.replaceUrl({
url: 'pages/NewPage'
});
```
### Back Navigation
```typescript
// Back to previous
router.back();
// Back to specific page
router.back({
url: 'pages/HomePage'
});
# HarmonyOS API Reference
Common HarmonyOS APIs for ArkTS development.
## Table of Contents
1. [Router Navigation](#router-navigation)
2. [HTTP Networking](#http-networking)
3. [Preferences Storage](#preferences-storage)
4. [File Operations](#file-operations)
5. [Device Info](#device-info)
6. [Prompt & Dialog](#prompt--dialog)
7. [Media](#media)
---
## Router Navigation
```typescript
import { router } from '@kit.ArkUI';
```
### Push Page
```typescript
router.pushUrl({
url: 'pages/DetailPage',
params: {
id: 123,
title: 'Detail'
}
});
```
### Push with Mode
```typescript
// Standard mode (default) - adds to stack
router.pushUrl({
url: 'pages/Page',
}, router.RouterMode.Standard);
// Single mode - reuses if exists
router.pushUrl({
url: 'pages/Page',
}, router.RouterMode.Single);
```
### Replace Page
```typescript
router.replaceUrl({
url: 'pages/NewPage'
});
```
### Back Navigation
```typescript
// Back to previous
router.back();
// Back to specific page
router.back({
url: 'pages/HomePage'
});
// Back with result
router.back({
url: 'pages/HomePage',
params: { result: 'success' }
});
// Note: Previous page receives params via router.getParams()
```
### Get Parameters
```typescript
// Define expected params interface
interface PageParams {
id: number;
title?: string;
}
// In target page
aboutToAppear(): void {
const params = router.getParams() as Record<string, Object>;
const params = router.getParams() as PageParams;
if (params) {
const id = params['id'] as number;
const title = params['title'] as string;
const id = params.id;
const title = params.title;
}
}
```
### Get Router State
```typescript
const state = router.getState();
console.log('Current page:', state.name);
console.log('Page path:', state.path);
console.log('Stack index:', state.index);
```
### Clear Router Stack
```typescript
router.clear();
```
---
## HTTP Networking
```typescript
import { http } from '@kit.NetworkKit';
```
### GET Request
```typescript
async function getData(): Promise<void> {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(
'https://api.example.com/data',
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
},
connectTimeout: 60000,
readTimeout: 60000
}
);
if (response.responseCode === 200) {
const data = JSON.parse(response.result as string);
console.log('Data:', JSON.stringify(data));
}
} catch (error) {
console.error('Request failed:', error);
} finally {
httpRequest.destroy();
}
}
```
### POST Request
```typescript
async function postData(body: object): Promise<void> {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(
'https://api.example.com/submit',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
extraData: JSON.stringify(body)
}
);
console.log('Response code:', response.responseCode);
} finally {
httpRequest.destroy();
}
}
```
### Request Options
```typescript
interface HttpRequestOptions {
method: http.RequestMethod; // GET, POST, PUT, DELETE, etc.
header?: Object; // Request headers
extraData?: string | Object; // Request body
connectTimeout?: number; // Connection timeout (ms)
readTimeout?: number; // Read timeout (ms)
expectDataType?: http.HttpDataType;
}
```
---
## Preferences Storage
```typescript
import { preferences } from '@kit.ArkData';
```
### Get Preferences Instance
```typescript
// In component with context
const dataPreferences = await preferences.getPreferences(
this.context,
'myPreferencesStore'
);
```
### Write Data
```typescript
await dataPreferences.put('username', 'John');
await dataPreferences.put('age', 25);
await dataPreferences.put('isVip', true);
await dataPreferences.put('scores', [90, 85, 92]);
await dataPreferences.flush(); // Persist to disk
```
### Read Data
```typescript
// With default values
const username = await dataPreferences.get('username', '') as string;
const age = await dataPreferences.get('age', 0) as number;
const isVip = await dataPreferences.get('isVip', false) as boolean;
```
### Check Key Exists
```typescript
const hasKey = await dataPreferences.has('username');
```
### Delete Data
```typescript
await dataPreferences.delete('username');
await dataPreferences.flush();
```
### Clear All
```typescript
await dataPreferences.clear();
await dataPreferences.flush();
```
### Delete Preferences File
```typescript
await preferences.deletePreferences(this.context, 'myPreferencesStore');
```
---
## File Operations
```typescript
import { fileIo as fs } from '@kit.CoreFileKit';
```
### Get Application Paths
```typescript
// In AbilityStage or UIAbility
const filesDir = this.context.filesDir; // /data/app/.../files
const cacheDir = this.context.cacheDir; // /data/app/.../cache
const tempDir = this.context.tempDir; // /data/app/.../temp
```
### Write File
```typescript
const filePath = `${this.context.filesDir}/data.txt`;
const file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
fs.writeSync(file.fd, 'Hello, HarmonyOS!');
fs.closeSync(file);
```
### Read File
```typescript
const filePath = `${this.context.filesDir}/data.txt`;
const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
const buffer = new ArrayBuffer(4096);
const readLen = fs.readSync(file.fd, buffer);
const content = String.fromCharCode(...new Uint8Array(buffer.slice(0, readLen)));
fs.closeSync(file);
```
### Check File Exists
```typescript
const exists = fs.accessSync(filePath);
```
### Delete File
```typescript
fs.unlinkSync(filePath);
```
### List Directory
```typescript
const files = fs.listFileSync(this.context.filesDir);
files.forEach((file: string) => {
console.log('File:', file);
});
```
---
## Device Info
```typescript
import { deviceInfo } from '@kit.BasicServicesKit';
```
### Get Device Information
```typescript
const brand = deviceInfo.brand; // e.g., "HUAWEI"
const model = deviceInfo.productModel; // e.g., "Mate 60"
const osVersion = deviceInfo.osFullName; // e.g., "HarmonyOS 5.0"
const sdkVersion = deviceInfo.sdkApiVersion; // e.g., 12
const deviceType = deviceInfo.deviceType; // e.g., "phone", "tablet"
```
---
## Prompt & Dialog
```typescript
import { promptAction } from '@kit.ArkUI';
```
### Toast
```typescript
promptAction.showToast({
message: 'Operation successful',
duration: 2000,
bottom: 80
});
```
### Alert Dialog
```typescript
promptAction.showDialog({
title: 'Confirm',
message: 'Are you sure you want to delete?',
buttons: [
{ text: 'Cancel', color: '#999999' },
{ text: 'Delete', color: '#FF0000' }
]
}).then((result) => {
if (result.index === 1) {
// Delete confirmed
}
});
```
### Action Sheet
```typescript
promptAction.showActionMenu({
title: 'Select Option',
buttons: [
{ text: 'Camera', color: '#000000' },
{ text: 'Gallery', color: '#000000' },
{ text: 'Cancel', color: '#999999' }
]
}).then((result) => {
switch (result.index) {
case 0: // Camera
break;
case 1: // Gallery
break;
}
});
```
### Custom Dialog
```typescript
@CustomDialog
struct ConfirmDialog {
controller: CustomDialogController;
title: string = '';
onConfirm: () => void = () => {};
build() {
Column() {
Text(this.title).fontSize(20).margin({ bottom: 20 })
Row({ space: 20 }) {
Button('Cancel')
.onClick(() => { this.controller.close(); })
Button('Confirm')
.onClick(() => {
this.onConfirm();
this.controller.close();
})
}
}
.padding(20)
}
}
// Usage in component
@Entry
@Component
struct DialogExample {
dialogController: CustomDialogController = new CustomDialogController({
builder: ConfirmDialog({
title: 'Delete Item?',
onConfirm: () => { this.handleDelete(); }
}),
autoCancel: true
});
handleDelete(): void {
// Delete logic
}
build() {
Button('Show Dialog')
.onClick(() => { this.dialogController.open(); })
}
}
```
---
## Media
### Image Picker
```typescript
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { picker } from '@kit.CoreFileKit';
async function pickImage(): Promise<string | null> {
const photoPicker = new picker.PhotoViewPicker();
try {
const result = await photoPicker.select({
MIMEType: picker.PhotoViewMIMETypes.IMAGE_TYPE,
maxSelectNumber: 1
});
if (result.photoUris.length > 0) {
return result.photoUris[0];
}
} catch (error) {
console.error('Pick image failed:', error);
}
return null;
}
```
### Camera Capture
```typescript
import { camera } from '@kit.CameraKit';
// Request camera permission first
// Then use camera APIs for capture
```
### Audio Playback
```typescript
import { media } from '@kit.MediaKit';
async function playAudio(uri: string): Promise<void> {
const player = await media.createAVPlayer();
player.on('stateChange', (state: string) => {
if (state === 'prepared') {
player.play();
}
});
player.url = uri;
}
```
---
## Common Import Patterns
```typescript
// UI Kit
import { router, promptAction } from '@kit.ArkUI';
// Network Kit
import { http } from '@kit.NetworkKit';
// Data Kit
import { preferences } from '@kit.ArkData';
// File Kit
import { fileIo as fs, picker } from '@kit.CoreFileKit';
// Basic Services
import { deviceInfo } from '@kit.BasicServicesKit';
// Media Kit
import { media } from '@kit.MediaKit';
```
```
### Get Router State
```typescript
const state = router.getState();
console.log('Current page:', state.name);
console.log('Page path:', state.path);
console.log('Stack index:', state.index);
```
### Clear Router Stack
```typescript
router.clear();
```
---
## HTTP Networking
```typescript
import { http } from '@kit.NetworkKit';
```
### GET Request
```typescript
async function getData(): Promise<void> {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(
'https://api.example.com/data',
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
},
connectTimeout: 60000,
readTimeout: 60000
}
);
if (response.responseCode === 200) {
const data = JSON.parse(response.result as string);
console.log('Data:', JSON.stringify(data));
}
} catch (error) {
console.error('Request failed:', error);
} finally {
httpRequest.destroy();
}
}
```
### POST Request
```typescript
async function postData(body: object): Promise<void> {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(
'https://api.example.com/submit',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
extraData: JSON.stringify(body)
}
);
console.log('Response code:', response.responseCode);
} finally {
httpRequest.destroy();
}
}
```
### Request Options
```typescript
interface HttpRequestOptions {
method: http.RequestMethod; // GET, POST, PUT, DELETE, etc.
header?: Object; // Request headers
extraData?: string | Object; // Request body
connectTimeout?: number; // Connection timeout (ms)
readTimeout?: number; // Read timeout (ms)
expectDataType?: http.HttpDataType;
}
```
---
## Preferences Storage
```typescript
import { preferences } from '@kit.ArkData';
```
### Get Preferences Instance
```typescript
// In component with context
const dataPreferences = await preferences.getPreferences(
this.context,
'myPreferencesStore'
);
```
### Write Data
```typescript
await dataPreferences.put('username', 'John');
await dataPreferences.put('age', 25);
await dataPreferences.put('isVip', true);
await dataPreferences.put('scores', [90, 85, 92]);
await dataPreferences.flush(); // Persist to disk
```
### Read Data
```typescript
// With default values
const username = await dataPreferences.get('username', '') as string;
const age = await dataPreferences.get('age', 0) as number;
const isVip = await dataPreferences.get('isVip', false) as boolean;
```
### Check Key Exists
```typescript
const hasKey = await dataPreferences.has('username');
```
### Delete Data
```typescript
await dataPreferences.delete('username');
await dataPreferences.flush();
```
### Clear All
```typescript
await dataPreferences.clear();
await dataPreferences.flush();
```
### Delete Preferences File
```typescript
await preferences.deletePreferences(this.context, 'myPreferencesStore');
```
---
## File Operations
```typescript
import { fileIo as fs } from '@kit.CoreFileKit';
```
### Get Application Paths
```typescript
// In AbilityStage or UIAbility
const filesDir = this.context.filesDir; // /data/app/.../files
const cacheDir = this.context.cacheDir; // /data/app/.../cache
const tempDir = this.context.tempDir; // /data/app/.../temp
```
### Write File
```typescript
const filePath = `${this.context.filesDir}/data.txt`;
const file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
fs.writeSync(file.fd, 'Hello, HarmonyOS!');
fs.closeSync(file);
```
### Read File
```typescript
const filePath = `${this.context.filesDir}/data.txt`;
const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
const buffer = new ArrayBuffer(4096);
const readLen = fs.readSync(file.fd, buffer);
const content = String.fromCharCode(...new Uint8Array(buffer.slice(0, readLen)));
fs.closeSync(file);
```
### Check File Exists
```typescript
const exists = fs.accessSync(filePath);
```
### Delete File
```typescript
fs.unlinkSync(filePath);
```
### List Directory
```typescript
const files = fs.listFileSync(this.context.filesDir);
files.forEach((file: string) => {
console.log('File:', file);
});
```
---
## Device Info
```typescript
import { deviceInfo } from '@kit.BasicServicesKit';
```
### Get Device Information
```typescript
const brand = deviceInfo.brand; // e.g., "HUAWEI"
const model = deviceInfo.productModel; // e.g., "Mate 60"
const osVersion = deviceInfo.osFullName; // e.g., "HarmonyOS 5.0"
const sdkVersion = deviceInfo.sdkApiVersion; // e.g., 12
const deviceType = deviceInfo.deviceType; // e.g., "phone", "tablet"
```
---
## Prompt & Dialog
```typescript
import { promptAction } from '@kit.ArkUI';
```
### Toast
```typescript
promptAction.showToast({
message: 'Operation successful',
duration: 2000,
bottom: 80
});
```
### Alert Dialog
```typescript
promptAction.showDialog({
title: 'Confirm',
message: 'Are you sure you want to delete?',
buttons: [
{ text: 'Cancel', color: '#999999' },
{ text: 'Delete', color: '#FF0000' }
]
}).then((result) => {
if (result.index === 1) {
// Delete confirmed
}
});
```
### Action Sheet
```typescript
promptAction.showActionMenu({
title: 'Select Option',
buttons: [
{ text: 'Camera', color: '#000000' },
{ text: 'Gallery', color: '#000000' },
{ text: 'Cancel', color: '#999999' }
]
}).then((result) => {
switch (result.index) {
case 0: // Camera
break;
case 1: // Gallery
break;
}
});
```
### Custom Dialog
```typescript
@CustomDialog
struct ConfirmDialog {
controller: CustomDialogController;
title: string = '';
onConfirm: () => void = () => {};
build() {
Column() {
Text(this.title).fontSize(20).margin({ bottom: 20 })
Row({ space: 20 }) {
Button('Cancel')
.onClick(() => { this.controller.close(); })
Button('Confirm')
.onClick(() => {
this.onConfirm();
this.controller.close();
})
}
}
.padding(20)
}
}
// Usage in component
@Entry
@Component
struct DialogExample {
dialogController: CustomDialogController = new CustomDialogController({
builder: ConfirmDialog({
title: 'Delete Item?',
onConfirm: () => { this.handleDelete(); }
}),
autoCancel: true
});
handleDelete(): void {
// Delete logic
}
build() {
Button('Show Dialog')
.onClick(() => { this.dialogController.open(); })
}
}
```
---
## Media
### Image Picker
```typescript
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { picker } from '@kit.CoreFileKit';
async function pickImage(): Promise<string | null> {
const photoPicker = new picker.PhotoViewPicker();
try {
const result = await photoPicker.select({
MIMEType: picker.PhotoViewMIMETypes.IMAGE_TYPE,
maxSelectNumber: 1
});
if (result.photoUris.length > 0) {
return result.photoUris[0];
}
} catch (error) {
console.error('Pick image failed:', error);
}
return null;
}
```
### Camera Capture
```typescript
import { camera } from '@kit.CameraKit';
// Request camera permission first
// Then use camera APIs for capture
```
### Audio Playback
```typescript
import { media } from '@kit.MediaKit';
async function playAudio(uri: string): Promise<void> {
const player = await media.createAVPlayer();
player.on('stateChange', (state: string) => {
if (state === 'prepared') {
player.play();
}
});
player.url = uri;
}
```
---
## Common Import Patterns
```typescript
// UI Kit
import { router, promptAction } from '@kit.ArkUI';
// Network Kit
import { http } from '@kit.NetworkKit';
// Data Kit
import { preferences } from '@kit.ArkData';
// File Kit
import { fileIo as fs, picker } from '@kit.CoreFileKit';
// Basic Services
import { deviceInfo } from '@kit.BasicServicesKit';
// Media Kit
import { media } from '@kit.MediaKit';
```

View File

@@ -0,0 +1,126 @@
# ArkGuard Code Obfuscation Guide
ArkGuard is the officially recommended code obfuscation tool for HarmonyOS, designed to enhance application security and prevent reverse engineering.
## Requirements
- **DevEco Studio**: Version 5.0.3.600 or above
- **Project Model**: Stage model only
- **Effective Mode**: Only active in Release mode
## Enabling Obfuscation
Configure in the module's `build-profile.json5`:
```json
{
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"files": ["./obfuscation-rules.txt"]
},
"consumerFiles": ["./consumer-rules.txt"]
}
}
}
```
## Obfuscation Rules Configuration
Create `obfuscation-rules.txt` in the project root directory:
```text
# Enable property obfuscation
-enable-property-obfuscation
# Enable top-level scope name obfuscation
-enable-toplevel-obfuscation
# Enable filename obfuscation
-enable-filename-obfuscation
# Enable import/export name obfuscation
-enable-export-obfuscation
```
## Whitelist Configuration
Certain names must not be obfuscated (e.g., dynamic property names, API fields, database fields):
```text
# Keep property names
-keep-property-name apiKey
-keep-property-name userId
-keep-property-name responseData
# Keep global names
-keep-global-name AppConfig
# Keep file names
-keep-file-name MainPage
-keep-file-name LoginPage
```
## Configuration Files
| Config File | Purpose | Editable | Scope |
|-------------|---------|:--------:|-------|
| `obfuscation-rules.txt` | Obfuscation rules applied when building this module | ✓ | Current module |
| `consumer-rules.txt` | Obfuscation rules applied when this module is used as a dependency (recommended: keep rules only) | ✓ | Modules depending on this module |
| `obfuscation.txt` | HAR/HSP build artifact, auto-generated | ✗ | Dependent modules |
## Common Obfuscation Options
| Option | Description |
|--------|-------------|
| `-enable-property-obfuscation` | Obfuscate object property names |
| `-enable-toplevel-obfuscation` | Obfuscate top-level scope variable and function names |
| `-enable-filename-obfuscation` | Obfuscate file names |
| `-enable-export-obfuscation` | Obfuscate import/export names |
| `-disable-obfuscation` | Temporarily disable obfuscation (for debugging) |
## Whitelist Options
| Option | Description |
|--------|-------------|
| `-keep-property-name <name>` | Preserve specified property name from obfuscation |
| `-keep-global-name <name>` | Preserve specified global name from obfuscation |
| `-keep-file-name <name>` | Preserve specified file name from obfuscation |
## Troubleshooting
### Diagnostic Steps
1. **Confirm obfuscation is the cause**: Temporarily add `-disable-obfuscation` and check if the issue disappears
2. **Locate the problematic field**: Identify the obfuscated field from crash logs
3. **Add to whitelist**: Add the problematic field to the `-keep-property-name` whitelist
### Common Scenarios Requiring Whitelisting
- **Network requests**: Request parameter field names, response data field names
- **Database operations**: Table field names
- **System APIs**: System callback parameters
- **Third-party library interfaces**: Field names required by third-party libraries
### Example: Preserving Network Request Fields
```text
# API request/response fields
-keep-property-name code
-keep-property-name message
-keep-property-name data
-keep-property-name token
-keep-property-name userId
```
## Verifying Obfuscation Results
1. Switch to **Release** mode and build
2. Inspect the build artifacts
3. Use decompilation tools to verify that class/method/property names are obfuscated
4. Test that the application functions correctly
## References
- [Huawei Official Documentation - ArkGuard](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-arkguard)

View File

@@ -1,103 +1,103 @@
# CodeLinter 代码检查工具
# CodeLinter Code Analysis Tool
codelinter 是 HarmonyOS 的代码检查与修复工具,可集成到门禁或 CI/CD 环境中。
codelinter is a code analysis and auto-fix tool for HarmonyOS, suitable for integration into gating checks or CI/CD pipelines.
## 命令格式
## Command Format
```bash
codelinter [options] [dir]
```
- `options`: 可选配置参数
- `dir`: 待检查的工程根目录(可选,默认为当前目录)
- `options`: Optional configuration parameters
- `dir`: Project root directory to check (optional, defaults to current directory)
## 命令参数
## Command Parameters
| 参数 | 说明 |
|------|------|
| `--config, -c <filepath>` | 指定规则配置文件 (code-linter.json5) |
| `--fix` | 检查同时执行自动修复 |
| `--format, -f <format>` | 输出格式: `default`/`json`/`xml`/`html` |
| `--output, -o <filepath>` | 指定结果保存位置(不在命令行显示) |
| `--version, -v` | 查看版本 |
| `--product, -p <productName>` | 指定生效的 product |
| `--incremental, -i` | 仅检查 Git 增量文件(新增/修改/重命名) |
| `--help, -h` | 查询帮助 |
| `--exit-on, -e <levels>` | 指定返回非零退出码的告警级别 |
| Parameter | Description |
|-----------|-------------|
| `--config, -c <filepath>` | Specify rule configuration file (code-linter.json5) |
| `--fix` | Check and apply auto-fixes simultaneously |
| `--format, -f <format>` | Output format: `default`/`json`/`xml`/`html` |
| `--output, -o <filepath>` | Specify output file path (suppresses console output) |
| `--version, -v` | Show version |
| `--product, -p <productName>` | Specify the active product |
| `--incremental, -i` | Check only Git incremental files (added/modified/renamed) |
| `--help, -h` | Show help |
| `--exit-on, -e <levels>` | Specify warning levels that trigger a non-zero exit code |
## 基本用法
## Basic Usage
### 在工程根目录下执行
### Run in the Project Root Directory
```bash
# 使用默认规则检查当前工程
# Check current project with default rules
codelinter
# 指定规则配置文件
# Specify a rule configuration file
codelinter -c ./code-linter.json5
# 检查并自动修复
# Check and apply auto-fixes
codelinter -c ./code-linter.json5 --fix
```
### 在非工程目录下执行
### Run Outside the Project Directory
```bash
# 检查指定工程目录
# Check a specific project directory
codelinter /path/to/project
# 检查多个目录或文件
# Check multiple directories or files
codelinter dir1 dir2 file1.ets
# 指定规则文件和工程目录
# Specify rule file and project directory
codelinter -c /path/to/code-linter.json5 /path/to/project
# 检查并修复指定工程
# Check and fix a specific project
codelinter -c ./code-linter.json5 /path/to/project --fix
```
## 输出格式
## Output Formats
```bash
# 默认文本格式输出到命令行
# Default text format to console
codelinter /path/to/project
# JSON 格式输出
# JSON format output
codelinter /path/to/project -f json
# HTML 格式保存到文件
# HTML format saved to file
codelinter /path/to/project -f html -o ./report.html
# XML 格式保存到文件
# XML format saved to file
codelinter /path/to/project -f xml -o ./report.xml
```
## 增量检查
## Incremental Checking
对 Git 工程中的增量文件执行检查(仅检查新增、修改、重命名的文件):
Check only incremental files in a Git project (only added, modified, or renamed files):
```bash
codelinter -i
codelinter --incremental
```
## 指定 Product
## Specifying a Product
当工程存在多个 product 时,指定生效的 product
When the project has multiple products, specify the active product:
```bash
codelinter -p free /path/to/project
codelinter --product default
```
## 退出码 (--exit-on)
## Exit Codes (--exit-on)
用于 CI/CD 中根据告警级别控制流程。告警级别:`error``warn``suggestion`
Used in CI/CD to control the pipeline based on warning levels. Warning levels: `error`, `warn`, `suggestion`
退出码计算方式3位二进制数从高到低表示 error, warn, suggestion
Exit code calculation (3-bit binary number, from high to low representing error, warn, suggestion):
| 配置 | 检查结果包含 | 二进制 | 退出码 |
|------|-------------|--------|--------|
| Configuration | Check Results Include | Binary | Exit Code |
|---------------|---------------------|--------|-----------|
| `--exit-on error` | error, warn, suggestion | 100 | 4 |
| `--exit-on error` | warn, suggestion | 000 | 0 |
| `--exit-on error,warn` | error, warn | 110 | 6 |
@@ -105,40 +105,40 @@ codelinter --product default
| `--exit-on error,warn,suggestion` | error, warn, suggestion | 111 | 7 |
```bash
# 仅 error 级别返回非零退出码
# Non-zero exit code only for error level
codelinter --exit-on error
# error warn 级别返回非零退出码
# Non-zero exit code for error and warn levels
codelinter --exit-on error,warn
# 所有级别都返回非零退出码
# Non-zero exit code for all levels
codelinter --exit-on error,warn,suggestion
```
## CI/CD 集成示例
## CI/CD Integration Examples
```bash
# 完整的 CI 检查流程
# Full CI check pipeline
codelinter -c ./code-linter.json5 \
-f json \
-o ./codelinter-report.json \
--exit-on error,warn
# 增量检查(仅检查变更文件)
# Incremental check (changed files only)
codelinter -i -c ./code-linter.json5 --exit-on error
# 检查并自动修复,生成 HTML 报告
# Check with auto-fix, generate HTML report
codelinter -c ./code-linter.json5 \
--fix \
-f html \
-o ./codelinter-report.html
```
## 规则配置文件 (code-linter.json5)
## Rule Configuration File (code-linter.json5)
默认规则清单可在检查完成后,根据命令行提示查看生成的 `code-linter.json5` 文件。
The default rule list can be viewed in the generated `code-linter.json5` file, as indicated by the console output after a check completes.
示例配置:
Example configuration:
```json5
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,92 +1,92 @@
# 堆栈解析工具 (hstack)
# Stack Trace Analysis Tool (hstack)
hstack 是用于将 Release 应用混淆后的 crash 堆栈解析为源码对应堆栈的工具,支持 WindowsMac、Linux 三个平台。
hstack is a tool for resolving obfuscated crash stack traces from Release builds back to their original source code locations. It supports Windows, Mac, and Linux.
## 命令格式
## Command Format
```bash
hstack [options]
```
## 命令参数
## Command Parameters
| 参数 | 说明 |
|------|------|
| `-i, --input` | 指定 crash 文件归档目录 |
| `-c, --crash` | 指定一条 crash 堆栈 |
| `-o, --output` | 指定解析结果输出目录(使用 `-c` 时指定输出文件) |
| `-s, --sourcemapDir` | 指定 sourcemap 文件归档目录 |
| `--so, --soDir` | 指定 shared object (.so) 文件归档目录 |
| `-n, --nameObfuscation` | 指定 nameCache 文件归档目录 |
| `-v, --version` | 查看版本 |
| `-h, --help` | 查询帮助 |
| Parameter | Description |
|-----------|-------------|
| `-i, --input` | Specify the crash file archive directory |
| `-c, --crash` | Specify a single crash stack trace |
| `-o, --output` | Specify the output directory for parsed results (output file when using `-c`) |
| `-s, --sourcemapDir` | Specify the sourcemap file archive directory |
| `--so, --soDir` | Specify the shared object (.so) file archive directory |
| `-n, --nameObfuscation` | Specify the nameCache file archive directory |
| `-v, --version` | Show version |
| `-h, --help` | Show help |
## 参数约束
## Parameter Constraints
- crash 文件目录 (`-i`) crash 堆栈 (`-c`) **必须且只能提供一项**
- sourcemap (`-s`) shared object (`--so`) 目录**至少提供一项**
- 如需还原混淆的方法名,需**同时提供** sourcemap nameCache 文件
- 路径参数不支持特殊字符:`` `~!@#$^&*=|{};,\s\[\]<>? ``
- Crash file directory (`-i`) and crash stack trace (`-c`) **must provide exactly one**
- Sourcemap (`-s`) and shared object (`--so`) directories **must provide at least one**
- To restore obfuscated method names, **both** sourcemap and nameCache files must be provided
- Path parameters do not support special characters: `` `~!@#$^&*=|{};,\s\[\]<>? ``
## 环境配置
## Environment Setup
1. Command Line Tools `bin` 目录配置到 PATH 环境变量
2. 配置 Node.js 到环境变量
3. 解析 C++ 异常需配置 SDK `native\llvm\bin` 目录到环境变量 `ADDR2LINE_PATH`
1. Add the Command Line Tools `bin` directory to the PATH environment variable
2. Add Node.js to the environment variables
3. To parse C++ exceptions, add the SDK's `native\llvm\bin` directory to the `ADDR2LINE_PATH` environment variable
## 使用示例
## Usage Examples
### 解析 crash 文件目录
### Parse Crash File Directory
```bash
# 完整解析命令
# Full parse command
hstack -i crashDir -o outputDir -s sourcemapDir --so soDir -n nameCacheDir
# 仅使用 sourcemap 解析 (ArkTS)
# Parse using sourcemap only (ArkTS)
hstack -i crashDir -o outputDir -s sourcemapDir
# 仅使用 so 文件解析 (C++)
# Parse using .so files only (C++)
hstack -i crashDir -o outputDir --so soDir
# 包含方法名还原
# Include method name restoration
hstack -i crashDir -o outputDir -s sourcemapDir -n nameCacheDir
```
### 解析单条堆栈
### Parse a Single Stack Trace
```bash
# 输出到控制台
# Output to console
hstack -c "at har (entry|har|1.0.0|src/main/ets/pages/Index.ts:58:58)" -s sourcemapDir
# 输出到文件
# Output to file
hstack -c "at har (entry|har|1.0.0|src/main/ets/pages/Index.ts:58:58)" -s sourcemapDir -o result.txt
```
## 输出说明
## Output
- 解析结果输出到 `-o` 指定目录,文件以原始 crash 文件名加 `_` 前缀命名
- 不指定 `-o` 时:
- 使用 `-i` 输入:输出到 crashDir 目录
- 使用 `-c` 输入:直接输出到控制台
- Parsed results are written to the directory specified by `-o`, with filenames prefixed by `_` followed by the original crash filename
- When `-o` is not specified:
- With `-i` input: output to the crashDir directory
- With `-c` input: output directly to console
## 文件获取
## File Sources
### Sourcemap 文件
### Sourcemap Files
构建产物中的 sourcemap 文件,包含:
- 路径信息映射
- 行列号映射 (mappings 字段)
- package-info 信息
Sourcemap files from build artifacts, containing:
- Path information mapping
- Line/column number mapping (mappings field)
- package-info information
### NameCache 文件
### NameCache Files
构建产物中的 nameCache 文件,包含:
- `IdentifierCache`: 标识符混淆映射
- `MemberMethodCache`: 成员方法混淆映射,格式为 `"源码方法名:起始行:结束行": "混淆后方法名"`
NameCache files from build artifacts, containing:
- `IdentifierCache`: Identifier obfuscation mapping
- `MemberMethodCache`: Member method obfuscation mapping, format: `"sourceMethodName:startLine:endLine": "obfuscatedMethodName"`
### Shared Object (.so) 文件
### Shared Object (.so) Files
构建 Release 应用时,默认 so 文件不包含符号表。如需生成包含符号表的 so 文件,在模块 `build-profile.json5` 中配置:
When building Release applications, .so files do not include symbol tables by default. To generate .so files with symbol tables, configure in the module's `build-profile.json5`:
```json5
{
@@ -98,46 +98,46 @@ hstack -c "at har (entry|har|1.0.0|src/main/ets/pages/Index.ts:58:58)" -s source
}
```
## 堆栈解析原理
## Stack Trace Resolution Principles
### Crash 堆栈格式
### Crash Stack Format
```
at har (entry|har|1.0.0|src/main/ets/components/mainpage/MainPage.js:58:58)
at i (entry|entry|1.0.0|src/main/ets/pages/Index.ts:71:71)
```
路径格式:`引用方packageName|被引用方packageName|version|源码相对路径`
Path format: `referrerPackageName|referredPackageName|version|sourceRelativePath`
### 解析步骤
### Resolution Steps
1. **根据路径信息找到 sourcemap**
- 从路径 `entry|har|1.0.0|src/main/ets/...` 在 entry 模块 sourcemap 中查找对应字段
1. **Find the sourcemap based on path information**
- From the path `entry|har|1.0.0|src/main/ets/...`, look up the corresponding field in the entry module's sourcemap
2. **利用 sourcemap 还原路径和行列号**
- 根据 `sources` `mappings` 字段解析
- 如包含 `package-info`,可进行二次解析获取更准确的源码位置
2. **Restore path and line/column numbers using sourcemap**
- Parse using the `sources` and `mappings` fields
- If `package-info` is included, perform a secondary parse for more accurate source locations
3. **利用 nameCache 还原方法名**
- 查找混淆后方法名对应的所有条目
- 根据还原后的行号范围匹配正确的源码方法名
3. **Restore method names using nameCache**
- Find all entries matching the obfuscated method name
- Match the correct source method name based on the restored line number range
### 解析示例
### Resolution Example
原始堆栈:
Original stack trace:
```
at i (entry|entry|1.0.0|src/main/ets/pages/Index.ts:71:71)
```
还原后:
After resolution:
```
at callHarFunction (entry/src/main/ets/pages/Index.ets:25:3)
```
## CI/CD 集成
## CI/CD Integration
```bash
# 自动化解析脚本示例
# Automated parsing script example
hstack \
-i ./crash-logs \
-o ./parsed-logs \
@@ -146,8 +146,8 @@ hstack \
-n ./build/nameCache
```
## 常见问题
## FAQ
1. **方法名未还原**: 确保同时提供 `-s` `-n` 参数
2. **C++ 堆栈未解析**: 检查 `ADDR2LINE_PATH` 环境变量配置
3. **so 文件无符号表**: 配置 `RelWithDebInfo` 构建选项
1. **Method names not restored**: Ensure both `-s` and `-n` parameters are provided
2. **C++ stack traces not parsed**: Check the `ADDR2LINE_PATH` environment variable configuration
3. **No symbol table in .so files**: Configure the `RelWithDebInfo` build option

View File

@@ -1,179 +1,179 @@
# Hvigor 命令行构建工具 (hvigorw)
# Hvigor Command-Line Build Tool (hvigorw)
hvigorw Hvigor wrapper 包装工具,支持自动安装 Hvigor 构建工具和相关插件依赖,以及执行 Hvigor 构建命令。
hvigorw is the Hvigor wrapper tool that supports automatic installation of the Hvigor build tool and its plugin dependencies, as well as executing Hvigor build commands.
## 命令格式
## Command Format
```bash
hvigorw [taskNames...] <options>
```
## 编译构建任务
## Build Tasks
| 任务 | 说明 |
|------|------|
| `clean` | 清理构建产物 build 目录 |
| `assembleHap` | 构建 Hap 应用 |
| `assembleApp` | 构建 App 应用 |
| `assembleHsp` | 构建 Hsp 包 |
| `assembleHar` | 构建 Har 包 |
| `collectCoverage` | 基于打点数据生成覆盖率统计报表 |
| Task | Description |
|------|-------------|
| `clean` | Clean build artifacts in the build directory |
| `assembleHap` | Build HAP application |
| `assembleApp` | Build APP application |
| `assembleHsp` | Build HSP package |
| `assembleHar` | Build HAR package |
| `collectCoverage` | Generate coverage statistics report from instrumented data |
## 常用构建参数
## Common Build Parameters
| 参数 | 说明 |
|------|------|
| `-p buildMode={debug\|release}` | 指定构建模式。默认Hap/Hsp/Har 为 debugApp 为 release |
| `-p debuggable=true/false` | 覆盖 buildOption 中的 debuggable 配置 |
| `-p product={ProductName}` | 指定 product 进行编译,默认为 default |
| `-p module={ModuleName}@{TargetName}` | 指定模块及 target 编译(需配合 `--mode module` |
| `-p ohos-test-coverage={true\|false}` | 执行测试框架代码覆盖率插桩编译 |
| `-p parameterFile=param.json` | 设置 oh-package.json5 的参数配置文件 |
| Parameter | Description |
|-----------|-------------|
| `-p buildMode={debug\|release}` | Specify build mode. Default: debug for Hap/Hsp/Har, release for App |
| `-p debuggable=true/false` | Override the debuggable setting in buildOption |
| `-p product={ProductName}` | Specify product for compilation, defaults to default |
| `-p module={ModuleName}@{TargetName}` | Specify module and target for compilation (requires `--mode module`) |
| `-p ohos-test-coverage={true\|false}` | Enable test framework code coverage instrumentation |
| `-p parameterFile=param.json` | Set parameter configuration file for oh-package.json5 |
## 构建示例
## Build Examples
```bash
# 清理构建产物
# Clean build artifacts
hvigorw clean
# Debug 模式构建 Hap
# Build HAP in debug mode
hvigorw assembleHap -p buildMode=debug
# Release 模式构建 App
# Build APP in release mode
hvigorw assembleApp -p buildMode=release
# 构建指定 product
# Build a specific product
hvigorw assembleHap -p product=free
# 构建指定模块
# Build a specific module
hvigorw assembleHap -p module=entry@default --mode module
# 构建多个模块
# Build multiple modules
hvigorw assembleHar -p module=library1@default,library2@default --mode module
```
## 测试命令
## Test Commands
### Instrument Test (设备测试)
### Instrument Test (On-Device Test)
```bash
hvigorw onDeviceTest -p module={moduleName} -p coverage={true|false} -p scope={suiteName}#{methodName}
```
- `module`: 执行测试的模块,缺省执行所有模块
- `coverage`: 是否生成覆盖率报告,默认 true
- `scope`: 测试范围,格式 `{suiteName}#{methodName}` `{suiteName}`
- `ohos-debug-asan`: 是否启用 ASan 检测,默认 false (5.19.0+)
- `module`: Module to test; omit to test all modules
- `coverage`: Whether to generate coverage report, defaults to true
- `scope`: Test scope, format `{suiteName}#{methodName}` or `{suiteName}`
- `ohos-debug-asan`: Whether to enable ASan detection, defaults to false (5.19.0+)
**输出路径:**
- 覆盖率报告: `<module-path>/.test/default/outputs/ohosTest/reports`
- 测试结果: `<project>/<module>/.test/default/intermediates/ohosTest/coverage_data/test_result.txt`
**Output paths:**
- Coverage report: `<module-path>/.test/default/outputs/ohosTest/reports`
- Test results: `<project>/<module>/.test/default/intermediates/ohosTest/coverage_data/test_result.txt`
### Local Test (本地测试)
### Local Test
```bash
hvigorw test -p module={moduleName} -p coverage={true|false} -p scope={suiteName}#{methodName}
```
**输出路径:**
- 覆盖率报告: `<module-path>/.test/default/outputs/test/reports`
- 测试结果: `<project>/<module>/.test/default/intermediates/test/coverage_data/test_result.txt`
**Output paths:**
- Coverage report: `<module-path>/.test/default/outputs/test/reports`
- Test results: `<project>/<module>/.test/default/intermediates/test/coverage_data/test_result.txt`
## 日志级别
## Log Levels
| 参数 | 说明 |
|------|------|
| `-e, --error` | 设置日志级别为 error |
| `-w, --warn` | 设置日志级别为 warn |
| `-i, --info` | 设置日志级别为 info |
| `-d, --debug` | 设置日志级别为 debug |
| `--stacktrace` | 开启打印异常堆栈信息 |
| Parameter | Description |
|-----------|-------------|
| `-e, --error` | Set log level to error |
| `-w, --warn` | Set log level to warn |
| `-i, --info` | Set log level to info |
| `-d, --debug` | Set log level to debug |
| `--stacktrace` | Enable exception stack trace printing |
## 构建分析 (Build Analyzer)
## Build Analyzer
| 参数 | 说明 |
|------|------|
| `--analyze=normal` | 普通模式分析 |
| `--analyze=advanced` | 进阶模式,更详细的任务耗时数据 |
| `--analyze=ultrafine` | 超精细化模式ArkTS 编译详细打点 (6.0.0+) |
| `--analyze=false` | 不启用构建分析 |
| `--config properties.hvigor.analyzeHtml=true` | 生成 HTML 可视化报告到 `.hvigor/report` |
| Parameter | Description |
|-----------|-------------|
| `--analyze=normal` | Normal mode analysis |
| `--analyze=advanced` | Advanced mode with detailed task timing data |
| `--analyze=ultrafine` | Ultra-fine mode with detailed ArkTS compilation instrumentation (6.0.0+) |
| `--analyze=false` | Disable build analysis |
| `--config properties.hvigor.analyzeHtml=true` | Generate HTML visual report to `.hvigor/report` |
## 守护进程 (Daemon)
## Daemon
| 参数 | 说明 |
|------|------|
| `--daemon` | 启用守护进程 |
| `--no-daemon` | 关闭守护进程(命令行模式推荐) |
| `--stop-daemon` | 关闭当前工程的守护进程 |
| `--stop-daemon-all` | 关闭所有工程的守护进程 |
| `--status-daemon` | 查询所有 Hvigor 守护进程信息 |
| `--max-old-space-size=12345` | 设置老生代内存大小 (MB) |
| `--max-semi-space-size=32` | 设置新生代半空间大小 (MB, 5.18.4+) |
| Parameter | Description |
|-----------|-------------|
| `--daemon` | Enable daemon process |
| `--no-daemon` | Disable daemon process (recommended for CLI mode) |
| `--stop-daemon` | Stop the daemon for the current project |
| `--stop-daemon-all` | Stop all project daemons |
| `--status-daemon` | Query all Hvigor daemon process information |
| `--max-old-space-size=12345` | Set old generation memory size (MB) |
| `--max-semi-space-size=32` | Set new generation semi-space size (MB, 5.18.4+) |
## 性能与内存优化
## Performance and Memory Optimization
| 参数 | 说明 |
|------|------|
| `--parallel` / `--no-parallel` | 开启/关闭并行构建(默认开启) |
| `--incremental` / `--no-incremental` | 开启/关闭增量构建(默认开启) |
| `--optimization-strategy=performance` | 性能优先模式,加快构建但占用更多内存 (5.19.2+) |
| `--optimization-strategy=memory` | 内存优先模式(默认)(5.19.2+) |
| Parameter | Description |
|-----------|-------------|
| `--parallel` / `--no-parallel` | Enable/disable parallel builds (enabled by default) |
| `--incremental` / `--no-incremental` | Enable/disable incremental builds (enabled by default) |
| `--optimization-strategy=performance` | Performance-first mode, faster builds but higher memory usage (5.19.2+) |
| `--optimization-strategy=memory` | Memory-first mode (default) (5.19.2+) |
## 公共命令
## Utility Commands
| 任务 | 说明 |
|------|------|
| `tasks` | 打印工程各模块包含的任务信息 |
| `taskTree` | 打印工程各模块的任务依赖关系 |
| `prune` | 清除 30 天未使用的缓存并删除 pnpm 未引用包 |
| `buildInfo` | 打印 build-profile.json5 配置信息 (5.18.4+) |
| Task | Description |
|------|-------------|
| `tasks` | Print task information for all project modules |
| `taskTree` | Print task dependency graph for all project modules |
| `prune` | Clean caches unused for 30 days and remove unreferenced pnpm packages |
| `buildInfo` | Print build-profile.json5 configuration information (5.18.4+) |
### buildInfo 扩展参数
### buildInfo Extended Parameters
```bash
# 打印工程级配置
# Print project-level configuration
hvigorw buildInfo
# 打印指定模块配置
# Print configuration for a specific module
hvigorw buildInfo -p module=entry
# 包含 buildOption 配置
# Include buildOption configuration
hvigorw buildInfo -p buildOption
# JSON 格式输出
# JSON format output
hvigorw buildInfo -p json
```
## 其他参数
## Other Parameters
| 参数 | 说明 |
|------|------|
| `-h, --help` | 打印帮助信息 |
| `-v, --version` | 打印版本信息 |
| `-s, --sync` | 同步工程信息到 `./hvigor/outputs/sync/output.json` |
| `-m, --mode` | 指定执行目录级别 (如 `-m project`) |
| `--type-check` | 开启 hvigorfile.ts 类型检查 |
| `--watch` | 观察模式,用于预览和热加载 |
| `--node-home <string>` | 指定 Node.js 路径 |
| `--config, -c` | 指定 hvigor-config.json5 参数 |
| Parameter | Description |
|-----------|-------------|
| `-h, --help` | Print help information |
| `-v, --version` | Print version information |
| `-s, --sync` | Sync project information to `./hvigor/outputs/sync/output.json` |
| `-m, --mode` | Specify execution directory level (e.g., `-m project`) |
| `--type-check` | Enable type checking for hvigorfile.ts |
| `--watch` | Watch mode for preview and hot reload |
| `--node-home <string>` | Specify Node.js path |
| `--config, -c` | Specify hvigor-config.json5 parameters |
## CI/CD 常用命令组合
## CI/CD Common Command Combinations
```bash
# 完整的 Release 构建流程
# Full release build pipeline
hvigorw clean && hvigorw assembleApp -p buildMode=release --no-daemon
# 带构建分析的 Debug 构建
# Debug build with build analysis
hvigorw assembleHap -p buildMode=debug --analyze=advanced --no-daemon
# 运行测试并生成覆盖率报告
# Run tests and generate coverage report
hvigorw onDeviceTest -p coverage=true --no-daemon
# 内存受限环境构建
# Build in memory-constrained environment
hvigorw assembleHap --optimization-strategy=memory --no-daemon
# 清理缓存
# Clean caches
hvigorw prune
hvigorw --stop-daemon-all
```

View File

@@ -1,407 +1,407 @@
# TypeScript to ArkTS Migration Guide
Complete guide for migrating TypeScript code to ArkTS, covering all language constraints and adaptation rules.
## Table of Contents
1. [Overview](#overview)
2. [Constraint Categories](#constraint-categories)
3. [Prohibited Features](#prohibited-features)
4. [Migration Examples](#migration-examples)
5. [Migration Checklist](#migration-checklist)
---
## Overview
ArkTS is based on TypeScript but enforces stricter rules for:
- **Performance**: Static analysis enables AOT compilation
- **Type Safety**: Eliminates runtime type errors
- **Predictability**: Fixed object structures at compile time
Constraints are categorized as:
- **Error**: Must fix, blocks compilation
- **Warning**: Should fix, may become errors in future
---
## Constraint Categories
### 1. Type System Constraints
#### Prohibited: `any` and `unknown`
```typescript
// ❌ TypeScript
let value: any = getData();
let result: unknown = parse(input);
// ✅ ArkTS
interface Data { id: number; name: string; }
let value: Data = getData();
let result: Data | null = parse(input);
```
#### Prohibited: Type assertions to `any`
```typescript
// ❌ TypeScript
(obj as any).dynamicProp = value;
// ✅ ArkTS - Define complete interface
interface MyObject {
existingProp: string;
dynamicProp?: number;
}
let obj: MyObject = { existingProp: 'test' };
obj.dynamicProp = value;
```
### 2. Variable Declaration
#### Prohibited: `var`
```typescript
// ❌ TypeScript
var count = 0;
var name = "hello";
// ✅ ArkTS
let count = 0;
const name = "hello";
```
### 3. Object Structure Constraints
#### Prohibited: Runtime property modification
```typescript
class Point {
x: number = 0;
y: number = 0;
}
let p = new Point();
// ❌ All prohibited
p['z'] = 99; // Dynamic property
delete p.x; // Property deletion
Object.assign(p, {z: 1}); // Runtime extension
// ✅ Define all properties upfront
class Point3D {
x: number = 0;
y: number = 0;
z: number = 0;
}
```
#### Prohibited: Structural typing (duck typing)
```typescript
interface Named { name: string; }
// ❌ TypeScript allows structural matching
let obj = { name: "Alice", age: 25 };
let named: Named = obj; // Works in TS, fails in ArkTS
// ✅ ArkTS requires explicit implementation
class Person implements Named {
name: string = "";
age: number = 0;
}
let named: Named = new Person();
```
### 4. Private Fields
#### Prohibited: `#` private fields
```typescript
// ❌ TypeScript
class MyClass {
#secret: string = "";
#getValue(): string { return this.#secret; }
}
// ✅ ArkTS
class MyClass {
private secret: string = "";
private getValue(): string { return this.secret; }
}
```
### 5. Symbol Properties
#### Prohibited: Symbol as property key
```typescript
// ❌ TypeScript
const sym = Symbol('key');
let obj = { [sym]: 'value' };
// ✅ ArkTS
let obj = { key: 'value' };
```
### 6. Prohibited Statements
#### `for...in`
```typescript
// ❌ TypeScript
for (let key in obj) {
console.log(obj[key]);
}
// ✅ ArkTS - Use Object.keys with forEach
Object.keys(obj).forEach((key: string) => {
// Access via typed interface
});
// ✅ ArkTS - Use for...of for arrays
let arr: string[] = ['a', 'b', 'c'];
for (let item of arr) {
console.log(item);
}
```
#### `delete`
```typescript
// ❌ TypeScript
delete obj.property;
// ✅ ArkTS - Use optional properties
interface Config {
name: string;
value?: number; // Optional, can be undefined
}
let config: Config = { name: 'test', value: undefined };
```
#### `with`
```typescript
// ❌ TypeScript
with (obj) {
console.log(property);
}
// ✅ ArkTS - Use explicit references
console.log(obj.property);
```
#### `in` operator for type checking
```typescript
// ❌ TypeScript
if ('name' in person) {
console.log(person.name);
}
// ✅ ArkTS - Use instanceof
if (person instanceof Person) {
console.log(person.name);
}
// ✅ ArkTS - Use discriminated unions
interface Person { type: 'person'; name: string; }
interface Animal { type: 'animal'; species: string; }
type Entity = Person | Animal;
function getName(e: Entity): string {
if (e.type === 'person') {
return e.name;
}
return e.species;
}
```
### 7. Interface Constraints
#### Prohibited: Call signatures and construct signatures
```typescript
// ❌ TypeScript
interface Callable {
(x: number): number;
new (s: string): Object;
}
// ✅ ArkTS - Use classes
class Calculator {
calculate(x: number): number {
return x * 2;
}
}
class Factory {
create(s: string): Object {
return { value: s };
}
}
```
### 8. Other Restrictions
| Feature | Status | Alternative |
|---------|--------|-------------|
| Comma expressions | Prohibited (except in `for`) | Separate statements |
| Computed property names | Limited | String literal keys |
| Spread on non-arrays | Limited | Explicit copying |
| `eval()` | Prohibited | Avoid |
| `Function()` constructor | Prohibited | Arrow functions |
| Prototype modification | Prohibited | Class inheritance |
---
## Migration Examples
### Example 1: Dynamic Configuration Object
```typescript
// ❌ TypeScript
let config: any = {};
config.apiUrl = 'https://api.example.com';
config.timeout = 5000;
config.retry = true;
// ✅ ArkTS
interface AppConfig {
apiUrl: string;
timeout: number;
retry: boolean;
}
let config: AppConfig = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retry: true
};
```
### Example 2: Object Iteration
```typescript
// ❌ TypeScript
interface User { name: string; age: number; }
let user: User = { name: 'John', age: 30 };
for (let key in user) {
console.log(`${key}: ${user[key]}`);
}
// ✅ ArkTS
interface User {
name: string;
age: number;
}
let user: User = { name: 'John', age: 30 };
console.log(`name: ${user.name}`);
console.log(`age: ${user.age}`);
// Or use explicit property list
const props: (keyof User)[] = ['name', 'age'];
for (let prop of props) {
// Handle each known property
}
```
### Example 3: Optional Property Handling
```typescript
// ❌ TypeScript
let obj: any = { a: 1 };
if (obj.b) {
delete obj.b;
}
obj.c = 3;
// ✅ ArkTS
interface MyObj {
a: number;
b?: number;
c?: number;
}
let obj: MyObj = { a: 1 };
if (obj.b !== undefined) {
obj.b = undefined; // Set to undefined instead of delete
}
obj.c = 3;
```
### Example 4: Type Guards
```typescript
// ❌ TypeScript
function process(input: unknown) {
if (typeof input === 'string') {
return input.toUpperCase();
}
if ('length' in input) {
return (input as any[]).length;
}
}
// ✅ ArkTS
function processString(input: string): string {
return input.toUpperCase();
}
function processArray(input: string[]): number {
return input.length;
}
// Use union types with type narrowing
type Input = string | string[];
function process(input: Input): string | number {
if (typeof input === 'string') {
return input.toUpperCase();
}
return input.length;
}
```
---
## Migration Checklist
### Phase 1: Enable Strict Mode
- [ ] Enable `strict: true` in tsconfig.json
- [ ] Enable `noImplicitAny: true`
- [ ] Enable `strictNullChecks: true`
- [ ] Fix all resulting errors
### Phase 2: Remove Prohibited Keywords
- [ ] Replace all `var` with `let`/`const`
- [ ] Remove all `any` type annotations
- [ ] Remove all `unknown` type annotations
- [ ] Replace `#` private fields with `private`
### Phase 3: Fix Object Patterns
- [ ] Replace dynamic property access with typed interfaces
- [ ] Remove `delete` statements
- [ ] Remove `for...in` loops
- [ ] Remove `with` statements
- [ ] Replace `in` operator type checks
### Phase 4: Update Interfaces
- [ ] Remove call signatures from interfaces
- [ ] Remove construct signatures from interfaces
- [ ] Replace structural typing with explicit implements
### Phase 5: Validate
- [ ] Build with ArkTS compiler
- [ ] Fix remaining errors
- [ ] Test all functionality
---
## Resources
- [Official Migration Guide](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/typescript-to-arkts-migration-guide)
- [ArkTS Language Reference](https://developer.huawei.com/consumer/cn/arkts/)
# TypeScript to ArkTS Migration Guide
Complete guide for migrating TypeScript code to ArkTS, covering all language constraints and adaptation rules.
## Table of Contents
1. [Overview](#overview)
2. [Constraint Categories](#constraint-categories)
3. [Prohibited Features](#prohibited-features)
4. [Migration Examples](#migration-examples)
5. [Migration Checklist](#migration-checklist)
---
## Overview
ArkTS is based on TypeScript but enforces stricter rules for:
- **Performance**: Static analysis enables AOT compilation
- **Type Safety**: Eliminates runtime type errors
- **Predictability**: Fixed object structures at compile time
Constraints are categorized as:
- **Error**: Must fix, blocks compilation
- **Warning**: Should fix, may become errors in future
---
## Constraint Categories
### 1. Type System Constraints
#### Prohibited: `any` and `unknown`
```typescript
// ❌ TypeScript
let value: any = getData();
let result: unknown = parse(input);
// ✅ ArkTS
interface Data { id: number; name: string; }
let value: Data = getData();
let result: Data | null = parse(input);
```
#### Prohibited: Type assertions to `any`
```typescript
// ❌ TypeScript
(obj as any).dynamicProp = value;
// ✅ ArkTS - Define complete interface
interface MyObject {
existingProp: string;
dynamicProp?: number;
}
let obj: MyObject = { existingProp: 'test' };
obj.dynamicProp = value;
```
### 2. Variable Declaration
#### Prohibited: `var`
```typescript
// ❌ TypeScript
var count = 0;
var name = "hello";
// ✅ ArkTS
let count = 0;
const name = "hello";
```
### 3. Object Structure Constraints
#### Prohibited: Runtime property modification
```typescript
class Point {
x: number = 0;
y: number = 0;
}
let p = new Point();
// ❌ All prohibited
p['z'] = 99; // Dynamic property
delete p.x; // Property deletion
Object.assign(p, {z: 1}); // Runtime extension
// ✅ Define all properties upfront
class Point3D {
x: number = 0;
y: number = 0;
z: number = 0;
}
```
#### Prohibited: Structural typing (duck typing)
```typescript
interface Named { name: string; }
// ❌ TypeScript allows structural matching
let obj = { name: "Alice", age: 25 };
let named: Named = obj; // Works in TS, fails in ArkTS
// ✅ ArkTS requires explicit implementation
class Person implements Named {
name: string = "";
age: number = 0;
}
let named: Named = new Person();
```
### 4. Private Fields
#### Prohibited: `#` private fields
```typescript
// ❌ TypeScript
class MyClass {
#secret: string = "";
#getValue(): string { return this.#secret; }
}
// ✅ ArkTS
class MyClass {
private secret: string = "";
private getValue(): string { return this.secret; }
}
```
### 5. Symbol Properties
#### Prohibited: Symbol as property key
```typescript
// ❌ TypeScript
const sym = Symbol('key');
let obj = { [sym]: 'value' };
// ✅ ArkTS
let obj = { key: 'value' };
```
### 6. Prohibited Statements
#### `for...in`
```typescript
// ❌ TypeScript
for (let key in obj) {
console.log(obj[key]);
}
// ✅ ArkTS - Use Object.keys with forEach
Object.keys(obj).forEach((key: string) => {
// Access via typed interface
});
// ✅ ArkTS - Use for...of for arrays
let arr: string[] = ['a', 'b', 'c'];
for (let item of arr) {
console.log(item);
}
```
#### `delete`
```typescript
// ❌ TypeScript
delete obj.property;
// ✅ ArkTS - Use optional properties
interface Config {
name: string;
value?: number; // Optional, can be undefined
}
let config: Config = { name: 'test', value: undefined };
```
#### `with`
```typescript
// ❌ TypeScript
with (obj) {
console.log(property);
}
// ✅ ArkTS - Use explicit references
console.log(obj.property);
```
#### `in` operator for type checking
```typescript
// ❌ TypeScript
if ('name' in person) {
console.log(person.name);
}
// ✅ ArkTS - Use instanceof
if (person instanceof Person) {
console.log(person.name);
}
// ✅ ArkTS - Use discriminated unions
interface Person { type: 'person'; name: string; }
interface Animal { type: 'animal'; species: string; }
type Entity = Person | Animal;
function getName(e: Entity): string {
if (e.type === 'person') {
return e.name;
}
return e.species;
}
```
### 7. Interface Constraints
#### Prohibited: Call signatures and construct signatures
```typescript
// ❌ TypeScript
interface Callable {
(x: number): number;
new (s: string): Object;
}
// ✅ ArkTS - Use classes
class Calculator {
calculate(x: number): number {
return x * 2;
}
}
class Factory {
create(s: string): Object {
return { value: s };
}
}
```
### 8. Other Restrictions
| Feature | Status | Alternative |
|---------|--------|-------------|
| Comma expressions | Prohibited (except in `for`) | Separate statements |
| Computed property names | Limited | String literal keys |
| Spread on non-arrays | Limited | Explicit copying |
| `eval()` | Prohibited | Avoid |
| `Function()` constructor | Prohibited | Arrow functions |
| Prototype modification | Prohibited | Class inheritance |
---
## Migration Examples
### Example 1: Dynamic Configuration Object
```typescript
// ❌ TypeScript
let config: any = {};
config.apiUrl = 'https://api.example.com';
config.timeout = 5000;
config.retry = true;
// ✅ ArkTS
interface AppConfig {
apiUrl: string;
timeout: number;
retry: boolean;
}
let config: AppConfig = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retry: true
};
```
### Example 2: Object Iteration
```typescript
// ❌ TypeScript
interface User { name: string; age: number; }
let user: User = { name: 'John', age: 30 };
for (let key in user) {
console.log(`${key}: ${user[key]}`);
}
// ✅ ArkTS
interface User {
name: string;
age: number;
}
let user: User = { name: 'John', age: 30 };
console.log(`name: ${user.name}`);
console.log(`age: ${user.age}`);
// Or use explicit property list
const props: (keyof User)[] = ['name', 'age'];
for (let prop of props) {
// Handle each known property
}
```
### Example 3: Optional Property Handling
```typescript
// ❌ TypeScript
let obj: any = { a: 1 };
if (obj.b) {
delete obj.b;
}
obj.c = 3;
// ✅ ArkTS
interface MyObj {
a: number;
b?: number;
c?: number;
}
let obj: MyObj = { a: 1 };
if (obj.b !== undefined) {
obj.b = undefined; // Set to undefined instead of delete
}
obj.c = 3;
```
### Example 4: Type Guards
```typescript
// ❌ TypeScript
function process(input: unknown) {
if (typeof input === 'string') {
return input.toUpperCase();
}
if ('length' in input) {
return (input as any[]).length;
}
}
// ✅ ArkTS
function processString(input: string): string {
return input.toUpperCase();
}
function processArray(input: string[]): number {
return input.length;
}
// Use union types with type narrowing
type Input = string | string[];
function process(input: Input): string | number {
if (typeof input === 'string') {
return input.toUpperCase();
}
return input.length;
}
```
---
## Migration Checklist
### Phase 1: Enable Strict Mode
- [ ] Enable `strict: true` in tsconfig.json
- [ ] Enable `noImplicitAny: true`
- [ ] Enable `strictNullChecks: true`
- [ ] Fix all resulting errors
### Phase 2: Remove Prohibited Keywords
- [ ] Replace all `var` with `let`/`const`
- [ ] Remove all `any` type annotations
- [ ] Remove all `unknown` type annotations
- [ ] Replace `#` private fields with `private`
### Phase 3: Fix Object Patterns
- [ ] Replace dynamic property access with typed interfaces
- [ ] Remove `delete` statements
- [ ] Remove `for...in` loops
- [ ] Remove `with` statements
- [ ] Replace `in` operator type checks
### Phase 4: Update Interfaces
- [ ] Remove call signatures from interfaces
- [ ] Remove construct signatures from interfaces
- [ ] Replace structural typing with explicit implements
### Phase 5: Validate
- [ ] Build with ArkTS compiler
- [ ] Fix remaining errors
- [ ] Test all functionality
---
## Resources
- [Official Migration Guide](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/typescript-to-arkts-migration-guide)
- [ArkTS Language Reference](https://developer.huawei.com/consumer/cn/arkts/)

File diff suppressed because it is too large Load Diff

View File

@@ -248,6 +248,114 @@ outputs/
| HAR | `.har` | Harmony Archive - Static library (compiled into HAP) |
| APP | `.app` | Complete application bundle (all HAP + HSP) |
## Finding Modules
All modules are defined in `build-profile.json5` at the project root, in the `modules` array.
### Module Definition Structure
```json5
{
"modules": [
{
"name": "entry", // Module name (used in build commands)
"srcPath": "./entry", // Module source path (relative to project root)
"targets": [ // Build target config (optional)
{
"name": "default",
"applyToProducts": ["default", "app_store"]
}
]
},
{
"name": "support_http",
"srcPath": "./support/support_http",
"targets": [...]
}
]
}
```
### Key Fields
| Field | Description |
|-------|-------------|
| `name` | Module name, used in build commands (e.g., `-p module=entry@default`) |
| `srcPath` | Module source path relative to project root |
| `targets` | Build target config, specifies which products this module applies to |
### Module Type Identification
| Characteristic | Module Type |
|----------------|-------------|
| Has `targets` and `name` is `entry` | **HAP** (Application entry) |
| Has `targets` config | **HSP** (Dynamic shared package) |
| No `targets` config | **HAR** (Static library, compiled into other modules) |
### Quick Commands
```bash
# Read build-profile.json5 to find all modules
cat build-profile.json5
# Extract module names and paths (grep)
grep -E '"name"|"srcPath"' build-profile.json5
```
## Finding Module Build Outputs
Module build outputs are located at:
```
{srcPath}/build/default/outputs/default/
```
**Note:** Debug and Release builds output to the same directory. The difference is in the signing configuration used (defined in `build-profile.json5``signingConfigs`).
### Output Files
| File | Description |
|------|-------------|
| `{name}-default-signed.hsp` | **Signed HSP** (ready for installation) |
| `{name}-default-unsigned.hsp` | Unsigned HSP |
| `{name}.har` | HAR static library |
| `app/{name}-default.hsp` | Intermediate artifact |
| `mapping/sourceMaps.map` | Source maps for debugging |
### Example
For module `support_http` with `srcPath: "./support/support_http"`:
```
support/support_http/build/default/outputs/default/
├── support_http-default-signed.hsp ← Signed, ready to install
├── support_http-default-unsigned.hsp
├── support_http.har
├── app/
│ └── support_http-default.hsp
├── mapping/
│ └── sourceMaps.map
└── pack.info
```
### Search Commands
```bash
# Find all signed HSP/HAP outputs
dir /s /b "*-signed.hsp" "*-signed.hap" 2>nul # Windows
find . -name "*-signed.hsp" -o -name "*-signed.hap" # Linux/macOS
# Find specific module's output
dir /s /b "{srcPath}\build\default\outputs\default\*" # Windows
ls -la {srcPath}/build/default/outputs/default/ # Linux/macOS
```
### Notes
1. **Build required**: If `build/` directory doesn't exist, run build first
2. **Project-level outputs**: Complete app bundle is in project root `outputs/` after `assembleApp`
3. **oh_modules outputs**: Dependency modules may have outputs in `oh_modules/@xxx/build/...` (these are resolved dependencies)
## Unwanted Modules in Output Directory
Sometimes HSP files appear in the output directory that are **not listed in `build-profile.json5`**. These modules should not be deployed.