* Parse a Java/Kotlin source file for `@ReactMethod` annotated methods * and the surrounding class's `getName()` return value (the JS-visible * module name). * * Java: `@ReactMethod public void getCurrentPosition(Callback cb) { … }` * Kotlin: `@ReactMethod fun getCurrentPosition(cb: Callback) {
( source: string )
| 169 | * when the literal isn't present. |
| 170 | */ |
| 171 | function parseJvmRNExports( |
| 172 | source: string |
| 173 | ): Array<{ moduleName: string; jsName: string }> { |
| 174 | const results: Array<{ moduleName: string; jsName: string }> = []; |
| 175 | |
| 176 | // getName() literal — Java + Kotlin both look something like: |
| 177 | // public String getName() { return "Geolocation"; } |
| 178 | // fun getName(): String = "Geolocation" |
| 179 | // fun getName() = "Geolocation" |
| 180 | const getName = source.match( |
| 181 | /\bgetName\s*\([^)]*\)\s*(?::\s*String)?\s*(?:=\s*|\{[^}]*return\s*)"([^"]+)"/ |
| 182 | ); |
| 183 | // Class name fallback. |
| 184 | const classMatch = |
| 185 | source.match(/\bclass\s+([A-Za-z_][A-Za-z0-9_]*)\b[^{]*ReactContextBaseJavaModule/) ?? |
| 186 | source.match(/\bclass\s+([A-Za-z_][A-Za-z0-9_]*)\b[^{]*ReactPackage/); |
| 187 | const moduleName = |
| 188 | getName?.[1] ?? (classMatch?.[1] ? classMatch[1].replace(/Module$/, '') : null); |
| 189 | if (!moduleName) return results; |
| 190 | |
| 191 | // @ReactMethod annotations — followed (after optional modifiers / args / |
| 192 | // newlines) by either `void <name>(` (Java) or `fun <name>(` (Kotlin). |
| 193 | const methodRegex = |
| 194 | /@ReactMethod\b[^{]*?(?:\bfun\s+|\bvoid\s+|\bpublic\s+\w[\w<>\[\]]*\s+)([A-Za-z_][A-Za-z0-9_]*)\s*\(/g; |
| 195 | let m: RegExpExecArray | null; |
| 196 | while ((m = methodRegex.exec(source)) !== null) { |
| 197 | const jsName = m[1]; |
| 198 | if (jsName) results.push({ moduleName, jsName }); |
| 199 | } |
| 200 | |
| 201 | return results; |
| 202 | } |
| 203 | |
| 204 | /** |
| 205 | * Parse a TS file for a TurboModule spec declaration. The spec file is |