From a669de1dd2d86e0780dcb115d5d236e74a2bfbfa Mon Sep 17 00:00:00 2001 From: Eli White Date: Mon, 7 Oct 2024 16:56:04 -0700 Subject: [PATCH] Parse string unions as StringLiteralUnionTypeAnnotation (#46845) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46845 Previously, the parser was throwing away the values of a string union when storing it in the schema. It would only store the union as ``` { type: 'UnionTypeAnnotation', memberType: 'StringTypeAnnotation' } ``` We now track the string literals through the parser and store them in the schema: ``` { type: 'StringLiteralUnionTypeAnnotation', types: [ { type: 'StringLiteralTypeAnnotation' value: 'light' }, { type: 'StringLiteralTypeAnnotation' value: 'dark' }, ], } ``` We aren't changing the generators, those still just output "string". They could eventually be made smarter. The value of this is that the compat checker can now error when the union changes. Changelog: [Internal] Reviewed By: yungsters Differential Revision: D63917685 fbshipit-source-id: 34a10e1f1910d2935837a3659f66049fd4473134 --- .../src/CodegenSchema.d.ts | 7 ++ .../react-native-codegen/src/CodegenSchema.js | 7 ++ .../generators/modules/GenerateModuleCpp.js | 2 + .../src/generators/modules/GenerateModuleH.js | 2 + .../modules/GenerateModuleJavaSpec.js | 8 ++ .../modules/GenerateModuleJniCpp.js | 6 ++ .../GenerateModuleObjCpp/StructCollector.js | 2 + .../header/serializeConstantsStruct.js | 4 + .../header/serializeRegularStruct.js | 4 + .../serializeEventEmitter.js | 2 + .../GenerateModuleObjCpp/serializeMethod.js | 8 ++ .../modules/__test_fixtures__/fixtures.js | 34 ++++++++ .../GenerateModuleCpp-test.js.snap | 10 ++- .../GenerateModuleH-test.js.snap | 20 ++--- .../GenerateModuleHObjCpp-test.js.snap | 3 +- .../GenerateModuleJavaSpec-test.js.snap | 2 +- .../GenerateModuleJniCpp-test.js.snap | 4 +- .../GenerateModuleMm-test.js.snap | 4 +- .../__tests__/parsers-primitives-test.js | 72 ++++++++++++----- .../module-parser-snapshot-test.js.snap | 81 ++++++++++++++++--- .../src/parsers/flow/parser.js | 6 ++ .../src/parsers/parser.js | 8 ++ .../src/parsers/parserMock.js | 6 ++ .../src/parsers/parsers-primitives.js | 40 ++++++++- ...script-module-parser-snapshot-test.js.snap | 60 ++++++++++++-- .../src/parsers/typescript/parser.js | 6 ++ 26 files changed, 351 insertions(+), 57 deletions(-) diff --git a/packages/react-native-codegen/src/CodegenSchema.d.ts b/packages/react-native-codegen/src/CodegenSchema.d.ts index 045a5b4ccaf..d16379472fa 100644 --- a/packages/react-native-codegen/src/CodegenSchema.d.ts +++ b/packages/react-native-codegen/src/CodegenSchema.d.ts @@ -295,6 +295,11 @@ export interface NativeModuleStringLiteralTypeAnnotation { readonly value: string; } +export interface NativeModuleStringLiteralUnionTypeAnnotation { + readonly type: 'StringLiteralUnionTypeAnnotation'; + readonly types: NativeModuleStringLiteralTypeAnnotation[]; +} + export interface NativeModuleNumberTypeAnnotation { readonly type: 'NumberTypeAnnotation'; } @@ -377,6 +382,7 @@ export type NativeModuleEventEmitterBaseTypeAnnotation = | NativeModuleNumberTypeAnnotation | NativeModuleStringTypeAnnotation | NativeModuleStringLiteralTypeAnnotation + | NativeModuleStringLiteralUnionTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleGenericObjectTypeAnnotation | VoidTypeAnnotation; @@ -391,6 +397,7 @@ export type NativeModuleEventEmitterTypeAnnotation = export type NativeModuleBaseTypeAnnotation = | NativeModuleStringTypeAnnotation | NativeModuleStringLiteralTypeAnnotation + | NativeModuleStringLiteralUnionTypeAnnotation | NativeModuleNumberTypeAnnotation | NativeModuleInt32TypeAnnotation | NativeModuleDoubleTypeAnnotation diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index 089a83098f9..4398919e775 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -46,6 +46,11 @@ export type StringLiteralTypeAnnotation = $ReadOnly<{ value: string, }>; +export type StringLiteralUnionTypeAnnotation = $ReadOnly<{ + type: 'StringLiteralUnionTypeAnnotation', + types: $ReadOnlyArray, +}>; + export type StringEnumTypeAnnotation = $ReadOnly<{ type: 'StringEnumTypeAnnotation', options: $ReadOnlyArray, @@ -363,6 +368,7 @@ type NativeModuleEventEmitterBaseTypeAnnotation = | NativeModuleNumberTypeAnnotation | StringTypeAnnotation | StringLiteralTypeAnnotation + | StringLiteralUnionTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleGenericObjectTypeAnnotation | VoidTypeAnnotation; @@ -377,6 +383,7 @@ export type NativeModuleEventEmitterTypeAnnotation = export type NativeModuleBaseTypeAnnotation = | StringTypeAnnotation | StringLiteralTypeAnnotation + | StringLiteralUnionTypeAnnotation | NativeModuleNumberTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js index 6a30a71fd9f..1aa3c73dbbb 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js @@ -163,6 +163,8 @@ function serializeArg( return wrap(val => `${val}.asString(rt)`); case 'StringLiteralTypeAnnotation': return wrap(val => `${val}.asString(rt)`); + case 'StringLiteralUnionTypeAnnotation': + return wrap(val => `${val}.asString(rt)`); case 'BooleanTypeAnnotation': return wrap(val => `${val}.asBool()`); case 'EnumDeclaration': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js index 2a76a9054d8..f7c4d674881 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js @@ -175,6 +175,8 @@ function translatePrimitiveJSTypeToCpp( return wrapOptional('jsi::String', isRequired); case 'StringLiteralTypeAnnotation': return wrapOptional('jsi::String', isRequired); + case 'StringLiteralUnionTypeAnnotation': + return wrapOptional('jsi::String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js index 0d130bc0777..779b9ac3f5f 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js @@ -133,6 +133,8 @@ function translateEventEmitterTypeToJavaType( return 'String'; case 'StringLiteralTypeAnnotation': return 'String'; + case 'StringLiteralUnionTypeAnnotation': + return 'String'; case 'NumberTypeAnnotation': case 'FloatTypeAnnotation': case 'DoubleTypeAnnotation': @@ -197,6 +199,8 @@ function translateFunctionParamToJavaType( return wrapOptional('String', isRequired); case 'StringLiteralTypeAnnotation': return wrapOptional('String', isRequired); + case 'StringLiteralUnionTypeAnnotation': + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -289,6 +293,8 @@ function translateFunctionReturnTypeToJavaType( return wrapOptional('String', isRequired); case 'StringLiteralTypeAnnotation': return wrapOptional('String', isRequired); + case 'StringLiteralUnionTypeAnnotation': + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -401,6 +407,8 @@ function getFalsyReturnStatementFromReturnType( return nullable ? 'return null;' : 'return "";'; case 'StringLiteralTypeAnnotation': return nullable ? 'return null;' : 'return "";'; + case 'StringLiteralUnionTypeAnnotation': + return nullable ? 'return null;' : 'return "";'; case 'ObjectTypeAnnotation': return 'return null;'; case 'GenericObjectTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js index 8b9228c15f4..3beb1c3fa1e 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js @@ -167,6 +167,8 @@ function translateReturnTypeToKind( return 'StringKind'; case 'StringLiteralTypeAnnotation': return 'StringKind'; + case 'StringLiteralUnionTypeAnnotation': + return 'StringKind'; case 'BooleanTypeAnnotation': return 'BooleanKind'; case 'EnumDeclaration': @@ -248,6 +250,8 @@ function translateParamTypeToJniType( return 'Ljava/lang/String;'; case 'StringLiteralTypeAnnotation': return 'Ljava/lang/String;'; + case 'StringLiteralUnionTypeAnnotation': + return 'Ljava/lang/String;'; case 'BooleanTypeAnnotation': return !isRequired ? 'Ljava/lang/Boolean;' : 'Z'; case 'EnumDeclaration': @@ -326,6 +330,8 @@ function translateReturnTypeToJniType( return 'Ljava/lang/String;'; case 'StringLiteralTypeAnnotation': return 'Ljava/lang/String;'; + case 'StringLiteralUnionTypeAnnotation': + return 'Ljava/lang/String;'; case 'BooleanTypeAnnotation': return nullable ? 'Ljava/lang/Boolean;' : 'Z'; case 'EnumDeclaration': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js index 0609689fe28..2792697aec1 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js @@ -23,6 +23,7 @@ import type { NativeModuleObjectTypeAnnotation, StringTypeAnnotation, StringLiteralTypeAnnotation, + StringLiteralUnionTypeAnnotation, NativeModuleTypeAliasTypeAnnotation, Nullable, ReservedTypeAnnotation, @@ -60,6 +61,7 @@ export type StructProperty = $ReadOnly<{ export type StructTypeAnnotation = | StringTypeAnnotation | StringLiteralTypeAnnotation + | StringLiteralUnionTypeAnnotation | NativeModuleNumberTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js index 8ceb6447277..9fe6e73fc70 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js @@ -96,6 +96,8 @@ function toObjCType( return 'NSString *'; case 'StringLiteralTypeAnnotation': return 'NSString *'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *'; case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -177,6 +179,8 @@ function toObjCValue( return value; case 'StringLiteralTypeAnnotation': return value; + case 'StringLiteralUnionTypeAnnotation': + return value; case 'NumberTypeAnnotation': return wrapPrimitive('double'); case 'FloatTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js index e68693af6b8..dbf0fbefb50 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js @@ -87,6 +87,8 @@ function toObjCType( return 'NSString *'; case 'StringLiteralTypeAnnotation': return 'NSString *'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *'; case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -167,6 +169,8 @@ function toObjCValue( return RCTBridgingTo('String'); case 'StringLiteralTypeAnnotation': return RCTBridgingTo('String'); + case 'StringLiteralUnionTypeAnnotation': + return RCTBridgingTo('String'); case 'NumberTypeAnnotation': return RCTBridgingTo('Double'); case 'FloatTypeAnnotation': 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 9f1d5b077fa..ae3ff4e198e 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js @@ -22,6 +22,8 @@ function getEventEmitterTypeObjCType( return 'NSString *_Nonnull'; case 'StringLiteralTypeAnnotation': return 'NSString *_Nonnull'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *_Nonnull'; case 'NumberTypeAnnotation': return 'NSNumber *_Nonnull'; case 'BooleanTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js index c5ea2b66840..3c024a65e44 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js @@ -259,6 +259,8 @@ function getParamObjCType( return notStruct(wrapOptional('NSString *', !nullable)); case 'StringLiteralTypeAnnotation': return notStruct(wrapOptional('NSString *', !nullable)); + case 'StringLiteralUnionTypeAnnotation': + return notStruct(wrapOptional('NSString *', !nullable)); case 'NumberTypeAnnotation': return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'FloatTypeAnnotation': @@ -336,6 +338,10 @@ function getReturnObjCType( // TODO: Can NSString * returns not be _Nullable? // In the legacy codegen, we don't surround NSSTring * with _Nullable return wrapOptional('NSString *', isRequired); + case 'StringLiteralUnionTypeAnnotation': + // TODO: Can NSString * returns not be _Nullable? + // In the legacy codegen, we don't surround NSSTring * with _Nullable + return wrapOptional('NSString *', isRequired); case 'NumberTypeAnnotation': return wrapOptional('NSNumber *', isRequired); case 'FloatTypeAnnotation': @@ -404,6 +410,8 @@ function getReturnJSType( return 'StringKind'; case 'StringLiteralTypeAnnotation': return 'StringKind'; + case 'StringLiteralUnionTypeAnnotation': + return 'StringKind'; case 'NumberTypeAnnotation': return 'NumberKind'; case 'FloatTypeAnnotation': 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 058ad1e7146..6e34e330eff 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 @@ -2316,6 +2316,23 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = { memberType: 'StringTypeAnnotation', }, }, + { + name: 'y-literal', + optional: false, + typeAnnotation: { + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], + }, + }, { name: 'z', optional: false, @@ -2626,6 +2643,23 @@ const UNION_MODULE: SchemaType = { memberType: 'StringTypeAnnotation', }, }, + { + name: 'chooseStringLiteral', + optional: false, + typeAnnotation: { + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], + }, + }, ], }, }, diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap index 4fca1c7436f..ebdad85f6ed 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap @@ -216,7 +216,8 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion(jsi: rt, count <= 0 ? throw jsi::JSError(rt, \\"Expected argument in position 0 to be passed\\") : args[0].asNumber(), count <= 1 ? throw jsi::JSError(rt, \\"Expected argument in position 1 to be passed\\") : args[1].asString(rt), - count <= 2 ? throw jsi::JSError(rt, \\"Expected argument in position 2 to be passed\\") : args[2].asObject(rt) + count <= 2 ? throw jsi::JSError(rt, \\"Expected argument in position 2 to be passed\\") : args[2].asString(rt), + count <= 3 ? throw jsi::JSError(rt, \\"Expected argument in position 3 to be passed\\") : args[3].asObject(rt) ); } static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getValue(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { @@ -309,7 +310,7 @@ NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getObject}; methodMap_[\\"getSet\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getSet}; methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getString}; - methodMap_[\\"getUnion\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion}; + methodMap_[\\"getUnion\\"] = MethodMetadata {4, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion}; methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getValue}; methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getValueWithCallback}; methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getValueWithPromise}; @@ -764,13 +765,14 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion(jsi: count <= 0 ? throw jsi::JSError(rt, \\"Expected argument in position 0 to be passed\\") : args[0].asNumber(), count <= 1 ? throw jsi::JSError(rt, \\"Expected argument in position 1 to be passed\\") : args[1].asNumber(), count <= 2 ? throw jsi::JSError(rt, \\"Expected argument in position 2 to be passed\\") : args[2].asObject(rt), - count <= 3 ? throw jsi::JSError(rt, \\"Expected argument in position 3 to be passed\\") : args[3].asString(rt) + count <= 3 ? throw jsi::JSError(rt, \\"Expected argument in position 3 to be passed\\") : args[3].asString(rt), + count <= 4 ? throw jsi::JSError(rt, \\"Expected argument in position 4 to be passed\\") : args[4].asString(rt) ); } NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr jsInvoker) : TurboModule(\\"SampleTurboModule\\", jsInvoker) { - methodMap_[\\"getUnion\\"] = MethodMetadata {4, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion}; + methodMap_[\\"getUnion\\"] = MethodMetadata {5, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion}; } 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 ede5b6c5771..4f64f741130 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 @@ -693,7 +693,7 @@ public: virtual jsi::Object getObject(jsi::Runtime &rt, jsi::Object arg) = 0; virtual jsi::Array getSet(jsi::Runtime &rt, jsi::Array arg) = 0; virtual jsi::String getString(jsi::Runtime &rt, jsi::String arg) = 0; - virtual jsi::String getUnion(jsi::Runtime &rt, double x, jsi::String y, jsi::Object z) = 0; + virtual jsi::String getUnion(jsi::Runtime &rt, double x, jsi::String y, jsi::String y-literal, jsi::Object z) = 0; virtual jsi::Object getValue(jsi::Runtime &rt, double x, jsi::String y, jsi::Object z) = 0; virtual void getValueWithCallback(jsi::Runtime &rt, jsi::Function callback) = 0; virtual jsi::Value getValueWithPromise(jsi::Runtime &rt, bool error) = 0; @@ -855,13 +855,13 @@ private: return bridging::callFromJs( rt, &T::getString, jsInvoker_, instance_, std::move(arg)); } - jsi::String getUnion(jsi::Runtime &rt, double x, jsi::String y, jsi::Object z) override { + jsi::String getUnion(jsi::Runtime &rt, double x, jsi::String y, jsi::String y-literal, jsi::Object z) override { static_assert( - bridging::getParameterCount(&T::getUnion) == 4, - \\"Expected getUnion(...) to have 4 parameters\\"); + bridging::getParameterCount(&T::getUnion) == 5, + \\"Expected getUnion(...) to have 5 parameters\\"); return bridging::callFromJs( - rt, &T::getUnion, jsInvoker_, instance_, std::move(x), std::move(y), std::move(z)); + rt, &T::getUnion, jsInvoker_, instance_, std::move(x), std::move(y), std::move(y-literal), std::move(z)); } jsi::Object getValue(jsi::Runtime &rt, double x, jsi::String y, jsi::Object z) override { static_assert( @@ -2520,7 +2520,7 @@ protected: NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr jsInvoker); public: - virtual jsi::Object getUnion(jsi::Runtime &rt, double chooseInt, double chooseFloat, jsi::Object chooseObject, jsi::String chooseString) = 0; + virtual jsi::Object getUnion(jsi::Runtime &rt, double chooseInt, double chooseFloat, jsi::Object chooseObject, jsi::String chooseString, jsi::String chooseStringLiteral) = 0; }; @@ -2551,13 +2551,13 @@ private: } - jsi::Object getUnion(jsi::Runtime &rt, double chooseInt, double chooseFloat, jsi::Object chooseObject, jsi::String chooseString) override { + jsi::Object getUnion(jsi::Runtime &rt, double chooseInt, double chooseFloat, jsi::Object chooseObject, jsi::String chooseString, jsi::String chooseStringLiteral) override { static_assert( - bridging::getParameterCount(&T::getUnion) == 5, - \\"Expected getUnion(...) to have 5 parameters\\"); + bridging::getParameterCount(&T::getUnion) == 6, + \\"Expected getUnion(...) to have 6 parameters\\"); return bridging::callFromJs( - rt, &T::getUnion, jsInvoker_, instance_, std::move(chooseInt), std::move(chooseFloat), std::move(chooseObject), std::move(chooseString)); + rt, &T::getUnion, jsInvoker_, instance_, std::move(chooseInt), std::move(chooseFloat), std::move(chooseObject), std::move(chooseString), std::move(chooseStringLiteral)); } private: 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 c7047527e6f..3cb576c6ad3 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 @@ -1292,7 +1292,8 @@ Map { - (NSDictionary *)getUnion:(double)chooseInt chooseFloat:(double)chooseFloat chooseObject:(NSDictionary *)chooseObject - chooseString:(NSString *)chooseString; + chooseString:(NSString *)chooseString + chooseStringLiteral:(NSString *)chooseStringLiteral; @end 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 c26611b1afb..689f389a8bb 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 @@ -678,7 +678,7 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo @ReactMethod(isBlockingSynchronousMethod = true) @DoNotStrip - public abstract WritableMap getUnion(double chooseInt, double chooseFloat, ReadableMap chooseObject, String chooseString); + public abstract WritableMap getUnion(double chooseInt, double chooseFloat, ReadableMap chooseObject, String chooseString, String chooseStringLiteral); } ", } 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 917d063a1ae..a9be8dc3fed 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 @@ -571,12 +571,12 @@ namespace facebook::react { static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getUnion(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { static jmethodID cachedMethodId = nullptr; - return static_cast(turboModule).invokeJavaMethod(rt, ObjectKind, \\"getUnion\\", \\"(DDLcom/facebook/react/bridge/ReadableMap;Ljava/lang/String;)Lcom/facebook/react/bridge/WritableMap;\\", args, count, cachedMethodId); + return static_cast(turboModule).invokeJavaMethod(rt, ObjectKind, \\"getUnion\\", \\"(DDLcom/facebook/react/bridge/ReadableMap;Ljava/lang/String;Ljava/lang/String;)Lcom/facebook/react/bridge/WritableMap;\\", args, count, cachedMethodId); } NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const JavaTurboModule::InitParams ¶ms) : JavaTurboModule(params) { - methodMap_[\\"getUnion\\"] = MethodMetadata {4, __hostFunction_NativeSampleTurboModuleSpecJSI_getUnion}; + methodMap_[\\"getUnion\\"] = MethodMetadata {5, __hostFunction_NativeSampleTurboModuleSpecJSI_getUnion}; } std::shared_ptr union_module_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) { 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 7bbb6f8ee77..8d20280fc7f 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 @@ -784,13 +784,13 @@ Map { namespace facebook::react { static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getUnion(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getUnion\\", @selector(getUnion:chooseFloat:chooseObject:chooseString:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getUnion\\", @selector(getUnion:chooseFloat:chooseObject:chooseString:chooseStringLiteral:), args, count); } NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_[\\"getUnion\\"] = MethodMetadata {4, __hostFunction_NativeSampleTurboModuleSpecJSI_getUnion}; + methodMap_[\\"getUnion\\"] = MethodMetadata {5, __hostFunction_NativeSampleTurboModuleSpecJSI_getUnion}; } } // namespace facebook::react diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js index 02aa6b3a535..a95af6cbdf4 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js @@ -862,8 +862,8 @@ describe('emitUnion', () => { const typeAnnotation = { type: 'UnionTypeAnnotation', types: [ - {type: 'StringLiteralTypeAnnotation'}, - {type: 'StringLiteralTypeAnnotation'}, + {type: 'StringLiteralTypeAnnotation', value: 'foo'}, + {type: 'StringLiteralTypeAnnotation', value: 'bar'}, ], }; describe('when nullable is true', () => { @@ -878,8 +878,17 @@ describe('emitUnion', () => { const expected = { type: 'NullableTypeAnnotation', typeAnnotation: { - type: 'UnionTypeAnnotation', - memberType: 'StringTypeAnnotation', + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], }, }; @@ -897,8 +906,17 @@ describe('emitUnion', () => { ); const expected = { - type: 'UnionTypeAnnotation', - memberType: 'StringTypeAnnotation', + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], }; expect(result).toEqual(expected); @@ -955,8 +973,8 @@ describe('emitUnion', () => { const typeAnnotation = { type: 'UnionTypeAnnotation', types: [ - {type: 'NumberLiteralTypeAnnotation'}, - {type: 'StringLiteralTypeAnnotation'}, + {type: 'NumberLiteralTypeAnnotation', value: 'foo'}, + {type: 'StringLiteralTypeAnnotation', value: 'bar'}, {type: 'ObjectTypeAnnotation'}, ], }; @@ -1002,11 +1020,11 @@ describe('emitUnion', () => { types: [ { type: 'TSLiteralType', - literal: {type: 'NumericLiteral'}, + literal: {type: 'NumericLiteral', value: 4}, }, { type: 'TSLiteralType', - literal: {type: 'NumericLiteral'}, + literal: {type: 'NumericLiteral', value: 5}, }, ], }; @@ -1056,11 +1074,11 @@ describe('emitUnion', () => { types: [ { type: 'TSLiteralType', - literal: {type: 'StringLiteral'}, + literal: {type: 'StringLiteral', value: 'foo'}, }, { type: 'TSLiteralType', - literal: {type: 'StringLiteral'}, + literal: {type: 'StringLiteral', value: 'bar'}, }, ], }; @@ -1076,8 +1094,17 @@ describe('emitUnion', () => { const expected = { type: 'NullableTypeAnnotation', typeAnnotation: { - type: 'UnionTypeAnnotation', - memberType: 'StringTypeAnnotation', + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], }, }; @@ -1095,8 +1122,17 @@ describe('emitUnion', () => { ); const expected = { - type: 'UnionTypeAnnotation', - memberType: 'StringTypeAnnotation', + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], }; expect(result).toEqual(expected); @@ -1162,11 +1198,11 @@ describe('emitUnion', () => { types: [ { type: 'TSLiteralType', - literal: {type: 'NumericLiteral'}, + literal: {type: 'NumericLiteral', value: 4}, }, { type: 'TSLiteralType', - literal: {type: 'StringLiteral'}, + literal: {type: 'StringLiteral', value: 'foo'}, }, { type: 'TSLiteralType', 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 9937ce800ff..60340fc027f 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 @@ -396,8 +396,21 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`] 'name': 'chooseString', 'optional': false, 'typeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'One' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Two' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Three' + } + ] } } ] @@ -2286,8 +2299,21 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_UNION 1` 'name': 'chooseString', 'optional': false, 'typeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'One' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Two' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Three' + } + ] } } ] @@ -2317,8 +2343,17 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_UNION_RE 'typeAnnotation': { 'type': 'FunctionTypeAnnotation', 'returnTypeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'light' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'dark' + } + ] }, 'params': [] } @@ -2336,8 +2371,17 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_UNION_RE 'name': 'strings', 'optional': false, 'typeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'light' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'dark' + } + ] } } ] @@ -2602,8 +2646,25 @@ exports[`RN Codegen Flow Parser can generate fixture PROMISE_WITH_COMMONLY_USED_ 'returnTypeAnnotation': { 'type': 'PromiseTypeAnnotation', 'elementType': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Spring' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Summer' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Autumn' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Winter' + } + ] } }, 'params': [] diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index 91cac322b93..3a435e47ba4 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -117,6 +117,12 @@ class FlowParser implements Parser { return [...new Set(membersTypes.map(remapLiteral))]; } + getStringLiteralUnionTypeAnnotationStringLiterals( + membersTypes: $FlowFixMe[], + ): string[] { + return membersTypes.map((item: $FlowFixMe) => item.value); + } + parseFile(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8'); diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index 4c0d0257ed5..bfb160bc77f 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -146,6 +146,14 @@ export interface Parser { remapUnionTypeAnnotationMemberNames( types: $FlowFixMe, ): UnionTypeAnnotationMemberType[]; + /** + * Given a union annotation members types, it returns an array of string literals. + * @parameter membersTypes: union annotation members types + * @returns: an array of string literals. + */ + getStringLiteralUnionTypeAnnotationStringLiterals( + types: $FlowFixMe, + ): string[]; /** * Given the name of a file, it returns a Schema. * @parameter filename: the name of the file. diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 7b0cc7ed53f..1045acfea12 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -109,6 +109,12 @@ export class MockedParser implements Parser { return []; } + getStringLiteralUnionTypeAnnotationStringLiterals( + membersTypes: $FlowFixMe[], + ): string[] { + return []; + } + parseFile(filename: string): SchemaType { return schemaMock; } diff --git a/packages/react-native-codegen/src/parsers/parsers-primitives.js b/packages/react-native-codegen/src/parsers/parsers-primitives.js index 91dd4532dc0..1a6957e0ae1 100644 --- a/packages/react-native-codegen/src/parsers/parsers-primitives.js +++ b/packages/react-native-codegen/src/parsers/parsers-primitives.js @@ -35,6 +35,7 @@ import type { ReservedTypeAnnotation, StringTypeAnnotation, StringLiteralTypeAnnotation, + StringLiteralUnionTypeAnnotation, VoidTypeAnnotation, } from '../CodegenSchema'; import type {Parser} from './parser'; @@ -409,7 +410,14 @@ function emitUnion( hasteModuleName: string, typeAnnotation: $FlowFixMe, parser: Parser, -): Nullable { +): Nullable< + NativeModuleUnionTypeAnnotation | StringLiteralUnionTypeAnnotation, +> { + // Get all the literals by type + // Verify they are all the same + // If string, persist as StringLiteralUnionType + // If number, persist as NumberTypeAnnotation (TODO: Number literal) + const unionTypes = parser.remapUnionTypeAnnotationMemberNames( typeAnnotation.types, ); @@ -423,12 +431,42 @@ function emitUnion( ); } + if (unionTypes[0] === 'StringTypeAnnotation') { + // Reprocess as a string literal union + return emitStringLiteralUnion( + nullable, + hasteModuleName, + typeAnnotation, + parser, + ); + } + return wrapNullable(nullable, { type: 'UnionTypeAnnotation', memberType: unionTypes[0], }); } +function emitStringLiteralUnion( + nullable: boolean, + hasteModuleName: string, + typeAnnotation: $FlowFixMe, + parser: Parser, +): Nullable { + const stringLiterals = + parser.getStringLiteralUnionTypeAnnotationStringLiterals( + typeAnnotation.types, + ); + + return wrapNullable(nullable, { + type: 'StringLiteralUnionTypeAnnotation', + types: stringLiterals.map(stringLiteral => ({ + type: 'StringLiteralTypeAnnotation', + value: stringLiteral, + })), + }); +} + function translateArrayTypeAnnotation( hasteModuleName: string, types: TypeDeclarationMap, 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 2e2917b7457..185b5adb7c2 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 @@ -387,8 +387,21 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL 'name': 'chooseString', 'optional': false, 'typeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'One' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Two' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Three' + } + ] } } ] @@ -2764,8 +2777,21 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_UN 'name': 'chooseString', 'optional': false, 'typeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'One' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Two' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'Three' + } + ] } } ] @@ -2795,8 +2821,17 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_UN 'typeAnnotation': { 'type': 'FunctionTypeAnnotation', 'returnTypeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'light' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'dark' + } + ] }, 'params': [] } @@ -2814,8 +2849,17 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_UN 'name': 'strings', 'optional': false, 'typeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralUnionTypeAnnotation', + 'types': [ + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'light' + }, + { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'dark' + } + ] } } ] diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index 04f8c1d69e6..592929dbd43 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -117,6 +117,12 @@ class TypeScriptParser implements Parser { return [...new Set(membersTypes.map(remapLiteral))]; } + getStringLiteralUnionTypeAnnotationStringLiterals( + membersTypes: $FlowFixMe[], + ): string[] { + return membersTypes.map((item: $FlowFixMe) => item.literal.value); + } + parseFile(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8');