Align TurboModule EventEmitter payload types across platforms (#58063)

Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58063

Codegen's `EventEmitter<T>` support for TurboModules diverged between platforms
in two ways:

- The explicit number types `Double`, `Float` and `Int32` worked as emitter
  payloads on Android and C++, but iOS rejected them at codegen time (plain
  `number` worked). iOS now maps all of them to `NSNumber *_Nonnull`, matching
  how plain `number` is already emitted.
- `ArrayBuffer` was rejected on Android and iOS, but the C++ generator silently
  accepted it and produced a `jsi::ArrayBuffer` emitter. `ArrayBuffer` is not
  emittable on any platform — Android emitters always carry a `folly::dynamic`
  payload, which cannot hold raw bytes — and the schema type
  `NativeModuleEventEmitterBaseTypeAnnotation` already excluded it. It is now
  rejected everywhere.

`ArrayBuffer` payloads are rejected in the shared parser, so Flow and TypeScript
specs produce the same error, and the three generators keep an equivalent guard
so schemas that are constructed without going through the parser fail the same
way. `ArrayBuffer` remains supported as a method argument and as a synchronous
return value.

Changelog:
[iOS][Added] - Support `Double`, `Float` and `Int32` payloads for TurboModule `EventEmitter`s
[General][Breaking] - Reject `ArrayBuffer` as a TurboModule `EventEmitter` payload on all platforms

Reviewed By: GijsWeterings

Differential Revision: D116952150

fbshipit-source-id: e148a394c4916b3ebfbd3f559b190a5910b29de1
This commit is contained in:
Christoph Purrer
2026-08-24 09:55:31 -07:00
committed by meta-codesync[bot]
parent 2c4278d83b
commit ab2ea649e6
23 changed files with 441 additions and 10 deletions
@@ -40,6 +40,7 @@ const {
getModules,
isArrayRecursiveMember,
isDirectRecursiveMember,
throwIfUnsupportedEventEmitterPayload,
} = require('./Utils');
type FilesOutput = Map<string, string>;
@@ -638,6 +639,11 @@ function translateEventEmitterToCpp(
resolveAlias: AliasResolver,
enumMap: NativeModuleEnumMap,
): EventEmitterCpp {
throwIfUnsupportedEventEmitterPayload(
eventEmitter.name,
eventEmitter.typeAnnotation.typeAnnotation,
);
const isVoidTypeAnnotation =
eventEmitter.typeAnnotation.typeAnnotation.type === 'VoidTypeAnnotation';
const templateName = `${toPascalCase(eventEmitter.name)}Type`;
@@ -28,6 +28,7 @@ const {parseValidUnionType, toPascalCase} = require('../Utils');
const {
createAliasResolver,
getModules,
throwIfUnsupportedEventEmitterPayload,
throwIfUnsupportedPromiseArrayBuffer,
} = require('./Utils');
@@ -141,6 +142,9 @@ function translateEventEmitterTypeToJavaType(
imports: Set<string>,
): string {
const typeAnnotation = eventEmitter.typeAnnotation.typeAnnotation;
throwIfUnsupportedEventEmitterPayload(eventEmitter.name, typeAnnotation);
switch (typeAnnotation.type) {
case 'StringTypeAnnotation':
return 'String';
@@ -179,12 +183,8 @@ function translateEventEmitterTypeToJavaType(
case 'ArrayTypeAnnotation':
imports.add('com.facebook.react.bridge.ReadableArray');
return 'ReadableArray';
case 'DoubleTypeAnnotation':
case 'FloatTypeAnnotation':
case 'Int32TypeAnnotation':
case 'VoidTypeAnnotation':
case 'ArrayBufferTypeAnnotation':
// TODO: Add support for these types
// Void emitters take no argument, so the caller never asks for a type.
throw new Error(
`Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`,
);
@@ -11,12 +11,15 @@
import type {NativeModuleEventEmitterShape} from '../../../CodegenSchema';
const {parseValidUnionType, toPascalCase} = require('../../Utils');
const {throwIfUnsupportedEventEmitterPayload} = require('../Utils');
function getEventEmitterTypeObjCType(
eventEmitter: NativeModuleEventEmitterShape,
): string {
const typeAnnotation = eventEmitter.typeAnnotation.typeAnnotation;
throwIfUnsupportedEventEmitterPayload(eventEmitter.name, typeAnnotation);
switch (typeAnnotation.type) {
case 'StringTypeAnnotation':
return 'NSString *_Nonnull';
@@ -39,6 +42,9 @@ function getEventEmitterTypeObjCType(
}
case 'NumberTypeAnnotation':
case 'NumberLiteralTypeAnnotation':
case 'DoubleTypeAnnotation':
case 'FloatTypeAnnotation':
case 'Int32TypeAnnotation':
return 'NSNumber *_Nonnull';
case 'BooleanTypeAnnotation':
case 'BooleanLiteralTypeAnnotation':
@@ -49,11 +55,8 @@ function getEventEmitterTypeObjCType(
return 'NSDictionary *';
case 'ArrayTypeAnnotation':
return 'NSArray<id<NSObject>> *';
case 'DoubleTypeAnnotation':
case 'FloatTypeAnnotation':
case 'Int32TypeAnnotation':
case 'VoidTypeAnnotation':
// TODO: Add support for these types
// Void emitters take no argument, so both callers skip this function.
throw new Error(
`Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`,
);
@@ -118,10 +118,29 @@ function throwIfUnsupportedPromiseArrayBuffer(
}
}
// ArrayBuffer is not emittable on any platform: Android emitters always carry a
// folly::dynamic payload, which cannot hold raw bytes, and neither the ObjC nor
// the C++ emitter contract can hand out a buffer that outlives the emit call.
// The parser rejects this too; the guard here also covers schemas built without
// going through the parser.
function throwIfUnsupportedEventEmitterPayload(
eventEmitterName: string,
typeAnnotation: NativeModuleTypeAnnotation,
): void {
if (typeAnnotation.type === 'ArrayBufferTypeAnnotation') {
throw new Error(
`Unsupported eventType for ${eventEmitterName}. Found: ${typeAnnotation.type}. ` +
'ArrayBuffer is not supported as an EventEmitter payload on any platform. ' +
'Pass the ArrayBuffer through a method instead.',
);
}
}
module.exports = {
createAliasResolver,
getModules,
isDirectRecursiveMember,
isArrayRecursiveMember,
throwIfUnsupportedEventEmitterPayload,
throwIfUnsupportedPromiseArrayBuffer,
};
@@ -130,6 +130,36 @@ const EVENT_EMITTER_MODULES: SchemaType = {
},
},
},
{
name: 'onEvent7',
optional: false,
typeAnnotation: {
type: 'EventEmitterTypeAnnotation',
typeAnnotation: {
type: 'DoubleTypeAnnotation',
},
},
},
{
name: 'onEvent8',
optional: false,
typeAnnotation: {
type: 'EventEmitterTypeAnnotation',
typeAnnotation: {
type: 'FloatTypeAnnotation',
},
},
},
{
name: 'onEvent9',
optional: false,
typeAnnotation: {
type: 'EventEmitterTypeAnnotation',
typeAnnotation: {
type: 'Int32TypeAnnotation',
},
},
},
],
methods: [
{
@@ -10,9 +10,37 @@
'use strict';
import type {SchemaType} from '../../../CodegenSchema';
const fixtures = require('../__test_fixtures__/fixtures.js');
const generator = require('../GenerateModuleH.js');
const ARRAY_BUFFER_EVENT_EMITTER_SCHEMA: SchemaType = {
modules: {
NativeSampleTurboModule: {
type: 'NativeModule',
aliasMap: {},
enumMap: {},
spec: {
eventEmitters: [
{
name: 'onBuffer',
optional: false,
typeAnnotation: {
type: 'EventEmitterTypeAnnotation',
typeAnnotation: {
type: 'ArrayBufferTypeAnnotation',
},
},
},
],
methods: [],
},
moduleName: 'SampleTurboModule',
},
},
};
describe('GenerateModuleH', () => {
Object.keys(fixtures)
.sort()
@@ -29,4 +57,13 @@ describe('GenerateModuleH', () => {
).toMatchSnapshot();
});
});
it('throws for an EventEmitter with an ArrayBuffer payload', () => {
expect(() =>
generator.generate(
'array_buffer_event_emitter_throws',
ARRAY_BUFFER_EVENT_EMITTER_SCHEMA,
),
).toThrow(/ArrayBuffer is not supported as an EventEmitter payload/);
});
});
@@ -71,4 +71,40 @@ describe('GenerateModuleHObjCpp', () => {
),
).toThrow(/Promise<ArrayBuffer> is not supported/);
});
it('throws for an EventEmitter with an ArrayBuffer payload', () => {
const schema: SchemaType = {
modules: {
NativeSampleTurboModule: {
type: 'NativeModule',
aliasMap: {},
enumMap: {},
spec: {
eventEmitters: [
{
name: 'onBuffer',
optional: false,
typeAnnotation: {
type: 'EventEmitterTypeAnnotation',
typeAnnotation: {
type: 'ArrayBufferTypeAnnotation',
},
},
},
],
methods: [],
},
moduleName: 'SampleTurboModule',
},
},
};
expect(() =>
generator.generate(
'array_buffer_event_emitter_throws',
schema,
'com.facebook.fbreact.specs',
false,
),
).toThrow(/ArrayBuffer is not supported as an EventEmitter payload/);
});
});
@@ -64,4 +64,35 @@ describe('GenerateModuleJavaSpec', () => {
generator.generate('array_buffer_promise_throws', schema),
).toThrow(/Promise<ArrayBuffer> is not supported/);
});
it('throws for an EventEmitter with an ArrayBuffer payload', () => {
const schema: SchemaType = {
modules: {
NativeSampleTurboModule: {
type: 'NativeModule',
aliasMap: {},
enumMap: {},
spec: {
eventEmitters: [
{
name: 'onBuffer',
optional: false,
typeAnnotation: {
type: 'EventEmitterTypeAnnotation',
typeAnnotation: {
type: 'ArrayBufferTypeAnnotation',
},
},
},
],
methods: [],
},
moduleName: 'SampleTurboModule',
},
},
};
expect(() =>
generator.generate('array_buffer_event_emitter_throws', schema),
).toThrow(/ArrayBuffer is not supported as an EventEmitter payload/);
});
});
@@ -1083,6 +1083,9 @@ protected:
eventEmitterMap_[\\"onEvent4\\"] = std::make_shared<AsyncEventEmitter<jsi::Value>>();
eventEmitterMap_[\\"onEvent5\\"] = std::make_shared<AsyncEventEmitter<jsi::Value>>();
eventEmitterMap_[\\"onEvent6\\"] = std::make_shared<AsyncEventEmitter<jsi::Value>>();
eventEmitterMap_[\\"onEvent7\\"] = std::make_shared<AsyncEventEmitter<jsi::Value>>();
eventEmitterMap_[\\"onEvent8\\"] = std::make_shared<AsyncEventEmitter<jsi::Value>>();
eventEmitterMap_[\\"onEvent9\\"] = std::make_shared<AsyncEventEmitter<jsi::Value>>();
}
void emitOnEvent1() {
@@ -1123,6 +1126,27 @@ protected:
return bridging::toJs(rt, eventValue, jsInvoker);
});
}
template <typename OnEvent7Type> void emitOnEvent7(OnEvent7Type value) {
static_assert(bridging::supportsFromJs<OnEvent7Type, double>, \\"value cannnot be converted to double\\");
static_cast<AsyncEventEmitter<jsi::Value>&>(*eventEmitterMap_[\\"onEvent7\\"]).emit([jsInvoker = jsInvoker_, eventValue = value](jsi::Runtime& rt) -> jsi::Value {
return bridging::toJs(rt, eventValue, jsInvoker);
});
}
template <typename OnEvent8Type> void emitOnEvent8(OnEvent8Type value) {
static_assert(bridging::supportsFromJs<OnEvent8Type, double>, \\"value cannnot be converted to double\\");
static_cast<AsyncEventEmitter<jsi::Value>&>(*eventEmitterMap_[\\"onEvent8\\"]).emit([jsInvoker = jsInvoker_, eventValue = value](jsi::Runtime& rt) -> jsi::Value {
return bridging::toJs(rt, eventValue, jsInvoker);
});
}
template <typename OnEvent9Type> void emitOnEvent9(OnEvent9Type value) {
static_assert(bridging::supportsFromJs<OnEvent9Type, int>, \\"value cannnot be converted to int\\");
static_cast<AsyncEventEmitter<jsi::Value>&>(*eventEmitterMap_[\\"onEvent9\\"]).emit([jsInvoker = jsInvoker_, eventValue = value](jsi::Runtime& rt) -> jsi::Value {
return bridging::toJs(rt, eventValue, jsInvoker);
});
}
private:
static jsi::Value __voidFunc(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
static_assert(
@@ -614,6 +614,9 @@ facebook::react::EventEmitterCallback _eventEmitterCallback;
- (void)emitOnEvent4:(BOOL)value;
- (void)emitOnEvent5:(NSDictionary *)value;
- (void)emitOnEvent6:(NSArray<id<NSObject>> *)value;
- (void)emitOnEvent7:(NSNumber *_Nonnull)value;
- (void)emitOnEvent8:(NSNumber *_Nonnull)value;
- (void)emitOnEvent9:(NSNumber *_Nonnull)value;
@end
namespace facebook::react {
@@ -292,6 +292,27 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo
}
}
protected final void emitOnEvent7(double value) {
CxxCallbackImpl eventEmitterCallback = mEventEmitterCallback;
if (eventEmitterCallback != null) {
eventEmitterCallback.invoke(\\"onEvent7\\", value);
}
}
protected final void emitOnEvent8(double value) {
CxxCallbackImpl eventEmitterCallback = mEventEmitterCallback;
if (eventEmitterCallback != null) {
eventEmitterCallback.invoke(\\"onEvent8\\", value);
}
}
protected final void emitOnEvent9(double value) {
CxxCallbackImpl eventEmitterCallback = mEventEmitterCallback;
if (eventEmitterCallback != null) {
eventEmitterCallback.invoke(\\"onEvent9\\", value);
}
}
@ReactMethod
@DoNotStrip
public abstract void voidFunc();
@@ -280,6 +280,9 @@ NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const JavaTurboMo
eventEmitterMap_[\\"onEvent4\\"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
eventEmitterMap_[\\"onEvent5\\"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
eventEmitterMap_[\\"onEvent6\\"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
eventEmitterMap_[\\"onEvent7\\"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
eventEmitterMap_[\\"onEvent8\\"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
eventEmitterMap_[\\"onEvent9\\"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
configureEventEmitterCallback();
}
@@ -366,6 +366,27 @@ Map {
eventEmitterCallback(\\"onEvent6\\", value);
}
}
- (void)emitOnEvent7:(NSNumber *_Nonnull)value
{
auto eventEmitterCallback = _eventEmitterCallback;
if (eventEmitterCallback) {
eventEmitterCallback(\\"onEvent7\\", value);
}
}
- (void)emitOnEvent8:(NSNumber *_Nonnull)value
{
auto eventEmitterCallback = _eventEmitterCallback;
if (eventEmitterCallback) {
eventEmitterCallback(\\"onEvent8\\", value);
}
}
- (void)emitOnEvent9:(NSNumber *_Nonnull)value
{
auto eventEmitterCallback = _eventEmitterCallback;
if (eventEmitterCallback) {
eventEmitterCallback(\\"onEvent9\\", value);
}
}
- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper
{
@@ -391,6 +412,9 @@ namespace facebook::react {
eventEmitterMap_[\\"onEvent4\\"] = std::make_shared<AsyncEventEmitter<id>>();
eventEmitterMap_[\\"onEvent5\\"] = std::make_shared<AsyncEventEmitter<id>>();
eventEmitterMap_[\\"onEvent6\\"] = std::make_shared<AsyncEventEmitter<id>>();
eventEmitterMap_[\\"onEvent7\\"] = std::make_shared<AsyncEventEmitter<id>>();
eventEmitterMap_[\\"onEvent8\\"] = std::make_shared<AsyncEventEmitter<id>>();
eventEmitterMap_[\\"onEvent9\\"] = std::make_shared<AsyncEventEmitter<id>>();
setEventEmitterCallback([eventEmitterMap = eventEmitterMap_](const std::string &name, id value) {
auto it = eventEmitterMap.find(name);
if (it != eventEmitterMap.end() && it->second) {
@@ -18,6 +18,7 @@ const {
throwIfArgumentPropsAreNull,
throwIfArrayElementTypeAnnotationIsUnsupported,
throwIfBubblingTypeIsNull,
throwIfEventEmitterPayloadTypeIsUnsupported,
throwIfEventHasNoName,
throwIfIncorrectModuleRegistryCallArgument,
throwIfIncorrectModuleRegistryCallTypeParameterParserError,
@@ -825,6 +826,35 @@ describe('throwIfArrayElementTypeAnnotationIsUnsupported', () => {
});
});
describe('throwIfEventEmitterPayloadTypeIsUnsupported', () => {
const {
UnsupportedModuleEventEmitterPayloadTypeParserError,
} = require('../errors.js');
const moduleName = 'moduleName';
it('throws the error if the payload is an ArrayBuffer', () => {
expect(() => {
throwIfEventEmitterPayloadTypeIsUnsupported(
moduleName,
undefined,
'onBuffer',
{type: 'ArrayBufferTypeAnnotation'},
);
}).toThrow(UnsupportedModuleEventEmitterPayloadTypeParserError);
});
it('does not throw the error if the payload is a supported type', () => {
expect(() => {
throwIfEventEmitterPayloadTypeIsUnsupported(
moduleName,
undefined,
'onCount',
{type: 'DoubleTypeAnnotation'},
);
}).not.toThrow();
});
});
describe('throwIfPartialNotAnnotatingTypeParameter', () => {
const flowParser = new FlowParser();
const typescriptParser = new TypeScriptParser();
@@ -27,6 +27,7 @@ const {
UnsupportedArrayElementTypeAnnotationParserError,
UnsupportedFunctionParamTypeAnnotationParserError,
UnsupportedFunctionReturnTypeAnnotationParserError,
UnsupportedModuleEventEmitterPayloadTypeParserError,
UnsupportedModuleEventEmitterPropertyParserError,
UnsupportedModuleEventEmitterTypePropertyParserError,
UnsupportedModulePropertyParserError,
@@ -196,6 +197,25 @@ function throwIfEventEmitterEventTypeIsUnsupported(
}
}
// ArrayBuffer is not emittable on any platform: Android emitters always carry a
// folly::dynamic payload, which cannot hold raw bytes, and neither the ObjC nor
// the C++ emitter contract can hand out a buffer that outlives the emit call.
function throwIfEventEmitterPayloadTypeIsUnsupported(
nativeModuleName: string,
propertyValue: $FlowFixMe,
propertyName: string,
payloadTypeAnnotation: NativeModuleTypeAnnotation,
) {
if (payloadTypeAnnotation.type === 'ArrayBufferTypeAnnotation') {
throw new UnsupportedModuleEventEmitterPayloadTypeParserError(
nativeModuleName,
propertyValue,
propertyName,
'ArrayBuffer',
);
}
}
function throwIfModuleTypeIsUnsupported(
nativeModuleName: string,
propertyValue: $FlowFixMe,
@@ -418,6 +438,7 @@ module.exports = {
throwIfUntypedModule,
throwIfEventEmitterTypeIsUnsupported,
throwIfEventEmitterEventTypeIsUnsupported,
throwIfEventEmitterPayloadTypeIsUnsupported,
throwIfModuleTypeIsUnsupported,
throwIfMoreThanOneModuleInterfaceParserError,
throwIfUnsupportedFunctionParamTypeAnnotationParserError,
+16
View File
@@ -111,6 +111,21 @@ class UnsupportedModuleEventEmitterPropertyParserError extends ParserError {
}
}
class UnsupportedModuleEventEmitterPayloadTypeParserError extends ParserError {
constructor(
nativeModuleName: string,
propertyValue: $FlowFixMe,
propertyName: string,
payloadType: string,
) {
super(
nativeModuleName,
propertyValue,
`EventEmitter '${propertyName}' cannot have payload type '${payloadType}'.`,
);
}
}
class UnsupportedModulePropertyParserError extends ParserError {
constructor(
nativeModuleName: string,
@@ -456,6 +471,7 @@ module.exports = {
UnsupportedEnumDeclarationParserError,
UnsupportedModuleEventEmitterTypePropertyParserError,
UnsupportedModuleEventEmitterPropertyParserError,
UnsupportedModuleEventEmitterPayloadTypeParserError,
UnsupportedModulePropertyParserError,
UnsupportedObjectPropertyTypeAnnotationParserError,
UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError,
@@ -350,6 +350,31 @@ export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_EVENT_EMITTER = `
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import type {EventEmitter} from '../CodegenTypes';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export interface Spec extends TurboModule {
+onBuffer: EventEmitter<ArrayBuffer>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
module.exports = {
NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT,
NATIVE_MODULES_WITH_UNNAMED_PARAMS,
@@ -364,4 +389,5 @@ module.exports = {
NUMERIC_VALUES_ENUM_NATIVE_MODULE,
MAP_WITH_EXTRA_KEYS_NATIVE_MODULE,
NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_OBJECT_PROPERTY,
NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_EVENT_EMITTER,
};
@@ -667,7 +667,7 @@ const NATIVE_MODULE_WITH_EVENT_EMITTERS = `
'use strict';
import type {TurboModule} from '../RCTExport';
import type {EventEmitter} from '../CodegenTypes';
import type {Double, EventEmitter, Float, Int32} from '../CodegenTypes';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export type ObjectStruct = {
@@ -686,6 +686,9 @@ export interface Spec extends TurboModule {
+onEvent5: EventEmitter<ObjectStruct>;
+onEvent6: EventEmitter<ObjectStruct[]>;
+onEvent7: EventEmitter<MappedObject>;
+onEvent8: EventEmitter<Double>;
+onEvent9: EventEmitter<Float>;
+onEvent10: EventEmitter<Int32>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
@@ -6,6 +6,8 @@ exports[`RN Codegen Flow Parser Fails with error message MAP_WITH_EXTRA_KEYS_NAT
exports[`RN Codegen Flow Parser Fails with error message MIXED_VALUES_ENUM_NATIVE_MODULE 1`] = `"Module NativeSampleTurboModule: Failed parsing the enum SomeEnum in NativeSampleTurboModule with the error: Enums can not be mixed- they all must be either blank, number, or string values."`;
exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_EVENT_EMITTER 1`] = `"Module NativeSampleTurboModule: EventEmitter 'onBuffer' cannot have payload type 'ArrayBuffer'."`;
exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_OBJECT_PROPERTY 1`] = `"Module NativeSampleTurboModule: Object property '[object Object]' cannot have type 'ArrayBuffer'."`;
exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT 1`] = `"Module NativeSampleTurboModule: Generic 'Array' must have type parameters."`;
@@ -2119,6 +2121,36 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_EVENT_EM
}
}
}
},
{
'name': 'onEvent8',
'optional': false,
'typeAnnotation': {
'type': 'EventEmitterTypeAnnotation',
'typeAnnotation': {
'type': 'DoubleTypeAnnotation'
}
}
},
{
'name': 'onEvent9',
'optional': false,
'typeAnnotation': {
'type': 'EventEmitterTypeAnnotation',
'typeAnnotation': {
'type': 'FloatTypeAnnotation'
}
}
},
{
'name': 'onEvent10',
'optional': false,
'typeAnnotation': {
'type': 'EventEmitterTypeAnnotation',
'typeAnnotation': {
'type': 'Int32TypeAnnotation'
}
}
}
],
'methods': []
@@ -42,6 +42,7 @@ import type {
const {
throwIfConfigNotfound,
throwIfEventEmitterEventTypeIsUnsupported,
throwIfEventEmitterPayloadTypeIsUnsupported,
throwIfEventEmitterTypeIsUnsupported,
throwIfIncorrectModuleRegistryCallArgument,
throwIfIncorrectModuleRegistryCallTypeParameterParserError,
@@ -551,6 +552,13 @@ function buildEventEmitterSchema(
parser,
);
throwIfEventEmitterPayloadTypeIsUnsupported(
hasteModuleName,
typeAnnotation.typeParameters.params[0],
key.name,
eventTypeAnnotation,
);
return {
name: eventemitterName,
optional: Boolean(property.optional),
@@ -286,6 +286,27 @@ export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_EVENT_EMITTER = `
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
readonly onBuffer: EventEmitter<ArrayBuffer>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
module.exports = {
NATIVE_MODULES_WITH_UNNAMED_PARAMS,
NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE,
@@ -299,4 +320,5 @@ module.exports = {
NUMERIC_VALUES_ENUM_NATIVE_MODULE,
MAP_WITH_EXTRA_KEYS_NATIVE_MODULE,
NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_OBJECT_PROPERTY,
NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_EVENT_EMITTER,
};
@@ -766,6 +766,7 @@ const NATIVE_MODULE_WITH_EVENT_EMITTERS = `
*/
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import type {Double, Float, Int32} from 'react-native/Libraries/Types/CodegenTypes';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export type ObjectStruct = {
@@ -784,6 +785,9 @@ export interface Spec extends TurboModule {
readonly onEvent5: EventEmitter<ObjectStruct>;
readonly onEvent6: EventEmitter<ObjectStruct[]>;
readonly onEvent7: EventEmitter<MappedObject>;
readonly onEvent8: EventEmitter<Double>;
readonly onEvent9: EventEmitter<Float>;
readonly onEvent10: EventEmitter<Int32>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
@@ -6,6 +6,8 @@ exports[`RN Codegen TypeScript Parser Fails with error message MAP_WITH_EXTRA_KE
exports[`RN Codegen TypeScript Parser Fails with error message MIXED_VALUES_ENUM_NATIVE_MODULE 1`] = `"Module NativeSampleTurboModule: Failed parsing the enum SomeEnum in NativeSampleTurboModule with the error: Enum values can not be mixed. They all must be either blank, number, or string values."`;
exports[`RN Codegen TypeScript Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_EVENT_EMITTER 1`] = `"Module NativeSampleTurboModule: EventEmitter 'onBuffer' cannot have payload type 'ArrayBuffer'."`;
exports[`RN Codegen TypeScript Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_BUFFER_IN_OBJECT_PROPERTY 1`] = `"Module NativeSampleTurboModule: Object property '[object Object]' cannot have type 'ArrayBuffer'."`;
exports[`RN Codegen TypeScript Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT 1`] = `"Module NativeSampleTurboModule: Generic 'Array' must have type parameters."`;
@@ -2343,6 +2345,36 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_EV
}
}
}
},
{
'name': 'onEvent8',
'optional': false,
'typeAnnotation': {
'type': 'EventEmitterTypeAnnotation',
'typeAnnotation': {
'type': 'DoubleTypeAnnotation'
}
}
},
{
'name': 'onEvent9',
'optional': false,
'typeAnnotation': {
'type': 'EventEmitterTypeAnnotation',
'typeAnnotation': {
'type': 'FloatTypeAnnotation'
}
}
},
{
'name': 'onEvent10',
'optional': false,
'typeAnnotation': {
'type': 'EventEmitterTypeAnnotation',
'typeAnnotation': {
'type': 'Int32TypeAnnotation'
}
}
}
],
'methods': []