From d98f6357e6317ff90a12c366dbe8e582a98e598f Mon Sep 17 00:00:00 2001 From: NickSxti Date: Mon, 29 Jun 2026 17:07:23 +0400 Subject: [PATCH] Fix NoCodes TurboModule spec failing Codegen on React Native 0.84+ The NoCodes TurboModule spec typed the `onNoCodeEvent` emitter payload as a union of types imported from `../Mapper`. React Native Codegen only resolves types declared inside the spec file, and since RN 0.84 it fully descends into an event emitter's payload type alias. It therefore cannot resolve the imported `QNoCodeAction` and throws: UnsupportedGenericParserError: Module NativeNoCodesModule: Unrecognized generic type 'QNoCodeAction' in NativeModule spec. This aborts `pod install` for every New Architecture app on RN 0.84+ (iOS and Android), regardless of whether the app uses No-Codes, because Codegen processes the whole spec directory at build time. RN 0.82+ no longer lets apps opt out of the New Architecture, so there is no escape hatch. Type `onNoCodeEvent` as `EventEmitter`, matching every other emitter in the SDK (`onNoCodePurchase`, `onEntitlementsUpdated`, ...), and move the `NoCodeEvent` payload type to its only consumer (`NoCodesInternal`). The native boundary now carries an opaque payload that Codegen resolves natively; the handler casts it back in the JS layer, exactly as `customPurchaseHandler` already does. Runtime is unchanged - native still emits a `{ name, payload }` dictionary. Verified with @react-native/codegen 0.85.3: the spec parses with all three emitters preserved (`onNoCodeEvent`, `onNoCodePurchase`, `onNoCodeRestore`); typecheck and unit tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/internal/NoCodesInternal.ts | 10 ++++++++-- src/internal/specs/NativeNoCodesModule.ts | 8 +------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/internal/NoCodesInternal.ts b/src/internal/NoCodesInternal.ts index c05b8bd7..6bf95af5 100644 --- a/src/internal/NoCodesInternal.ts +++ b/src/internal/NoCodesInternal.ts @@ -6,10 +6,15 @@ import type {PurchaseDelegate} from '../dto/PurchaseDelegate'; import ScreenPresentationConfig from '../dto/ScreenPresentationConfig'; import NoCodesError from '../dto/NoCodesError'; import {NoCodesErrorCode, NoCodesTheme} from '../dto/enums'; -import RNNoCodes, {type NoCodeEvent} from './specs/NativeNoCodesModule'; +import RNNoCodes from './specs/NativeNoCodesModule'; import {sdkSource, sdkVersion} from './QonversionInternal'; import Product from '../dto/Product'; +type NoCodeEvent = { + name: string; + payload: QNoCodeAction | QNoCodesError | QNoCodeScreenInfo | undefined; +}; + const EVENT_SCREEN_SHOWN = "nocodes_screen_shown"; const EVENT_FINISHED = "nocodes_finished"; const EVENT_ACTION_STARTED = "nocodes_action_started"; @@ -46,7 +51,8 @@ export default class NoCodesInternal implements NoCodesApi { await RNNoCodes.close(); } - private noCodeEventHandler = (event: NoCodeEvent) => { + private noCodeEventHandler = (rawEvent: Object) => { + const event = rawEvent as NoCodeEvent; switch (event.name) { case EVENT_SCREEN_SHOWN: const screenId = (event.payload as QNoCodeScreenInfo)["screenId"] ?? ""; diff --git a/src/internal/specs/NativeNoCodesModule.ts b/src/internal/specs/NativeNoCodesModule.ts index b93a4377..073c6d39 100644 --- a/src/internal/specs/NativeNoCodesModule.ts +++ b/src/internal/specs/NativeNoCodesModule.ts @@ -1,12 +1,6 @@ import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native'; import type { EventEmitter } from 'react-native/Libraries/Types/CodegenTypes'; -import type { QNoCodeAction, QNoCodesError, QNoCodeScreenInfo } from '../Mapper'; - -export type NoCodeEvent = { - name: string; - payload: QNoCodeAction | QNoCodesError | QNoCodeScreenInfo | undefined; -}; export interface Spec extends TurboModule { initialize(projectKey: string, source: string, version: string, proxyUrl?: string, locale?: string, theme?: string): void; @@ -23,7 +17,7 @@ export interface Spec extends TurboModule { delegatedRestoreCompleted(): void; delegatedRestoreFailed(errorMessage: string): void; - readonly onNoCodeEvent: EventEmitter; + readonly onNoCodeEvent: EventEmitter; // NoCodeEvent (defined in NoCodesInternal.ts) readonly onNoCodePurchase: EventEmitter; // QProduct readonly onNoCodeRestore: EventEmitter; }