feat(extraction): add ArkTS language support with ArkUI dispatch bridges (#396, #512, #890 via #648) (#1186)

Adds ArkTS (.ets, HarmonyOS/OpenHarmony) as a first-class language:
full TypeScript-grade extraction via the harmony-contrib tree-sitter
grammar (MIT, vendored byte-identical from the tree-sitter-arkts 0.2.0
npm tarball), plus the ArkUI constructs that make HarmonyOS apps
traceable:

- @Component/@ComponentV2 structs with decorators from both grammar
  positions; members extract as class members with qualified names.
- build() component trees: child instantiation edges via
  arkui_component_expression, no synthesizer needed.
- Attribute chains emitted dot-prefixed and resolved ONLY against
  @Extend/@Styles/@AnimatableExtend/@Builder helpers (unique-or-drop) —
  bare-name fallthrough produced 36,840 wrong edges (17% of calls) on
  the OpenHarmony samples monorepo. All four grammar chain shapes
  handled, including the detached-chain forms.
- .onClick(this.handler) method-reference bindings.
- ohpm workspace modules: bare imports follow oh-package.json5 file:
  deps (ambiguous names dropped), honoring each module's main entry —
  which also lets .ts consumers resolve .ets modules.
- ArkUI dynamic-dispatch bridges, all provenance:'heuristic' with
  wiring-site metadata: assignment-gated state->build() re-render
  (V1 @State family + V2 @Local/@Provider/@Consumer),
  @ohos.events.emitter emit->subscriber pairing on static event keys
  (numeric ids same-file, named constants same-module, fan-out capped),
  and router.pushUrl literal urls -> the target page's @Entry struct.
- $r/$rawfile resource intrinsics treated as built-ins; arkts joins the
  web language family, value-reference edges, re-export chase, and the
  other TS-applicable gates.

Also ships a language-agnostic index-completeness guard: indexAll
stamps index_state (indexing -> complete/partial/failed), reconciles
discovered vs accounted files (a loaded run silently dropped 37 files),
and codegraph status surfaces truncated/partial indexes in human and
--json output.

Validated on HarmoneyOpenEye (82 files), CoolMallArkTS (528, modular
ohpm + ArkUI V2), and openharmony/applications_app_samples (11,693
files, 202,890 nodes stable across re-index, attribute false-positive
audit 36,840 -> 588 residual all-plausible). Supersedes PRs #656 and
#988 with credit — both informed this implementation.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-07-06 09:07:15 -05:00
committed by GitHub
co-authored by Claude Fable 5
parent f8cdbe3c67
commit 99152212a9
21 changed files with 1695 additions and 21 deletions
+20
View File
@@ -354,6 +354,14 @@ function printIndexResult(clack: typeof import('@clack/prompts'), result: IndexR
clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files`);
}
clack.log.info(`${formatNumber(result.nodesCreated)} nodes, ${formatNumber(result.edgesCreated)} edges in ${formatDuration(result.durationMs)}`);
// A PARTIAL index (files silently dropped mid-pipeline) must not pass
// as a clean run — it's the difference between "indexed the repo" and
// "indexed most of the repo, quietly". Only the completeness
// reconciliation warning; per-file extractor warnings stay in the
// error-code summary below.
for (const w of result.errors.filter((e) => e.code === 'index_partial')) {
clack.log.warn(w.message);
}
} else if (hasErrors) {
clack.log.error(`Indexing failed ${getGlyphs().dash} all ${formatNumber(result.filesErrored)} files had errors`);
} else {
@@ -798,6 +806,7 @@ program
const buildInfo = cg.getIndexBuildInfo();
const reindexRecommended = cg.isIndexStale();
const indexState = cg.getIndexState();
// JSON output mode
if (options.json) {
@@ -829,6 +838,10 @@ program
builtWithExtractionVersion: buildInfo.extractionVersion,
currentExtractionVersion: EXTRACTION_VERSION,
reindexRecommended,
// 'complete' | 'partial' (files silently dropped) | 'indexing'
// (a run was killed mid-index — the index is truncated) |
// 'failed' | null (predates the marker).
state: indexState,
},
}));
cg.destroy();
@@ -842,6 +855,13 @@ program
if (worktreeMismatch) {
warn(worktreeMismatchWarning(worktreeMismatch));
}
if (indexState === 'indexing') {
warn('The last index run never finished (killed mid-index?) — the index is truncated. Re-run "codegraph index".');
} else if (indexState === 'partial') {
warn('The last index run silently dropped files — the index is partial. Re-run "codegraph index".');
} else if (indexState === 'failed') {
warn('The last index run failed — results may be incomplete. Re-run "codegraph index".');
}
console.log();
// Index stats