Skip to content

Commit c96c893

Browse files
Support emitting typed RCTDeviceEmitter events
Summary: This enables to code-gen base C++ types for custom exported JS types from a RN TM spec - which have been previously excluded from code-gen as these aren't used in any function. The only work around so far was to ‘register’ a random function using the custom type which should be used for RCTDeviceEventEmitter events Changelog: [Internal] Reviewed By: rshest Differential Revision: D56685903 fbshipit-source-id: add9ca40018b91c9fca98609ba3d1f85d3affec1
1 parent cfbee0b commit c96c893

File tree

9 files changed

+147
-12
lines changed

9 files changed

+147
-12
lines changed

packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,12 @@ export type GraphNode = {
742742
neighbors?: Array<GraphNode>,
743743
};
744744
745+
export type CustomDeviceEvent = {
746+
type: string,
747+
level: number,
748+
degree?: number,
749+
};
750+
745751
export interface Spec extends TurboModule {
746752
+getCallback: () => () => void;
747753
+getMixed: (arg: mixed) => mixed;

packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,32 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
102102
}
103103
}
104104
]
105+
},
106+
'CustomDeviceEvent': {
107+
'type': 'ObjectTypeAnnotation',
108+
'properties': [
109+
{
110+
'name': 'type',
111+
'optional': false,
112+
'typeAnnotation': {
113+
'type': 'StringTypeAnnotation'
114+
}
115+
},
116+
{
117+
'name': 'level',
118+
'optional': false,
119+
'typeAnnotation': {
120+
'type': 'NumberTypeAnnotation'
121+
}
122+
},
123+
{
124+
'name': 'degree',
125+
'optional': true,
126+
'typeAnnotation': {
127+
'type': 'NumberTypeAnnotation'
128+
}
129+
}
130+
]
105131
}
106132
},
107133
'enumMap': {

packages/react-native-codegen/src/parsers/parsers-commons.js

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,52 @@ function isObjectProperty(property: $FlowFixMe, language: ParserType): boolean {
171171
}
172172
}
173173

174+
function getObjectTypeAnnotations(
175+
hasteModuleName: string,
176+
types: TypeDeclarationMap,
177+
tryParse: ParserErrorCapturer,
178+
translateTypeAnnotation: $FlowFixMe,
179+
parser: Parser,
180+
): {...NativeModuleAliasMap} {
181+
const aliasMap: {...NativeModuleAliasMap} = {};
182+
Object.entries(types).forEach(([key, value]) => {
183+
const isTypeAlias =
184+
value.type === 'TypeAlias' || value.type === 'TSTypeAliasDeclaration';
185+
if (!isTypeAlias) {
186+
return;
187+
}
188+
const parent = parser.nextNodeForTypeAlias(value);
189+
if (
190+
parent.type !== 'ObjectTypeAnnotation' &&
191+
parent.type !== 'TSTypeLiteral'
192+
) {
193+
return;
194+
}
195+
const typeProperties = parser
196+
.getAnnotatedElementProperties(value)
197+
.map(prop =>
198+
parseObjectProperty(
199+
parent,
200+
prop,
201+
hasteModuleName,
202+
types,
203+
aliasMap,
204+
{}, // enumMap
205+
tryParse,
206+
true, // cxxOnly
207+
prop?.optional || false,
208+
translateTypeAnnotation,
209+
parser,
210+
),
211+
);
212+
aliasMap[key] = {
213+
type: 'ObjectTypeAnnotation',
214+
properties: typeProperties,
215+
};
216+
});
217+
return aliasMap;
218+
}
219+
174220
function parseObjectProperty(
175221
parentObject?: $FlowFixMe,
176222
property: $FlowFixMe,
@@ -659,6 +705,16 @@ const buildModuleSchema = (
659705
moduleName,
660706
);
661707

708+
const aliasMap: {...NativeModuleAliasMap} = cxxOnly
709+
? getObjectTypeAnnotations(
710+
hasteModuleName,
711+
types,
712+
tryParse,
713+
translateTypeAnnotation,
714+
parser,
715+
)
716+
: {};
717+
662718
const properties: $ReadOnlyArray<$FlowFixMe> =
663719
language === 'Flow' ? moduleSpec.body.properties : moduleSpec.body.body;
664720

@@ -675,9 +731,7 @@ const buildModuleSchema = (
675731
enumMap: NativeModuleEnumMap,
676732
propertyShape: NativeModulePropertyShape,
677733
}>(property => {
678-
const aliasMap: {...NativeModuleAliasMap} = {};
679734
const enumMap: {...NativeModuleEnumMap} = {};
680-
681735
return tryParse(() => ({
682736
aliasMap,
683737
enumMap,
@@ -696,10 +750,7 @@ const buildModuleSchema = (
696750
})
697751
.filter(Boolean)
698752
.reduce(
699-
(
700-
moduleSchema: NativeModuleSchema,
701-
{aliasMap, enumMap, propertyShape},
702-
) => ({
753+
(moduleSchema: NativeModuleSchema, {enumMap, propertyShape}) => ({
703754
type: 'NativeModule',
704755
aliasMap: {...moduleSchema.aliasMap, ...aliasMap},
705756
enumMap: {...moduleSchema.enumMap, ...enumMap},

packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,12 @@ export type GraphNode = {
836836
neighbors?: Array<GraphNode>,
837837
};
838838
839+
export type CustomDeviceEvent = {
840+
type: string,
841+
level: number,
842+
degree?: number,
843+
};
844+
839845
export interface Spec extends TurboModule {
840846
readonly getCallback: () => () => void;
841847
readonly getMixed: (arg: unknown) => unknown;

packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,32 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL
9393
}
9494
}
9595
]
96+
},
97+
'CustomDeviceEvent': {
98+
'type': 'ObjectTypeAnnotation',
99+
'properties': [
100+
{
101+
'name': 'type',
102+
'optional': false,
103+
'typeAnnotation': {
104+
'type': 'StringTypeAnnotation'
105+
}
106+
},
107+
{
108+
'name': 'level',
109+
'optional': false,
110+
'typeAnnotation': {
111+
'type': 'NumberTypeAnnotation'
112+
}
113+
},
114+
{
115+
'name': 'degree',
116+
'optional': true,
117+
'typeAnnotation': {
118+
'type': 'NumberTypeAnnotation'
119+
}
120+
}
121+
]
96122
}
97123
},
98124
'enumMap': {

packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,15 +180,18 @@ void NativeCxxModuleExample::setMenu(jsi::Runtime& rt, MenuItem menuItem) {
180180

181181
void NativeCxxModuleExample::emitCustomDeviceEvent(
182182
jsi::Runtime& rt,
183-
jsi::String eventName) {
183+
const std::string& eventName) {
184184
// Test emitting device events (RCTDeviceEventEmitter.emit) from C++
185-
// TurboModule with arbitrary arguments
185+
// TurboModule with arbitrary arguments. This can be called from any thread
186186
emitDeviceEvent(
187-
eventName.utf8(rt).c_str(),
188-
[](jsi::Runtime& rt, std::vector<jsi::Value>& args) {
187+
eventName,
188+
[jsInvoker = jsInvoker_](
189+
jsi::Runtime& rt, std::vector<jsi::Value>& args) {
189190
args.emplace_back(jsi::Value(true));
190191
args.emplace_back(jsi::Value(42));
191192
args.emplace_back(jsi::String::createFromAscii(rt, "stringArg"));
193+
args.emplace_back(bridging::toJs(
194+
rt, CustomDeviceEvent{"one", 2, std::nullopt}, jsInvoker));
192195
});
193196
}
194197

packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ template <>
116116
struct Bridging<MenuItem>
117117
: NativeCxxModuleExampleCxxMenuItemBridging<MenuItem> {};
118118

119+
#pragma mark - RCTDeviceEventEmitter events
120+
121+
using CustomDeviceEvent = NativeCxxModuleExampleCxxCustomDeviceEvent<
122+
std::string,
123+
int32_t,
124+
std::optional<float>>;
125+
126+
template <>
127+
struct Bridging<CustomDeviceEvent>
128+
: NativeCxxModuleExampleCxxCustomDeviceEventBridging<CustomDeviceEvent> {};
129+
119130
#pragma mark - implementation
120131
class NativeCxxModuleExample
121132
: public NativeCxxModuleExampleCxxSpec<NativeCxxModuleExample> {
@@ -185,7 +196,7 @@ class NativeCxxModuleExample
185196

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

188-
void emitCustomDeviceEvent(jsi::Runtime& rt, jsi::String eventName);
199+
void emitCustomDeviceEvent(jsi::Runtime& rt, const std::string& eventName);
189200

190201
void voidFuncThrows(jsi::Runtime& rt);
191202

packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ export type MenuItem = {
7474
items?: Array<MenuItem>,
7575
};
7676

77+
export type CustomDeviceEvent = {
78+
type: string,
79+
level: number,
80+
degree?: ?number,
81+
};
82+
7783
export interface Spec extends TurboModule {
7884
+getArray: (arg: Array<ObjectStruct | null>) => Array<ObjectStruct | null>;
7985
+getBool: (arg: boolean) => boolean;

packages/rn-tester/js/examples/TurboModule/NativeCxxModuleExampleExample.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class NativeCxxModuleExampleExample extends React.Component<{||}, State> {
165165
DeviceEventEmitter.addListener(CUSTOM_EVENT_TYPE, (...args) => {
166166
this._setResult(
167167
'emitDeviceEvent',
168-
`${CUSTOM_EVENT_TYPE}(${args.map(s => `${s}`).join(', ')})`,
168+
`${CUSTOM_EVENT_TYPE}(${args.map(s => (typeof s === 'object' ? JSON.stringify(s) : s)).join(', ')})`,
169169
);
170170
});
171171
NativeCxxModuleExample?.emitCustomDeviceEvent(CUSTOM_EVENT_TYPE);

0 commit comments

Comments
 (0)