MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / arkuiEmitterEdges

Function arkuiEmitterEdges

src/resolution/callback-synthesizer.ts:588–678  ·  view source on GitHub ↗

* Phase 4b-ets2: HarmonyOS `@ohos.events.emitter` bridge. The cross-component * bus — `emitter.emit(eventId)` fires `emitter.on(eventId, cb)` — is * framework-internal, so an order flow riding it (OrangeShopping's * add-to-cart) dead-ends at the emit. Link emit-site enclosing * function/method →

(ctx: ResolutionContext, onYield: MaybeYield)

Source from the content-addressed store, hash-verified

586 * so targeting that method keeps the chain connected.
587 */
588async function arkuiEmitterEdges(ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]> {
589 let scannedFiles = 0;
590 interface Site { nodeId: string; file: string; line: number }
591 // bucket key -> emit sites / handler sites
592 const emits = new Map<string, Site[]>();
593 const handlers = new Map<string, Site[]>();
594
595 const moduleDirs = (() => {
596 const ws = ctx.getWorkspacePackages?.();
597 return ws ? [...new Set(ws.byName.values())].sort((a, b) => b.length - a.length) : [];
598 })();
599 const moduleScopeOf = (file: string): string => {
600 for (const dir of moduleDirs) {
601 if (file === dir || file.startsWith(dir + '/')) return dir;
602 }
603 return '';
604 };
605
606 for (const file of ctx.getAllFiles()) {
607 if ((++scannedFiles & 15) === 0) await onYield();
608 if (!file.endsWith('.ets')) continue;
609 const content = ctx.readFile(file);
610 if (!content || !content.includes('emitter.')) continue;
611 const safe = stripCommentsForRegex(content, 'typescript');
612 const nodes = ctx.getNodesInFile(file)
613 .filter((n) => n.kind === 'method' || n.kind === 'function');
614
615 ARKUI_EMITTER_CALL_RE.lastIndex = 0;
616 let m: RegExpExecArray | null;
617 while ((m = ARKUI_EMITTER_CALL_RE.exec(safe))) {
618 const verb = m[1]!;
619 const arg = m[2]!.trim();
620 const line = safe.slice(0, m.index).split('\n').length;
621 const encl = nodes
622 .filter((n) => n.startLine <= line && n.endLine >= line)
623 .sort((a, b) => (a.endLine - a.startLine) - (b.endLine - b.startLine))[0];
624 if (!encl) continue;
625
626 // Recover the event key from the first argument.
627 let key: string | null = null;
628 const idLit = arg.startsWith('{') ? arg.match(/\beventId\s*:\s*([\w$.]+)/)?.[1] : undefined;
629 const token = idLit ?? arg;
630 if (token !== undefined) {
631 if (/^\d+$/.test(token)) {
632 key = `num:${file}:${token}`; // numeric: same-file only
633 } else if (token.includes('.')) {
634 key = `name:${moduleScopeOf(file)}:${token}`;
635 } else {
636 // Local variable — chase its same-file declaration one level:
637 // `let x = new EventsId(K)` / `const x = K`.
638 const declRe = new RegExp(
639 `\\b${token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b\\s*(?::[^=\\n]+)?=\\s*(?:new\\s+[\\w$.]+\\(\\s*([^)\\n]+?)\\s*\\)|([\\w$.]+))`
640 );
641 const decl = safe.match(declRe);
642 const inner = (decl?.[1] ?? decl?.[2])?.trim();
643 if (inner && /^\d+$/.test(inner)) key = `num:${file}:${inner}`;
644 else if (inner && /^[\w$.]+$/.test(inner)) key = `name:${moduleScopeOf(file)}:${inner}`;
645 }

Callers 1

Calls 10

stripCommentsForRegexFunction · 0.90
moduleScopeOfFunction · 0.85
getWorkspacePackagesMethod · 0.80
readFileMethod · 0.80
hasMethod · 0.80
getAllFilesMethod · 0.65
getNodesInFileMethod · 0.65
execMethod · 0.65
getMethod · 0.65
setMethod · 0.45

Tested by

no test coverage detected