diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js index af7f00aa3008..d2395dd297cf 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js @@ -40,6 +40,7 @@ const { getModules, isArrayRecursiveMember, isDirectRecursiveMember, + throwIfUnsupportedEventEmitterPayload, } = require('./Utils'); type FilesOutput = Map; @@ -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`; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js index 9dc0b42ce8f6..52e8aa994d39 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js @@ -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 { 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}`, ); diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js index b5ea25048fef..774c4afb4903 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js @@ -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> *'; - 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}`, ); diff --git a/packages/react-native-codegen/src/generators/modules/Utils.js b/packages/react-native-codegen/src/generators/modules/Utils.js index 8cd8d37ff096..9c35c3e5bea0 100644 --- a/packages/react-native-codegen/src/generators/modules/Utils.js +++ b/packages/react-native-codegen/src/generators/modules/Utils.js @@ -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, }; diff --git a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js index 83cc98bef054..fca175285f76 100644 --- a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js @@ -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: [ { diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleH-test.js b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleH-test.js index 3bc24589a19c..48e619215f0d 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleH-test.js +++ b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleH-test.js @@ -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/); + }); }); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js index c151a4aa1cc1..5dd67931bc09 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js +++ b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js @@ -71,4 +71,40 @@ describe('GenerateModuleHObjCpp', () => { ), ).toThrow(/Promise 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/); + }); }); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleJavaSpec-test.js b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleJavaSpec-test.js index 3cbcf9747179..a413ae0c27a0 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleJavaSpec-test.js +++ b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleJavaSpec-test.js @@ -64,4 +64,35 @@ describe('GenerateModuleJavaSpec', () => { generator.generate('array_buffer_promise_throws', schema), ).toThrow(/Promise 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/); + }); }); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap index f2b1d6ad49ce..466e7e67626e 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap @@ -1083,6 +1083,9 @@ protected: eventEmitterMap_[\\"onEvent4\\"] = std::make_shared>(); eventEmitterMap_[\\"onEvent5\\"] = std::make_shared>(); eventEmitterMap_[\\"onEvent6\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent7\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent8\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent9\\"] = std::make_shared>(); } void emitOnEvent1() { @@ -1123,6 +1126,27 @@ protected: return bridging::toJs(rt, eventValue, jsInvoker); }); } + + template void emitOnEvent7(OnEvent7Type value) { + static_assert(bridging::supportsFromJs, \\"value cannnot be converted to double\\"); + static_cast&>(*eventEmitterMap_[\\"onEvent7\\"]).emit([jsInvoker = jsInvoker_, eventValue = value](jsi::Runtime& rt) -> jsi::Value { + return bridging::toJs(rt, eventValue, jsInvoker); + }); + } + + template void emitOnEvent8(OnEvent8Type value) { + static_assert(bridging::supportsFromJs, \\"value cannnot be converted to double\\"); + static_cast&>(*eventEmitterMap_[\\"onEvent8\\"]).emit([jsInvoker = jsInvoker_, eventValue = value](jsi::Runtime& rt) -> jsi::Value { + return bridging::toJs(rt, eventValue, jsInvoker); + }); + } + + template void emitOnEvent9(OnEvent9Type value) { + static_assert(bridging::supportsFromJs, \\"value cannnot be converted to int\\"); + static_cast&>(*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( diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap index 4726cdcd0d96..b1ce7953d5f3 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap @@ -614,6 +614,9 @@ facebook::react::EventEmitterCallback _eventEmitterCallback; - (void)emitOnEvent4:(BOOL)value; - (void)emitOnEvent5:(NSDictionary *)value; - (void)emitOnEvent6:(NSArray> *)value; +- (void)emitOnEvent7:(NSNumber *_Nonnull)value; +- (void)emitOnEvent8:(NSNumber *_Nonnull)value; +- (void)emitOnEvent9:(NSNumber *_Nonnull)value; @end namespace facebook::react { diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap index 14722e2179f5..c9d66a175adf 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap @@ -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(); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap index 547344fda403..9207d5d26a5d 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap @@ -280,6 +280,9 @@ NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const JavaTurboMo eventEmitterMap_[\\"onEvent4\\"] = std::make_shared>(); eventEmitterMap_[\\"onEvent5\\"] = std::make_shared>(); eventEmitterMap_[\\"onEvent6\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent7\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent8\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent9\\"] = std::make_shared>(); configureEventEmitterCallback(); } diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap index f70403ac89b6..0395bb0b6548 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap @@ -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>(); eventEmitterMap_[\\"onEvent5\\"] = std::make_shared>(); eventEmitterMap_[\\"onEvent6\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent7\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent8\\"] = std::make_shared>(); + eventEmitterMap_[\\"onEvent9\\"] = std::make_shared>(); setEventEmitterCallback([eventEmitterMap = eventEmitterMap_](const std::string &name, id value) { auto it = eventEmitterMap.find(name); if (it != eventEmitterMap.end() && it->second) { diff --git a/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js b/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js index 6e893f6e4197..ec101f4a0bd0 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js @@ -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(); diff --git a/packages/react-native-codegen/src/parsers/error-utils.js b/packages/react-native-codegen/src/parsers/error-utils.js index 348f688f9b7a..3eb3d5eef65d 100644 --- a/packages/react-native-codegen/src/parsers/error-utils.js +++ b/packages/react-native-codegen/src/parsers/error-utils.js @@ -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, diff --git a/packages/react-native-codegen/src/parsers/errors.js b/packages/react-native-codegen/src/parsers/errors.js index 7e0b2277930f..8cd8706eac56 100644 --- a/packages/react-native-codegen/src/parsers/errors.js +++ b/packages/react-native-codegen/src/parsers/errors.js @@ -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, diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js index 515098be5462..9c55884ac0a7 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js @@ -350,6 +350,31 @@ export default TurboModuleRegistry.getEnforcing('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; +} + +export default TurboModuleRegistry.getEnforcing('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, }; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js index 53605cbe10ea..9cc97dbef580 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js @@ -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; +onEvent6: EventEmitter; +onEvent7: EventEmitter; + +onEvent8: EventEmitter; + +onEvent9: EventEmitter; + +onEvent10: EventEmitter; } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap index 5309c7d0c616..64d69f752c6f 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap +++ b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap @@ -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': [] diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index 330e52fe8c76..a8b64de42408 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -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), diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/failures.js b/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/failures.js index a59c138df1d8..11b7f2b08519 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/failures.js @@ -286,6 +286,27 @@ export default TurboModuleRegistry.getEnforcing('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; +} + +export default TurboModuleRegistry.getEnforcing('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, }; diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js index c405627b93e4..ddd35c961d9c 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js @@ -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; readonly onEvent6: EventEmitter; readonly onEvent7: EventEmitter; + readonly onEvent8: EventEmitter; + readonly onEvent9: EventEmitter; + readonly onEvent10: EventEmitter; } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap b/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap index 4b098b27af55..27c8ba733690 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap +++ b/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap @@ -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': []