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:
co-authored by
Claude Fable 5
parent
f8cdbe3c67
commit
99152212a9
@@ -140,7 +140,9 @@ function pickClosestFileNode(candidates: Node[], ref: UnresolvedRef): Node {
|
||||
const LANGUAGE_FAMILY: Record<string, string> = {
|
||||
java: 'jvm', kotlin: 'jvm', scala: 'jvm',
|
||||
swift: 'apple', objc: 'apple',
|
||||
typescript: 'web', tsx: 'web', javascript: 'web', jsx: 'web',
|
||||
// ArkTS is a TS superset — every HarmonyOS project mixes `.ets` UI with
|
||||
// `.ts` logic modules, so refs must cross freely between them.
|
||||
typescript: 'web', tsx: 'web', javascript: 'web', jsx: 'web', arkts: 'web',
|
||||
c: 'c', cpp: 'c',
|
||||
// Razor/Blazor markup names C# types — same family so `@model Foo` /
|
||||
// `<MyComponent/>` resolve to their `.cs` class through the cross-family gate.
|
||||
@@ -226,6 +228,7 @@ export function matchFunctionRef(
|
||||
const bareFnOnly =
|
||||
ref.language === 'typescript' || ref.language === 'tsx' ||
|
||||
ref.language === 'javascript' || ref.language === 'jsx' ||
|
||||
ref.language === 'arkts' ||
|
||||
ref.language === 'cpp' || ref.language === 'python' ||
|
||||
ref.language === 'php';
|
||||
|
||||
@@ -1079,6 +1082,7 @@ function localReceiverTypePatterns(language: Language, r: string): RegExp[] {
|
||||
case 'javascript':
|
||||
case 'tsx':
|
||||
case 'jsx':
|
||||
case 'arkts':
|
||||
return [
|
||||
new RegExp(`\\b${r}\\b\\s*=\\s*new\\s+([A-Za-z_$][\\w.$]*)`), // = new Logger()
|
||||
// No keyword requirement, so this matches BOTH a local annotation
|
||||
@@ -1742,6 +1746,9 @@ export function matchFuzzy(
|
||||
/**
|
||||
* Match all strategies in order of confidence
|
||||
*/
|
||||
/** ArkUI attribute-helper decorators a `.attr(...)` chain may resolve to. */
|
||||
const ARKUI_ATTRIBUTE_DECORATORS = new Set(['Extend', 'Styles', 'AnimatableExtend', 'Builder']);
|
||||
|
||||
export function matchReference(
|
||||
ref: UnresolvedRef,
|
||||
context: ResolutionContext
|
||||
@@ -1753,6 +1760,37 @@ export function matchReference(
|
||||
return matchFunctionRef(ref, context);
|
||||
}
|
||||
|
||||
// ArkTS chained UI attributes — emitted with a leading dot (`.titleStyle`,
|
||||
// `.width`) by the extractor — resolve ONLY to decorator-marked attribute
|
||||
// helpers: `@Extend`/`@Styles`/`@AnimatableExtend` functions (and global
|
||||
// `@Builder`s used attribute-position). Framework attributes (`.width`,
|
||||
// `.fontSize` — on nearly every UI line) match no such helper and stay
|
||||
// unresolved, NEVER falling through to bare-name matching: on a samples
|
||||
// monorepo that fallthrough manufactured 36k wrong edges, giving single
|
||||
// same-named properties thousands of false callers. Ambiguity rule matches
|
||||
// the rest of the file: several same-named helpers → prefer the call-site
|
||||
// file, still ambiguous → drop the ref rather than guess.
|
||||
if (ref.language === 'arkts' && ref.referenceName.startsWith('.')) {
|
||||
const base = ref.referenceName.slice(1);
|
||||
const candidates = context
|
||||
.getNodesByName(base)
|
||||
.filter(
|
||||
(n) =>
|
||||
n.language === 'arkts' &&
|
||||
n.kind === 'function' &&
|
||||
(n.decorators ?? []).some((d) => ARKUI_ATTRIBUTE_DECORATORS.has(d))
|
||||
);
|
||||
const chosen =
|
||||
candidates.length > 1 ? preferCallSiteFile(candidates, ref.filePath) : candidates;
|
||||
if (chosen.length !== 1) return null;
|
||||
return {
|
||||
original: ref,
|
||||
targetNodeId: chosen[0]!.id,
|
||||
confidence: 0.85,
|
||||
resolvedBy: 'exact-match',
|
||||
};
|
||||
}
|
||||
|
||||
// Erlang `-behaviour(m)` refs target a MODULE. Letting them fall through to
|
||||
// bare-name matching grabs any same-named symbol — on emqx,
|
||||
// `-behaviour(supervisor)` resolved to a `-define(supervisor, …)` macro
|
||||
|
||||
Reference in New Issue
Block a user