Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support emitting typed RCTDeviceEmitter events #44305

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,12 @@ export type GraphNode = {
neighbors?: Array<GraphNode>,
};

export type CustomDeviceEvent = {
type: string,
level: number,
degree?: number,
};

export interface Spec extends TurboModule {
+getCallback: () => () => void;
+getMixed: (arg: mixed) => mixed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,32 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
}
}
]
},
'CustomDeviceEvent': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'type',
'optional': false,
'typeAnnotation': {
'type': 'StringTypeAnnotation'
}
},
{
'name': 'level',
'optional': false,
'typeAnnotation': {
'type': 'NumberTypeAnnotation'
}
},
{
'name': 'degree',
'optional': true,
'typeAnnotation': {
'type': 'NumberTypeAnnotation'
}
}
]
}
},
'enumMap': {
Expand Down
63 changes: 57 additions & 6 deletions packages/react-native-codegen/src/parsers/parsers-commons.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,52 @@ function isObjectProperty(property: $FlowFixMe, language: ParserType): boolean {
}
}

function getObjectTypeAnnotations(
hasteModuleName: string,
types: TypeDeclarationMap,
tryParse: ParserErrorCapturer,
translateTypeAnnotation: $FlowFixMe,
parser: Parser,
): {...NativeModuleAliasMap} {
const aliasMap: {...NativeModuleAliasMap} = {};
Object.entries(types).forEach(([key, value]) => {
const isTypeAlias =
value.type === 'TypeAlias' || value.type === 'TSTypeAliasDeclaration';
if (!isTypeAlias) {
return;
}
const parent = parser.nextNodeForTypeAlias(value);
if (
parent.type !== 'ObjectTypeAnnotation' &&
parent.type !== 'TSTypeLiteral'
) {
return;
}
const typeProperties = parser
.getAnnotatedElementProperties(value)
.map(prop =>
parseObjectProperty(
parent,
prop,
hasteModuleName,
types,
aliasMap,
{}, // enumMap
tryParse,
true, // cxxOnly
prop?.optional || false,
translateTypeAnnotation,
parser,
),
);
aliasMap[key] = {
type: 'ObjectTypeAnnotation',
properties: typeProperties,
};
});
return aliasMap;
}

function parseObjectProperty(
parentObject?: $FlowFixMe,
property: $FlowFixMe,
Expand Down Expand Up @@ -659,6 +705,16 @@ const buildModuleSchema = (
moduleName,
);

const aliasMap: {...NativeModuleAliasMap} = cxxOnly
? getObjectTypeAnnotations(
hasteModuleName,
types,
tryParse,
translateTypeAnnotation,
parser,
)
: {};

const properties: $ReadOnlyArray<$FlowFixMe> =
language === 'Flow' ? moduleSpec.body.properties : moduleSpec.body.body;

Expand All @@ -675,9 +731,7 @@ const buildModuleSchema = (
enumMap: NativeModuleEnumMap,
propertyShape: NativeModulePropertyShape,
}>(property => {
const aliasMap: {...NativeModuleAliasMap} = {};
const enumMap: {...NativeModuleEnumMap} = {};

return tryParse(() => ({
aliasMap,
enumMap,
Expand All @@ -696,10 +750,7 @@ const buildModuleSchema = (
})
.filter(Boolean)
.reduce(
(
moduleSchema: NativeModuleSchema,
{aliasMap, enumMap, propertyShape},
) => ({
(moduleSchema: NativeModuleSchema, {enumMap, propertyShape}) => ({
type: 'NativeModule',
aliasMap: {...moduleSchema.aliasMap, ...aliasMap},
enumMap: {...moduleSchema.enumMap, ...enumMap},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,12 @@ export type GraphNode = {
neighbors?: Array<GraphNode>,
};

export type CustomDeviceEvent = {
type: string,
level: number,
degree?: number,
};

export interface Spec extends TurboModule {
readonly getCallback: () => () => void;
readonly getMixed: (arg: unknown) => unknown;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,32 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL
}
}
]
},
'CustomDeviceEvent': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'type',
'optional': false,
'typeAnnotation': {
'type': 'StringTypeAnnotation'
}
},
{
'name': 'level',
'optional': false,
'typeAnnotation': {
'type': 'NumberTypeAnnotation'
}
},
{
'name': 'degree',
'optional': true,
'typeAnnotation': {
'type': 'NumberTypeAnnotation'
}
}
]
}
},
'enumMap': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,18 @@ void NativeCxxModuleExample::setMenu(jsi::Runtime& rt, MenuItem menuItem) {

void NativeCxxModuleExample::emitCustomDeviceEvent(
jsi::Runtime& rt,
jsi::String eventName) {
const std::string& eventName) {
// Test emitting device events (RCTDeviceEventEmitter.emit) from C++
// TurboModule with arbitrary arguments
// TurboModule with arbitrary arguments. This can be called from any thread
emitDeviceEvent(
eventName.utf8(rt).c_str(),
[](jsi::Runtime& rt, std::vector<jsi::Value>& args) {
eventName,
[jsInvoker = jsInvoker_](
jsi::Runtime& rt, std::vector<jsi::Value>& args) {
args.emplace_back(jsi::Value(true));
args.emplace_back(jsi::Value(42));
args.emplace_back(jsi::String::createFromAscii(rt, "stringArg"));
args.emplace_back(bridging::toJs(
rt, CustomDeviceEvent{"one", 2, std::nullopt}, jsInvoker));
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ template <>
struct Bridging<MenuItem>
: NativeCxxModuleExampleCxxMenuItemBridging<MenuItem> {};

#pragma mark - RCTDeviceEventEmitter events

using CustomDeviceEvent = NativeCxxModuleExampleCxxCustomDeviceEvent<
std::string,
int32_t,
std::optional<float>>;

template <>
struct Bridging<CustomDeviceEvent>
: NativeCxxModuleExampleCxxCustomDeviceEventBridging<CustomDeviceEvent> {};

#pragma mark - implementation
class NativeCxxModuleExample
: public NativeCxxModuleExampleCxxSpec<NativeCxxModuleExample> {
Expand Down Expand Up @@ -185,7 +196,7 @@ class NativeCxxModuleExample

void setMenu(jsi::Runtime& rt, MenuItem menuItem);

void emitCustomDeviceEvent(jsi::Runtime& rt, jsi::String eventName);
void emitCustomDeviceEvent(jsi::Runtime& rt, const std::string& eventName);

void voidFuncThrows(jsi::Runtime& rt);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ export type MenuItem = {
items?: Array<MenuItem>,
};

export type CustomDeviceEvent = {
type: string,
level: number,
degree?: ?number,
};

export interface Spec extends TurboModule {
+getArray: (arg: Array<ObjectStruct | null>) => Array<ObjectStruct | null>;
+getBool: (arg: boolean) => boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class NativeCxxModuleExampleExample extends React.Component<{||}, State> {
DeviceEventEmitter.addListener(CUSTOM_EVENT_TYPE, (...args) => {
this._setResult(
'emitDeviceEvent',
`${CUSTOM_EVENT_TYPE}(${args.map(s => `${s}`).join(', ')})`,
`${CUSTOM_EVENT_TYPE}(${args.map(s => (typeof s === 'object' ? JSON.stringify(s) : s)).join(', ')})`,
);
});
NativeCxxModuleExample?.emitCustomDeviceEvent(CUSTOM_EVENT_TYPE);
Expand Down