* Parse a TS file for a TurboModule spec declaration. The spec file is * the JS↔native source-of-truth in the new architecture — its interface * lists every JS-visible method, and a `TurboModuleRegistry.get* (...)` * default export pins the module name. * * Returns `null` when the file isn
( source: string )
| 210 | * Returns `null` when the file isn't a TurboModule spec. |
| 211 | */ |
| 212 | function parseTurboModuleSpec( |
| 213 | source: string |
| 214 | ): { moduleName: string; methods: string[] } | null { |
| 215 | // `TurboModuleRegistry.getEnforcing<Spec>('ModuleName')` or |
| 216 | // `TurboModuleRegistry.get<Spec>('ModuleName')`. The literal must be a |
| 217 | // single-or-double-quoted string. |
| 218 | const regMatch = source.match( |
| 219 | /TurboModuleRegistry\.(?:getEnforcing|get)\s*<[^>]*>\s*\(\s*['"]([^'"]+)['"]\s*\)/ |
| 220 | ); |
| 221 | if (!regMatch || !regMatch[1]) return null; |
| 222 | const moduleName = regMatch[1]; |
| 223 | |
| 224 | // Find `export interface Spec extends TurboModule { … }` and pull each |
| 225 | // method declaration's name. We don't need types — just names. |
| 226 | const ifaceMatch = source.match( |
| 227 | /export\s+interface\s+Spec\b[^{]*\{([\s\S]*?)\n\}/ |
| 228 | ); |
| 229 | if (!ifaceMatch || !ifaceMatch[1]) return null; |
| 230 | const body = ifaceMatch[1]; |
| 231 | |
| 232 | const methods: string[] = []; |
| 233 | // Method shape: `name(args): ReturnType;` or `name(): void;`. Skip |
| 234 | // properties (no parens before colon). |
| 235 | const methodRegex = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/gm; |
| 236 | let m: RegExpExecArray | null; |
| 237 | while ((m = methodRegex.exec(body)) !== null) { |
| 238 | const name = m[1]; |
| 239 | if (name) methods.push(name); |
| 240 | } |
| 241 | return { moduleName, methods }; |
| 242 | } |
| 243 | |
| 244 | // ─── Map building ─────────────────────────────────────────────────────────── |
| 245 |