diff --git a/packages/react-native-codegen/src/CodegenSchema.d.ts b/packages/react-native-codegen/src/CodegenSchema.d.ts index f3b1c2ae6d08..045a5b4ccaf3 100644 --- a/packages/react-native-codegen/src/CodegenSchema.d.ts +++ b/packages/react-native-codegen/src/CodegenSchema.d.ts @@ -290,6 +290,11 @@ export interface NativeModuleStringTypeAnnotation { readonly type: 'StringTypeAnnotation'; } +export interface NativeModuleStringLiteralTypeAnnotation { + readonly type: 'StringLiteralTypeAnnotation'; + readonly value: string; +} + export interface NativeModuleNumberTypeAnnotation { readonly type: 'NumberTypeAnnotation'; } @@ -371,6 +376,7 @@ export type NativeModuleEventEmitterBaseTypeAnnotation = | NativeModuleInt32TypeAnnotation | NativeModuleNumberTypeAnnotation | NativeModuleStringTypeAnnotation + | NativeModuleStringLiteralTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleGenericObjectTypeAnnotation | VoidTypeAnnotation; @@ -384,6 +390,7 @@ export type NativeModuleEventEmitterTypeAnnotation = export type NativeModuleBaseTypeAnnotation = | NativeModuleStringTypeAnnotation + | NativeModuleStringLiteralTypeAnnotation | NativeModuleNumberTypeAnnotation | NativeModuleInt32TypeAnnotation | NativeModuleDoubleTypeAnnotation diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index 87e92425165f..089a83098f9a 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -41,6 +41,11 @@ export type StringTypeAnnotation = $ReadOnly<{ type: 'StringTypeAnnotation', }>; +export type StringLiteralTypeAnnotation = $ReadOnly<{ + type: 'StringLiteralTypeAnnotation', + value: string, +}>; + export type StringEnumTypeAnnotation = $ReadOnly<{ type: 'StringEnumTypeAnnotation', options: $ReadOnlyArray, @@ -357,6 +362,7 @@ type NativeModuleEventEmitterBaseTypeAnnotation = | Int32TypeAnnotation | NativeModuleNumberTypeAnnotation | StringTypeAnnotation + | StringLiteralTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleGenericObjectTypeAnnotation | VoidTypeAnnotation; @@ -370,6 +376,7 @@ export type NativeModuleEventEmitterTypeAnnotation = export type NativeModuleBaseTypeAnnotation = | StringTypeAnnotation + | StringLiteralTypeAnnotation | 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 f0e5ac171b95..6a30a71fd9f3 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js @@ -161,6 +161,8 @@ function serializeArg( } case 'StringTypeAnnotation': return wrap(val => `${val}.asString(rt)`); + case 'StringLiteralTypeAnnotation': + 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 c89c4732a046..2a76a9054d87 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js @@ -173,6 +173,8 @@ function translatePrimitiveJSTypeToCpp( return 'void'; case 'StringTypeAnnotation': return wrapOptional('jsi::String', isRequired); + case 'StringLiteralTypeAnnotation': + 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 35a257b13c86..0d130bc0777e 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js @@ -127,9 +127,12 @@ function translateEventEmitterTypeToJavaType( eventEmitter: NativeModuleEventEmitterShape, imports: Set, ): string { - switch (eventEmitter.typeAnnotation.typeAnnotation.type) { + const type = eventEmitter.typeAnnotation.typeAnnotation.type; + switch (type) { case 'StringTypeAnnotation': return 'String'; + case 'StringLiteralTypeAnnotation': + return 'String'; case 'NumberTypeAnnotation': case 'FloatTypeAnnotation': case 'DoubleTypeAnnotation': @@ -145,7 +148,16 @@ function translateEventEmitterTypeToJavaType( case 'ArrayTypeAnnotation': imports.add('com.facebook.react.bridge.ReadableArray'); return 'ReadableArray'; + case 'DoubleTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + case 'VoidTypeAnnotation': + // TODO: Add support for these types + throw new Error( + `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, + ); default: + (type: empty); throw new Error( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); @@ -183,6 +195,8 @@ function translateFunctionParamToJavaType( } case 'StringTypeAnnotation': return wrapOptional('String', isRequired); + case 'StringLiteralTypeAnnotation': + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -273,6 +287,8 @@ function translateFunctionReturnTypeToJavaType( return 'void'; case 'StringTypeAnnotation': return wrapOptional('String', isRequired); + case 'StringLiteralTypeAnnotation': + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -383,6 +399,8 @@ function getFalsyReturnStatementFromReturnType( } case 'StringTypeAnnotation': return nullable ? 'return null;' : 'return "";'; + case 'StringLiteralTypeAnnotation': + 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 92e642118d1b..8b9228c15f4d 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js @@ -165,6 +165,8 @@ function translateReturnTypeToKind( return 'VoidKind'; case 'StringTypeAnnotation': return 'StringKind'; + case 'StringLiteralTypeAnnotation': + return 'StringKind'; case 'BooleanTypeAnnotation': return 'BooleanKind'; case 'EnumDeclaration': @@ -244,6 +246,8 @@ function translateParamTypeToJniType( } case 'StringTypeAnnotation': return 'Ljava/lang/String;'; + case 'StringLiteralTypeAnnotation': + return 'Ljava/lang/String;'; case 'BooleanTypeAnnotation': return !isRequired ? 'Ljava/lang/Boolean;' : 'Z'; case 'EnumDeclaration': @@ -320,6 +324,8 @@ function translateReturnTypeToJniType( return 'V'; case 'StringTypeAnnotation': return 'Ljava/lang/String;'; + case 'StringLiteralTypeAnnotation': + 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 101b9137c3ea..0609689fe282 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js @@ -22,6 +22,7 @@ import type { NativeModuleNumberTypeAnnotation, NativeModuleObjectTypeAnnotation, StringTypeAnnotation, + StringLiteralTypeAnnotation, NativeModuleTypeAliasTypeAnnotation, Nullable, ReservedTypeAnnotation, @@ -58,6 +59,7 @@ export type StructProperty = $ReadOnly<{ export type StructTypeAnnotation = | StringTypeAnnotation + | StringLiteralTypeAnnotation | 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 8e8054c583e1..8ceb64472778 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 @@ -94,6 +94,8 @@ function toObjCType( } case 'StringTypeAnnotation': return 'NSString *'; + case 'StringLiteralTypeAnnotation': + return 'NSString *'; case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -173,6 +175,8 @@ function toObjCValue( } case 'StringTypeAnnotation': return value; + case 'StringLiteralTypeAnnotation': + 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 3e53c456572f..e68693af6b87 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 @@ -85,6 +85,8 @@ function toObjCType( } case 'StringTypeAnnotation': return 'NSString *'; + case 'StringLiteralTypeAnnotation': + return 'NSString *'; case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -163,6 +165,8 @@ function toObjCValue( } case 'StringTypeAnnotation': return RCTBridgingTo('String'); + case 'StringLiteralTypeAnnotation': + 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 b486563cc3a6..9f1d5b077fa9 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js @@ -15,9 +15,13 @@ const {toPascalCase} = require('../../Utils'); function getEventEmitterTypeObjCType( eventEmitter: NativeModuleEventEmitterShape, ): string { - switch (eventEmitter.typeAnnotation.typeAnnotation.type) { + const type = eventEmitter.typeAnnotation.typeAnnotation.type; + + switch (type) { case 'StringTypeAnnotation': return 'NSString *_Nonnull'; + case 'StringLiteralTypeAnnotation': + return 'NSString *_Nonnull'; case 'NumberTypeAnnotation': return 'NSNumber *_Nonnull'; case 'BooleanTypeAnnotation': @@ -28,7 +32,16 @@ function getEventEmitterTypeObjCType( return 'NSDictionary *'; case 'ArrayTypeAnnotation': return 'NSArray> *'; + case 'DoubleTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + case 'VoidTypeAnnotation': + // TODO: Add support for these types + throw new Error( + `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, + ); default: + (type: empty); throw new Error( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); 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 c06354ebc3e6..c5ea2b668402 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js @@ -257,6 +257,8 @@ function getParamObjCType( } case 'StringTypeAnnotation': return notStruct(wrapOptional('NSString *', !nullable)); + case 'StringLiteralTypeAnnotation': + return notStruct(wrapOptional('NSString *', !nullable)); case 'NumberTypeAnnotation': return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'FloatTypeAnnotation': @@ -330,6 +332,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 'StringLiteralTypeAnnotation': + // 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': @@ -396,6 +402,8 @@ function getReturnJSType( return 'NumberKind'; case 'StringTypeAnnotation': return 'StringKind'; + case 'StringLiteralTypeAnnotation': + 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 409ff9cca7bb..058ad1e7146f 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 @@ -2636,6 +2636,55 @@ const UNION_MODULE: SchemaType = { }, }; +const STRING_LITERALS = { + modules: { + NativeSampleTurboModule: { + type: 'NativeModule', + aliasMap: {}, + enumMap: {}, + spec: { + eventEmitters: [ + { + name: 'literalEvent', + optional: false, + typeAnnotation: { + type: 'EventEmitterTypeAnnotation', + typeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Event', + }, + }, + }, + ], + methods: [ + { + name: 'getStringLiteral', + optional: false, + typeAnnotation: { + type: 'FunctionTypeAnnotation', + returnTypeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Return', + }, + params: [ + { + name: 'literalParam', + optional: false, + typeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Param', + }, + }, + ], + }, + }, + ], + }, + moduleName: 'SampleTurboModule', + }, + }, +}; + module.exports = { complex_objects: COMPLEX_OBJECTS, two_modules_different_files: TWO_MODULES_DIFFERENT_FILES, @@ -2647,4 +2696,5 @@ module.exports = { cxx_only_native_modules: CXX_ONLY_NATIVE_MODULES, SampleWithUppercaseName: SAMPLE_WITH_UPPERCASE_NAME, union_module: UNION_MODULE, + string_literals: STRING_LITERALS, }; 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 cefc9c3956ae..4fca1c7436f8 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 @@ -655,6 +655,39 @@ NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared } +} // namespace facebook::react +", +} +`; + +exports[`GenerateModuleCpp can generate fixture string_literals 1`] = ` +Map { + "string_literalsJSI-generated.cpp" => "/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateModuleCpp.js + */ + +#include \\"string_literalsJSI.h\\" + +namespace facebook::react { + +static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getStringLiteral(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { + return static_cast(&turboModule)->getStringLiteral( + rt, + count <= 0 ? throw jsi::JSError(rt, \\"Expected argument in position 0 to be passed\\") : args[0].asString(rt) + ); +} + +NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr jsInvoker) + : TurboModule(\\"SampleTurboModule\\", jsInvoker) { + methodMap_[\\"getStringLiteral\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getStringLiteral}; +} + + } // namespace facebook::react ", } 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 3c559f125a57..ede5b6c57711 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 @@ -2273,6 +2273,89 @@ private: } `; +exports[`GenerateModuleH can generate fixture string_literals 1`] = ` +Map { + "string_literalsJSI.h" => "/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateModuleH.js + */ + +#pragma once + +#include +#include + +namespace facebook::react { + + + class JSI_EXPORT NativeSampleTurboModuleCxxSpecJSI : public TurboModule { +protected: + NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr jsInvoker); + +public: + virtual jsi::String getStringLiteral(jsi::Runtime &rt, jsi::String literalParam) = 0; + +}; + +template +class JSI_EXPORT NativeSampleTurboModuleCxxSpec : public TurboModule { +public: + jsi::Value create(jsi::Runtime &rt, const jsi::PropNameID &propName) override { + return delegate_.create(rt, propName); + } + + std::vector getPropertyNames(jsi::Runtime& runtime) override { + return delegate_.getPropertyNames(runtime); + } + + static constexpr std::string_view kModuleName = \\"SampleTurboModule\\"; + +protected: + NativeSampleTurboModuleCxxSpec(std::shared_ptr jsInvoker) + : TurboModule(std::string{NativeSampleTurboModuleCxxSpec::kModuleName}, jsInvoker), + delegate_(reinterpret_cast(this), jsInvoker) {} + + template void emitLiteralEvent(LiteralEventType value) { + static_assert(bridging::supportsFromJs, \\"value cannnot be converted to jsi::String\\"); + static_cast&>(*delegate_.eventEmitterMap_[\\"literalEvent\\"]).emit([jsInvoker = jsInvoker_, eventValue = value](jsi::Runtime& rt) -> jsi::Value { + return bridging::toJs(rt, eventValue, jsInvoker); + }); + } + +private: + class Delegate : public NativeSampleTurboModuleCxxSpecJSI { + public: + Delegate(T *instance, std::shared_ptr jsInvoker) : + NativeSampleTurboModuleCxxSpecJSI(std::move(jsInvoker)), instance_(instance) { + eventEmitterMap_[\\"literalEvent\\"] = std::make_shared>(); + } + + jsi::String getStringLiteral(jsi::Runtime &rt, jsi::String literalParam) override { + static_assert( + bridging::getParameterCount(&T::getStringLiteral) == 2, + \\"Expected getStringLiteral(...) to have 2 parameters\\"); + + return bridging::callFromJs( + rt, &T::getStringLiteral, jsInvoker_, instance_, std::move(literalParam)); + } + + private: + friend class NativeSampleTurboModuleCxxSpec; + T *instance_; + }; + + Delegate delegate_; +}; + +} // namespace facebook::react +", +} +`; + exports[`GenerateModuleH can generate fixture two_modules_different_files 1`] = ` Map { "two_modules_different_filesJSI.h" => "/** 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 2b68008dc637..c7047527e6fb 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 @@ -1096,6 +1096,71 @@ inline JS::NativeSampleTurboModule::Constants::Builder::Builder(Constants i) : _ } `; +exports[`GenerateModuleHObjCpp can generate fixture string_literals 1`] = ` +Map { + "string_literals.h" => "/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateModuleObjCpp + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. + */ + +#ifndef __cplusplus +#error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm. +#endif + +// Avoid multiple includes of string_literals symbols +#ifndef string_literals_H +#define string_literals_H + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + + +@protocol NativeSampleTurboModuleSpec + +- (NSString *)getStringLiteral:(NSString *)literalParam; + +@end + +@interface NativeSampleTurboModuleSpecBase : NSObject { +@protected +facebook::react::EventEmitterCallback _eventEmitterCallback; +} +- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper; + +- (void)emitLiteralEvent:(NSString *_Nonnull)value; +@end + +namespace facebook::react { + /** + * ObjC++ class for module 'NativeSampleTurboModule' + */ + class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; +} // namespace facebook::react + +#endif // string_literals_H +", +} +`; + exports[`GenerateModuleHObjCpp can generate fixture two_modules_different_files 1`] = ` Map { "two_modules_different_files.h" => "/** 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 e90a1b00f8f4..c26611b1afb1 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 @@ -511,6 +511,53 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo } `; +exports[`GenerateModuleJavaSpec can generate fixture string_literals 1`] = ` +Map { + "java/com/facebook/fbreact/specs/NativeSampleTurboModuleSpec.java" => " +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateModuleJavaSpec.js + * + * @nolint + */ + +package com.facebook.fbreact.specs; + +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; +import javax.annotation.Nonnull; + +public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaModule implements TurboModule { + public static final String NAME = \\"SampleTurboModule\\"; + + public NativeSampleTurboModuleSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public @Nonnull String getName() { + return NAME; + } + + protected final void emitLiteralEvent(String value) { + mEventEmitterCallback.invoke(\\"literalEvent\\", value); + } + + @ReactMethod(isBlockingSynchronousMethod = true) + @DoNotStrip + public abstract String getStringLiteral(String literalParam); +} +", +} +`; + exports[`GenerateModuleJavaSpec can generate fixture two_modules_different_files 1`] = ` Map { "java/com/facebook/fbreact/specs/NativeSampleTurboModuleSpec.java" => " 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 7990033c5ca0..917d063a1ae8 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 @@ -461,6 +461,46 @@ std::shared_ptr simple_native_modules_ModuleProvider(const std::str } `; +exports[`GenerateModuleJniCpp can generate fixture string_literals 1`] = ` +Map { + "jni/string_literals-generated.cpp" => " +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateModuleJniCpp.js + */ + +#include \\"string_literals.h\\" + +namespace facebook::react { + +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getStringLiteral(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static jmethodID cachedMethodId = nullptr; + return static_cast(turboModule).invokeJavaMethod(rt, StringKind, \\"getStringLiteral\\", \\"(Ljava/lang/String;)Ljava/lang/String;\\", args, count, cachedMethodId); +} + +NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const JavaTurboModule::InitParams ¶ms) + : JavaTurboModule(params) { + methodMap_[\\"getStringLiteral\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getStringLiteral}; + eventEmitterMap_[\\"literalEvent\\"] = std::make_shared>(); + setEventEmitterCallback(params.instance); +} + +std::shared_ptr string_literals_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) { + if (moduleName == \\"SampleTurboModule\\") { + return std::make_shared(params); + } + return nullptr; +} + +} // namespace facebook::react +", +} +`; + exports[`GenerateModuleJniCpp can generate fixture two_modules_different_files 1`] = ` Map { "jni/two_modules_different_files-generated.cpp" => " diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap index 9126401fbeb7..3e015be8c930 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap @@ -593,6 +593,80 @@ target_compile_options( } `; +exports[`GenerateModuleJniH can generate fixture string_literals 1`] = ` +Map { + "jni/string_literals.h" => " +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateModuleJniH.js + */ + +#pragma once + +#include +#include +#include + +namespace facebook::react { + +/** + * JNI C++ class for module 'NativeSampleTurboModule' + */ +class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public JavaTurboModule { +public: + NativeSampleTurboModuleSpecJSI(const JavaTurboModule::InitParams ¶ms); +}; + + +JSI_EXPORT +std::shared_ptr string_literals_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms); + +} // namespace facebook::react +", + "jni/CMakeLists.txt" => "# 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. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +file(GLOB react_codegen_SRCS CONFIGURE_DEPENDS *.cpp react/renderer/components/string_literals/*.cpp) + +add_library( + react_codegen_string_literals + OBJECT + \${react_codegen_SRCS} +) + +target_include_directories(react_codegen_string_literals PUBLIC . react/renderer/components/string_literals) + +target_link_libraries( + react_codegen_string_literals + fbjni + jsi + # We need to link different libraries based on whether we are building rncore or not, that's necessary + # because we want to break a circular dependency between react_codegen_rncore and reactnative + reactnative +) + +target_compile_options( + react_codegen_string_literals + PRIVATE + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + -fexceptions + -frtti + -std=c++20 + -Wall +) +", +} +`; + exports[`GenerateModuleJniH can generate fixture two_modules_different_files 1`] = ` Map { "jni/two_modules_different_files.h" => " 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 656296fd5396..7bbb6f8ee77d 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 @@ -632,6 +632,58 @@ namespace facebook::react { } `; +exports[`GenerateModuleMm can generate fixture string_literals 1`] = ` +Map { + "string_literals-generated.mm" => "/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateModuleObjCpp + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. + */ + +#import \\"string_literals.h\\" + + +@implementation NativeSampleTurboModuleSpecBase +- (void)emitLiteralEvent:(NSString *_Nonnull)value +{ + _eventEmitterCallback(\\"literalEvent\\", value); +} + +- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper +{ + _eventEmitterCallback = std::move(eventEmitterCallbackWrapper->_eventEmitterCallback); +} +@end + + +namespace facebook::react { + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getStringLiteral(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getStringLiteral\\", @selector(getStringLiteral:), args, count); + } + + NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getStringLiteral\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getStringLiteral}; + + eventEmitterMap_[\\"literalEvent\\"] = std::make_shared>(); + setEventEmitterCallback([&](const std::string &name, id value) { + static_cast &>(*eventEmitterMap_[name]).emit(value); + }); + } +} // namespace facebook::react +", +} +`; + exports[`GenerateModuleMm can generate fixture two_modules_different_files 1`] = ` Map { "two_modules_different_files-generated.mm" => "/** 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 8a0b39348100..7eb0e0777275 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 @@ -128,6 +128,7 @@ export interface Spec extends TurboModule { +passNumber: (arg: number) => void; +passString: (arg: string) => void; +passStringish: (arg: Stringish) => void; + +passStringLiteral: (arg: 'A String Literal') => void; } 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 6580851a5afd..9937ce800ff9 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 @@ -1000,6 +1000,26 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_PA } ] } + }, + { + 'name': 'passStringLiteral', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'VoidTypeAnnotation' + }, + 'params': [ + { + 'name': 'arg', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'A String Literal' + } + } + ] + } } ] }, @@ -2469,8 +2489,8 @@ exports[`RN Codegen Flow Parser can generate fixture PROMISE_WITH_COMMONLY_USED_ 'name': 'type', 'optional': false, 'typeAnnotation': { - 'type': 'UnionTypeAnnotation', - 'memberType': 'StringTypeAnnotation' + 'type': 'StringLiteralTypeAnnotation', + 'value': 'A_String_Literal' } } ] diff --git a/packages/react-native-codegen/src/parsers/flow/modules/index.js b/packages/react-native-codegen/src/parsers/flow/modules/index.js index 1fb9a52adb2a..4c6d2ef20232 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -244,10 +244,9 @@ function translateTypeAnnotation( return emitUnion(nullable, hasteModuleName, typeAnnotation, parser); } case 'StringLiteralTypeAnnotation': { - // 'a' is a special case for 'a' | 'b' but the type name is different return wrapNullable(nullable, { - type: 'UnionTypeAnnotation', - memberType: 'StringTypeAnnotation', + type: 'StringLiteralTypeAnnotation', + value: typeAnnotation.value, }); } case 'EnumStringBody': diff --git a/packages/react-native-codegen/src/parsers/parsers-primitives.js b/packages/react-native-codegen/src/parsers/parsers-primitives.js index cb16608bbb3c..91dd4532dc03 100644 --- a/packages/react-native-codegen/src/parsers/parsers-primitives.js +++ b/packages/react-native-codegen/src/parsers/parsers-primitives.js @@ -34,6 +34,7 @@ import type { ObjectTypeAnnotation, ReservedTypeAnnotation, StringTypeAnnotation, + StringLiteralTypeAnnotation, VoidTypeAnnotation, } from '../CodegenSchema'; import type {Parser} from './parser'; @@ -174,6 +175,16 @@ function emitString(nullable: boolean): Nullable { }); } +function emitStringLiteral( + nullable: boolean, + value: string, +): Nullable { + return wrapNullable(nullable, { + type: 'StringLiteralTypeAnnotation', + value, + }); +} + function emitStringProp( name: string, optional: boolean, @@ -722,6 +733,7 @@ module.exports = { emitString, emitStringish, emitStringProp, + emitStringLiteral, emitMixed, emitUnion, emitPartial, 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 5c28d3955d4b..60f5d83fedd5 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 @@ -115,6 +115,7 @@ export interface Spec extends TurboModule { readonly passNumber: (arg: number) => void; readonly passString: (arg: string) => void; readonly passStringish: (arg: Stringish) => void; + readonly passStringLiteral: (arg: 'A String Literal') => void; } 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 0f3cd82c2e54..2e2917b74575 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 @@ -1145,6 +1145,26 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_BA } ] } + }, + { + 'name': 'passStringLiteral', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'VoidTypeAnnotation' + }, + 'params': [ + { + 'name': 'arg', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringLiteralTypeAnnotation', + 'value': 'A String Literal' + } + } + ] + } } ] }, diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/index.js b/packages/react-native-codegen/src/parsers/typescript/modules/index.js index db696d0b7b47..8a6ebcee35f3 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -40,6 +40,7 @@ const { emitPromise, emitRootTag, emitUnion, + emitStringLiteral, translateArrayTypeAnnotation, typeAliasResolution, typeEnumResolution, @@ -396,6 +397,21 @@ function translateTypeAnnotation( case 'TSUnionType': { return emitUnion(nullable, hasteModuleName, typeAnnotation, parser); } + case 'TSLiteralType': { + const literal = typeAnnotation.literal; + switch (literal.type) { + case 'StringLiteral': { + return emitStringLiteral(nullable, literal.value); + } + default: { + throw new UnsupportedTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + parser.language(), + ); + } + } + } default: { const commonType = emitCommonTypes( hasteModuleName,