Add support for StringLiteralTypeAnnotation (#46827)

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

You can now define a native module that takes an argument that is a specific string literal:

Like this:
```
export interface Spec extends TurboModule {
  +passString: (arg: string) => void;
  +passStringLiteral: (arg: 'A String Literal') => void;
}
```

On the native side, this will still generate `string`, but the schema will now store the string literal, and it will be allowed in JS. This should allow more strict flow / typescript types for modules.

This will also allow the compatibility check to fail if the literal changes.

This is a step towards accepting a union of string literals.

Changelog: [General][Added] Codegen for Native Modules now supports string literals

Reviewed By: GijsWeterings

Differential Revision: D63872440

fbshipit-source-id: e54b97d34af4a3d1af51727db0777f26fb7b778c
This commit is contained in:
Eli White
2024-10-07 12:35:19 -07:00
committed by Facebook GitHub Bot
parent 73ce1984e8
commit d2f3f06826
26 changed files with 593 additions and 7 deletions
+7
View File
@@ -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
+7
View File
@@ -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<string>,
@@ -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
@@ -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':
@@ -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':
@@ -127,9 +127,12 @@ function translateEventEmitterTypeToJavaType(
eventEmitter: NativeModuleEventEmitterShape,
imports: Set<string>,
): 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':
@@ -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':
@@ -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
@@ -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':
@@ -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':
@@ -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<id<NSObject>> *';
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}`,
);
@@ -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':
@@ -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,
};
@@ -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<NativeSampleTurboModuleCxxSpecJSI *>(&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<CallInvoker> jsInvoker)
: TurboModule(\\"SampleTurboModule\\", jsInvoker) {
methodMap_[\\"getStringLiteral\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getStringLiteral};
}
} // namespace facebook::react
",
}
@@ -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 <ReactCommon/TurboModule.h>
#include <react/bridging/Bridging.h>
namespace facebook::react {
class JSI_EXPORT NativeSampleTurboModuleCxxSpecJSI : public TurboModule {
protected:
NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker);
public:
virtual jsi::String getStringLiteral(jsi::Runtime &rt, jsi::String literalParam) = 0;
};
template <typename T>
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<jsi::PropNameID> getPropertyNames(jsi::Runtime& runtime) override {
return delegate_.getPropertyNames(runtime);
}
static constexpr std::string_view kModuleName = \\"SampleTurboModule\\";
protected:
NativeSampleTurboModuleCxxSpec(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule(std::string{NativeSampleTurboModuleCxxSpec::kModuleName}, jsInvoker),
delegate_(reinterpret_cast<T*>(this), jsInvoker) {}
template <typename LiteralEventType> void emitLiteralEvent(LiteralEventType value) {
static_assert(bridging::supportsFromJs<LiteralEventType, jsi::String>, \\"value cannnot be converted to jsi::String\\");
static_cast<AsyncEventEmitter<jsi::Value>&>(*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<CallInvoker> jsInvoker) :
NativeSampleTurboModuleCxxSpecJSI(std::move(jsInvoker)), instance_(instance) {
eventEmitterMap_[\\"literalEvent\\"] = std::make_shared<AsyncEventEmitter<jsi::Value>>();
}
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<jsi::String>(
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" => "/**
@@ -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 <Foundation/Foundation.h>
#import <RCTRequired/RCTRequired.h>
#import <RCTTypeSafety/RCTConvertHelpers.h>
#import <RCTTypeSafety/RCTTypedModuleConstants.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTCxxConvert.h>
#import <React/RCTManagedPointer.h>
#import <ReactCommon/RCTTurboModule.h>
#import <optional>
#import <vector>
@protocol NativeSampleTurboModuleSpec <RCTBridgeModule, RCTTurboModule>
- (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 &params);
};
} // namespace facebook::react
#endif // string_literals_H
",
}
`;
exports[`GenerateModuleHObjCpp can generate fixture two_modules_different_files 1`] = `
Map {
"two_modules_different_files.h" => "/**
@@ -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" => "
@@ -461,6 +461,46 @@ std::shared_ptr<TurboModule> 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<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, StringKind, \\"getStringLiteral\\", \\"(Ljava/lang/String;)Ljava/lang/String;\\", args, count, cachedMethodId);
}
NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const JavaTurboModule::InitParams &params)
: JavaTurboModule(params) {
methodMap_[\\"getStringLiteral\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getStringLiteral};
eventEmitterMap_[\\"literalEvent\\"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
setEventEmitterCallback(params.instance);
}
std::shared_ptr<TurboModule> string_literals_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams &params) {
if (moduleName == \\"SampleTurboModule\\") {
return std::make_shared<NativeSampleTurboModuleSpecJSI>(params);
}
return nullptr;
}
} // namespace facebook::react
",
}
`;
exports[`GenerateModuleJniCpp can generate fixture two_modules_different_files 1`] = `
Map {
"jni/two_modules_different_files-generated.cpp" => "
@@ -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 <ReactCommon/JavaTurboModule.h>
#include <ReactCommon/TurboModule.h>
#include <jsi/jsi.h>
namespace facebook::react {
/**
* JNI C++ class for module 'NativeSampleTurboModule'
*/
class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public JavaTurboModule {
public:
NativeSampleTurboModuleSpecJSI(const JavaTurboModule::InitParams &params);
};
JSI_EXPORT
std::shared_ptr<TurboModule> string_literals_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams &params);
} // 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" => "
@@ -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<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, StringKind, \\"getStringLiteral\\", @selector(getStringLiteral:), args, count);
}
NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams &params)
: ObjCTurboModule(params) {
methodMap_[\\"getStringLiteral\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getStringLiteral};
eventEmitterMap_[\\"literalEvent\\"] = std::make_shared<AsyncEventEmitter<id>>();
setEventEmitterCallback([&](const std::string &name, id value) {
static_cast<AsyncEventEmitter<id> &>(*eventEmitterMap_[name]).emit(value);
});
}
} // namespace facebook::react
",
}
`;
exports[`GenerateModuleMm can generate fixture two_modules_different_files 1`] = `
Map {
"two_modules_different_files-generated.mm" => "/**
@@ -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<Spec>('SampleTurboModule');
@@ -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'
}
}
]
@@ -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':
@@ -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<StringTypeAnnotation> {
});
}
function emitStringLiteral(
nullable: boolean,
value: string,
): Nullable<StringLiteralTypeAnnotation> {
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,
@@ -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<Spec>('SampleTurboModule');
@@ -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'
}
}
]
}
}
]
},
@@ -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,