Skip to content

feat: ArkTS (.ets) and HarmonyOS project config support#339

Open
davien wants to merge 3 commits intosafishamsi:v4from
davien:feat/arkts-support
Open

feat: ArkTS (.ets) and HarmonyOS project config support#339
davien wants to merge 3 commits intosafishamsi:v4from
davien:feat/arkts-support

Conversation

@davien
Copy link
Copy Markdown

@davien davien commented Apr 14, 2026

Adds full first-class support for HarmonyOS development.

ArkTS language (.ets)

ArkTS is HarmonyOS's UI language — a strict superset of TypeScript. The only non-TS syntactic feature is the struct keyword for UI components; decorators are already valid TS (experimentalDecorators). We preprocess structclass (same byte length, line numbers preserved) and then enrich the extraction with HarmonyOS-specific semantics.

Decorators with real semantics (not just names)

  • Component-level (→ decorates edges, struct upgraded to arkts_component):
    @Component, @ComponentV2, @Entry, @Preview, @CustomDialog, @Reusable, @ReusableV2
  • Observation (→ observes edges):
    @Observed, @ObservedV2 + @Trace field binding
  • State (→ contains_state edges, distinct arkts_state node type per field):
    @State, @Prop, @Link, @Local, @Param, @Provide/@Consume, @Provider/@Consumer, @StorageLink, @StorageProp, @LocalStorageLink, @LocalStorageProp, @Computed, @Event, @Once, @Watch, @Monitor, @Require, @BuilderParam, @ObjectLink
  • Builder (recognized, grouped): @Builder, @Styles, @Extend, @AnimatableExtend, @Concurrent

Semantic edges beyond decorator names

  • @watch('method')watches edge to the target method in the same struct
  • @monitor('field1', 'field2') → one monitors edge per target expression
  • @Provide/@consume cross-component flow → synthetic arkts_provide_key nodes that producers and consumers join on — lets you query "who provides key X and who consumes it"
  • @StorageLink/@StorageProparkts_app_storage_key node + storage_binds edge
  • @LocalStorageLink/@LocalStorageProparkts_local_storage_key node + local_storage_binds edge
  • build() bodyuses_component edges from parent struct to every capitalized-identifier-called-as-function inside the build tree (Column(), NavDestination(), ForEach(), user components, etc.)
  • $r("app.xxx.yyy") / $rawfile("…")arkts_resource nodes + references_resource edges, shared by key across components
  • Lifecycle methods (aboutToAppear, aboutToDisappear, onPageShow, onPageHide, onBackPress, onCreate, onWindowStageCreate, etc.) → tagged arkts_lifecycle + has_lifecycle edge
  • Multi-line decorator args (@Entry({ routeName: ... })) handled via paren-depth tracking

Helpers

  • _find_struct_ranges: brace-balanced struct/class scanner with string-literal awareness (handles '...', "...", `...` without miscounting)
  • _containing_struct: line → struct lookup so all enrichment edges attach to the right parent

HarmonyOS project config files

Adds structured extraction of project-level config that previously was silently skipped:

File Emits
module.json5 harmony_module + harmony_ability + harmony_extension_ability + harmony_permission, with declares_ability, declares_extension_ability, requires_permission edges
router_map.json harmony_route per entry + INFERRED route_maps_to edge to the page component (ID inferred from pageSourceFile stem + route name)
oh-package.json5 harmony_package + harmony_dependency, with depends_on / dev_depends_on / dynamic_depends_on distinguishing the three dep kinds
build-profile.json5 harmony_build_profile + configures_module edges
  • Minimal _parse_json5() helper — strips // comments, /* */ block comments, and trailing commas, then hands to json.loads. No new dependency.
  • Filename-based dispatch in extract() (same pattern already used for .blade.php).
  • detect.py routes these files to FileType.CODE so they enter the normal extraction pipeline.

Tests

29 new tests covering ArkTS language (10), V1+V2 semantic enrichment (12), and HarmonyOS config files (7). 462 total passing, zero regressions.

Verified on a real HarmonyOS RTC SDK (ZKJAIRTCDemo):

  • EnvCheckPage.ets → 60 nodes / 87 edges (27 resources, 10 UI components, 5 observes edges, 2 @ObservedV2 classes)
  • router_map.json → 6 routes + 6 route_maps_to edges
  • oh-package.json5 → 5 HAR dependencies extracted

Migration / compatibility

  • _extract_generic gains an optional source_override parameter so extract_ets can inject preprocessed bytes while keeping metadata tied to the real file. All other callers unaffected.
  • No new required dependencies. JSON5 parsing is self-contained.
  • All new node/edge types are additive — existing graphs unchanged for non-.ets projects.

Happy to split into smaller PRs or adjust the API (e.g. factoring _enrich_arkts into a separate module) if preferred.

weiqiang.zhao and others added 3 commits April 14, 2026 17:04
ArkTS is HarmonyOS's UI language — a strict superset of TypeScript with
two non-TS additions: the `struct` keyword for UI components, and a set
of HarmonyOS-specific decorators (@Component/@ComponentV2, @State/@Local,
@prop, @link, @provide, @consume, @watch, @builder, @Styles, etc.).

Implementation:
- detect.py: register '.ets' in CODE_EXTENSIONS
- extract.py:
  - _extract_generic gains a source_override parameter so callers can
    inject pre-processed bytes while keeping metadata tied to the real
    file (preserves node IDs and source_location line numbers)
  - extract_ets() preprocesses `struct` → `class ` (same byte length,
    line numbers preserved) then runs the standard TypeScript extractor
  - _enrich_arkts() post-processes the original source to recognize
    ArkTS decorators and emit two new node types:
      • arkts_decorator — @Component/@Entry/etc. wired via `decorates`
        edge to the struct they annotate
      • arkts_state — @State/@Local/@Prop/etc. fields, wired via
        `contains_state` edge to the file node
  - Handles multi-line decorator args like @entry({ routeName: "..." })
    via paren-depth tracking
  - Covers both V1 (@component, @State, @observed) and V2 (@ComponentV2,
    @Local, @ObservedV2, @trace, @param, @event, @monitor, @computed)
    decorator families
- Dispatch registered for '.ets' in extract()

Tests:
- tests/fixtures/sample.ets exercising both decorator flavors, struct,
  regular class, multi-line decorator args, and state fields
- 10 new tests in test_languages.py covering parsing, decorators,
  state nodes, no dangling edges, line-number preservation

Verified on a real HarmonyOS project (ZKJAIRTCDemo):
  EnvCheckPage.ets → 22 nodes / 42 edges, @entry + @ComponentV2
  recognized, 7 @trace state fields captured.

Full test suite: 443 passed, 0 regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…composition, resources, provide/consume, storage bindings

Extends extract_ets() beyond decorator-name recognition to actual state-
management semantics. Multi-pass enrichment over the original source text:

Pass 1 — Decorators + fields + methods:
  • @Component/@ComponentV2 struct → upgraded to node_type "arkts_component"
  • @ObservedV2/@observed class → arkts_observed_v2 / arkts_observed
  • Lifecycle methods (aboutToAppear, onPageShow, onBackPress, onCreate,
    onWindowStageCreate, etc.) → arkts_lifecycle + `has_lifecycle` edge
  • @trace field inside @ObservedV2 class → `observes` edge from class
  • @watch('methodName') (both multi-line and single-line layouts) →
    arkts_watch node + `watches` edge to target method
  • Decorated getters `@Computed get total(): T {...}` now recognized

Pass 2 — build() UI composition:
  • Finds each struct's build() method body via brace counting
  • Collects Capitalized-identifier + '(' calls as child UI components
  • Emits arkts_ui_component_usage nodes + `uses_component` edges
  • Parent components → built-in containers (Column, Row, NavDestination,
    List, Scroll, ForEach, ...) + user-defined components (TitleBar, ...)

Pass 3 — resource references:
  • $r("app.xxx.yyy") / $rawfile("...") → arkts_resource nodes
  • struct → resource via `references_resource` edges
  • Cross-file resource nodes are shared by key (same $r call in multiple
    components merges into one node)

Pass 4 — @Provide/@consume cross-component flow:
  • @provide('key') / @Provider('key') → `provides` edge to arkts_provide_key
  • @consume('key') / @consumer('key') → `consumes` edge to same key
  • Synthetic key nodes automatically join parent/child producers+consumers

Pass 5 — Storage bindings:
  • @storagelink('key') / @StorageProp('key') → arkts_app_storage_key +
    `storage_binds` edge (AppStorage global singletons)
  • @LocalStorageLink / @LocalStorageProp → arkts_local_storage_key +
    `local_storage_binds` edge (LocalStorage per-component)

Pass 6 — @monitor targets:
  • @monitor('checkState', 'model.titleName') → one arkts_monitor_target
    per string arg + `monitors` edges

Support helpers:
  • _find_struct_ranges: brace-balanced struct/class scanner with string
    literal awareness (handles '...' "..." `...` without mis-counting)
  • _containing_struct: line→struct lookup for contextual edges

New decorators covered: Provider, Consumer (V2 equivalents of Provide/Consume).

Fixture expanded to exercise the full surface: both V1 and V2 decorators,
lifecycle methods, @watch on a field, @monitor multi-arg, @computed getter,
@Provider+@consume pair, @storagelink + @LocalStorageProp, nested UI tree.

Tests: 22 passing (was 10), 455 total passing, 0 regressions.

Real-world verification on ZKJAIRTCDemo/EnvCheckPage.ets:
  60 nodes / 87 edges (was 22/42)
  27 $r() resource refs, 10 UI components, 5 observes edges,
  2 lifecycle methods, 2 @ObservedV2 classes, 1 @entry+@ComponentV2 pair.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…5 / build-profile.json5

Adds structured extraction of HarmonyOS project configuration files so the
knowledge graph captures real project-level semantics: abilities, permissions,
routes, HAR dependencies, build profile.

detect.py:
- New HARMONY_CONFIG_FILES set with exact filenames
- classify_file() routes these to FileType.CODE so they enter extraction

extract.py:
- _parse_json5(): minimal JSON5 parser (strips // comments, /* */ block
  comments, trailing commas) — no external dependency
- extract_harmony_config(): dispatch-by-filename handler

Per-file handlers:
  module.json5 →
    • harmony_module (name, type, deviceTypes)
    • harmony_ability per entry in abilities[] + `declares_ability` edge
    • harmony_extension_ability + `declares_extension_ability` edge
    • harmony_permission per requestPermissions[] + `requires_permission` edge

  router_map.json →
    • harmony_route (name, pageSourceFile, buildFunction)
    • `route_maps_to` INFERRED edge from route → page component (ID inferred
      from pageSourceFile stem + route name)

  oh-package.json5 →
    • harmony_package (name, version, main)
    • harmony_dependency per dep + `depends_on` / `dev_depends_on` /
      `dynamic_depends_on` edges (distinguishing dep kinds)

  build-profile.json5 →
    • harmony_build_profile + `configures_module` edges to declared modules

extract() dispatch updated: filename-based lookup checked before suffix
lookup (same pattern as .blade.php).

Fixtures: module.json5, router_map.json, oh-package.json5 exercising
abilities, permissions, multiple routes, three dependency kinds, and
both JSON5 features (// comments, trailing commas).

Tests: 7 new harmony_* cases + comment/trailing-comma tolerance +
classify_file routing check. 462 total passing, 0 regressions.

Real-world verification on ZKJAIRTCDemo:
  router_map.json → 6 routes + 6 route_maps_to edges
  oh-package.json5 → 5 HAR deps (zrtc, msxf_face, msxf_ocr, axios, librecorder.so)
  module.json5 → module:zkj_ai_record (HAR type, no abilities as expected)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@davien davien force-pushed the feat/arkts-support branch from ec38381 to ac43948 Compare April 14, 2026 09:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant