(ctx: ResolutionContext)
| 1047 | const RN_NATIVE_SENDEVENT_RE = /\bsendEvent\s*\([^;{}]*?"([^"]+)"/g; |
| 1048 | |
| 1049 | function rnEventEdges(ctx: ResolutionContext): Edge[] { |
| 1050 | // Native dispatchers (source = the native method whose body sends the |
| 1051 | // event) and JS handlers (target = the function/method registered as |
| 1052 | // the listener) keyed by event name. |
| 1053 | const nativeDispatchersByEvent = new Map<string, Set<string>>(); |
| 1054 | const jsHandlersByEvent = new Map<string, Map<string, string>>(); |
| 1055 | |
| 1056 | for (const file of ctx.getAllFiles()) { |
| 1057 | const content = ctx.readFile(file); |
| 1058 | if (!content) continue; |
| 1059 | |
| 1060 | const nodesInFile = ctx.getNodesInFile(file); |
| 1061 | const lineOf = (idx: number) => content.slice(0, idx).split('\n').length; |
| 1062 | const addDispatcher = (event: string, line: number) => { |
| 1063 | const disp = enclosingFn(nodesInFile, line); |
| 1064 | if (!disp) return; |
| 1065 | const set = nativeDispatchersByEvent.get(event) ?? new Set<string>(); |
| 1066 | set.add(disp.id); |
| 1067 | nativeDispatchersByEvent.set(event, set); |
| 1068 | }; |
| 1069 | |
| 1070 | // ObjC side: `sendEventWithName:@"X"` only fires inside `.m`/`.mm` |
| 1071 | // files (RCTEventEmitter subclasses). |
| 1072 | if (file.endsWith('.m') || file.endsWith('.mm')) { |
| 1073 | RN_OBJC_SEND_RE.lastIndex = 0; |
| 1074 | let m: RegExpExecArray | null; |
| 1075 | while ((m = RN_OBJC_SEND_RE.exec(content))) { |
| 1076 | if (m[1]) addDispatcher(m[1], lineOf(m.index)); |
| 1077 | } |
| 1078 | } |
| 1079 | |
| 1080 | // Swift side: same RCTEventEmitter method, parens/named-args syntax. |
| 1081 | if (file.endsWith('.swift')) { |
| 1082 | RN_SWIFT_SEND_RE.lastIndex = 0; |
| 1083 | let m: RegExpExecArray | null; |
| 1084 | while ((m = RN_SWIFT_SEND_RE.exec(content))) { |
| 1085 | if (m[1]) addDispatcher(m[1], lineOf(m.index)); |
| 1086 | } |
| 1087 | RN_NATIVE_SENDEVENT_RE.lastIndex = 0; |
| 1088 | while ((m = RN_NATIVE_SENDEVENT_RE.exec(content))) { |
| 1089 | if (m[1]) addDispatcher(m[1], lineOf(m.index)); |
| 1090 | } |
| 1091 | } |
| 1092 | |
| 1093 | // JVM side: `.emit("X", …)` in Java/Kotlin, plus the common |
| 1094 | // `sendEvent(ctx, "X", body)` wrapper. (We pattern-match anywhere in the |
| 1095 | // file; the JS in-language path uses a separate emitter object pattern and |
| 1096 | // is already handled by eventEmitterEdges.) |
| 1097 | if (file.endsWith('.java') || file.endsWith('.kt')) { |
| 1098 | let m: RegExpExecArray | null; |
| 1099 | RN_JVM_EMIT_RE.lastIndex = 0; |
| 1100 | while ((m = RN_JVM_EMIT_RE.exec(content))) { |
| 1101 | if (m[1]) addDispatcher(m[1], lineOf(m.index)); |
| 1102 | } |
| 1103 | RN_NATIVE_SENDEVENT_RE.lastIndex = 0; |
| 1104 | while ((m = RN_NATIVE_SENDEVENT_RE.exec(content))) { |
| 1105 | if (m[1]) addDispatcher(m[1], lineOf(m.index)); |
| 1106 | } |
no test coverage detected