Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Add support for turbomodule eventemitters, and codegen",
"packageName": "@react-native-windows/codegen",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Add support for turbomodule eventemitters, and codegen",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,11 @@ export function createNM2Generator({
});
let tuples = `
static constexpr auto methods = std::tuple{
${methods[0]}
${methods.traversedPropertyTuples}${methods.traversedEventEmitterTuples ? '\n' : ''}${methods.traversedEventEmitterTuples}
};`;
let checks = `
constexpr auto methodCheckResults = CheckMethods<TModule, ::_MODULE_NAME_::Spec>();`;
let errors = methods[1];
let errors = methods.traversedProperties + (methods.traversedEventEmitters ? '\n' : '') + methods.traversedEventEmitters;

// prepare constants
const constants = generateValidateConstants(nativeModule, aliases);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type {
NamedShape,
NativeModuleArrayTypeAnnotation,
NativeModuleBaseTypeAnnotation,
NativeModuleEventEmitterBaseTypeAnnotation,
NativeModuleEventEmitterTypeAnnotation,
NativeModuleEnumDeclaration,
NativeModuleFunctionTypeAnnotation,
NativeModuleParamTypeAnnotation,
Expand Down Expand Up @@ -141,6 +143,39 @@ function translateArray(
}
}

// Hopefully eventually NativeModuleEventEmitterTypeAnnotation will align better with NativeModuleParamTypeAnnotation
// and this method can be merged / replaced with translateArray
function translateEventEmitterArray(
param: {
readonly type: 'ArrayTypeAnnotation';
readonly elementType: NativeModuleEventEmitterBaseTypeAnnotation | {type: string};
},
aliases: AliasMap,
baseAliasName: string,
target: ParamTarget,
options: CppCodegenOptions,
): string {
switch (target) {
case 'spec':
case 'template':
return `std::vector<${translateEventEmitterParam(
param.elementType as NativeModuleEventEmitterBaseTypeAnnotation,
aliases,
`${baseAliasName}_element`,
'template',
options,
)}>`;
default:
return `std::vector<${translateEventEmitterParam(
param.elementType as NativeModuleEventEmitterBaseTypeAnnotation,
aliases,
`${baseAliasName}_element`,
'template',
options,
)}> const &`;
}
}

function translateParam(
param: NativeModuleParamTypeAnnotation,
aliases: AliasMap,
Expand Down Expand Up @@ -193,6 +228,39 @@ function translateParam(
}
}

// Hopefully eventually NativeModuleEventEmitterTypeAnnotation will align better with NativeModuleParamTypeAnnotation
// and this method can be merged / replaced with translateParam
function translateEventEmitterParam(
param: NativeModuleEventEmitterTypeAnnotation,
aliases: AliasMap,
baseAliasName: string,
target: ParamTarget,
options: CppCodegenOptions,
): string {
// avoid: Property 'type' does not exist on type 'never'
const paramType = param.type;
switch (param.type) {
case 'StringTypeAnnotation':
return options.cppStringType;
case 'NumberTypeAnnotation':
case 'FloatTypeAnnotation':
case 'DoubleTypeAnnotation':
return 'double';
case 'Int32TypeAnnotation':
return 'int';
case 'BooleanTypeAnnotation':
return 'bool';
case 'ArrayTypeAnnotation':
return translateEventEmitterArray(param, aliases, baseAliasName, target, options);
case 'TypeAliasTypeAnnotation':
return decorateType(getAliasCppName(param.name), target);
case 'VoidTypeAnnotation':
return 'void';
default:
throw new Error(`Unhandled type in translateParam: ${paramType}`);
}
}

function translateNullableParamType(
paramType: Nullable<NativeModuleParamTypeAnnotation>,
aliases: AliasMap,
Expand Down Expand Up @@ -280,6 +348,22 @@ export function translateSpecArgs(
});
}

export function translateEventEmitterArgs(
params: NativeModuleEventEmitterTypeAnnotation,
aliases: AliasMap,
baseAliasName: string,
options: CppCodegenOptions,
) {
const translatedParam = translateEventEmitterParam(
params,
aliases,
baseAliasName,
'spec',
options,
);
return `${translatedParam}`;
}

export function translateArgs(
params: ReadonlyArray<NativeModuleParamShape>,
aliases: AliasMap,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
'use strict';

import type {
NativeModuleEventEmitterShape,
NativeModuleFunctionTypeAnnotation,
NativeModulePropertyShape,
NativeModuleSchema,
} from '@react-native/codegen/lib/CodegenSchema';
import {AliasMap} from './AliasManaging';
import type {CppCodegenOptions} from './ObjectTypes';
import {translateArgs, translateSpecArgs} from './ParamTypes';
import {translateArgs, translateSpecArgs, translateEventEmitterArgs} from './ParamTypes';
import {translateImplReturnType, translateSpecReturnType} from './ReturnTypes';

function isMethodSync(funcType: NativeModuleFunctionTypeAnnotation) {
Expand Down Expand Up @@ -91,9 +92,9 @@ function renderProperties(
aliases: AliasMap,
tuple: boolean,
options: CppCodegenOptions,
): string {
): { code: string, numberOfProperties: number } {
// TODO: generate code for constants
return methods
const properties = methods
.filter(prop => prop.name !== 'getConstants')
.map((prop, index) => {
// TODO: prop.optional === true
Expand Down Expand Up @@ -151,6 +152,66 @@ function renderProperties(
options,
)});`;
}
});

return {code: properties.join('\n'), numberOfProperties: properties.length};
}

function getPossibleEventEmitterSignatures(
eventEmitter: NativeModuleEventEmitterShape,
aliases: AliasMap,
options: CppCodegenOptions): string[] {

const traversedArgs = translateEventEmitterArgs(
eventEmitter.typeAnnotation.typeAnnotation,
aliases,
eventEmitter.name,
options,
);
return [`REACT_EVENT(${eventEmitter.name}) std::function<void(${traversedArgs})> ${eventEmitter.name};`]
}

function translatePossibleEventSignatures(
eventEmitter: NativeModuleEventEmitterShape,
aliases: AliasMap,
options: CppCodegenOptions): string {
return getPossibleEventEmitterSignatures(
eventEmitter,
aliases,
options
)
.map(sig => `" ${sig}\\n"`)
.join('\n ');
}

function renderEventEmitters(
eventEmitters: ReadonlyArray<NativeModuleEventEmitterShape>,
indexOffset: number,
aliases: AliasMap,
tuple: boolean,
options: CppCodegenOptions,
): string {
return eventEmitters
.map((eventEmitter, index) => {
const traversedArgs = translateEventEmitterArgs(
eventEmitter.typeAnnotation.typeAnnotation,
aliases,
eventEmitter.name,
options,
);

if (tuple) {
return ` EventEmitter<void(${traversedArgs})>{${index + indexOffset}, L"${eventEmitter.name}"},`;
} else {
return ` REACT_SHOW_EVENTEMITTER_SPEC_ERRORS(
${index + indexOffset},
"${eventEmitter.name}",
${translatePossibleEventSignatures(
eventEmitter,
aliases,
options,
)});`;
}
})
.join('\n');
}
Expand All @@ -159,19 +220,39 @@ export function generateValidateMethods(
nativeModule: NativeModuleSchema,
aliases: AliasMap,
options: CppCodegenOptions,
): [string, string] {
): {
traversedProperties: string,
traversedEventEmitters: string,
traversedPropertyTuples: string,
traversedEventEmitterTuples: string,
} {
const methods = nativeModule.spec.methods;
const eventEmitters = nativeModule.spec.eventEmitters;
const traversedProperties = renderProperties(
methods,
aliases,
false,
options,
);
const traversedEventEmitters = renderEventEmitters(
eventEmitters,
traversedProperties.numberOfProperties,
aliases,
false,
options,
);
const traversedPropertyTuples = renderProperties(
methods,
aliases,
true,
options,
);
return [traversedPropertyTuples, traversedProperties];
const traversedEventEmitterTuples = renderEventEmitters(
eventEmitters,
traversedProperties.numberOfProperties,
aliases,
true,
options,
);
return {traversedPropertyTuples: traversedPropertyTuples.code, traversedEventEmitterTuples, traversedProperties: traversedProperties.code, traversedEventEmitters};
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,6 @@ class SampleTurboModuleExample extends React.Component<{||}, State> {
Windows]
*/

/*
[Windows Wee need to implement both a proper SampleTurboModule (issue #13531) and add EventEmitter support (issue #13532)
this.eventSubscriptions.push(
NativeSampleTurboModule.onPress(value => console.log('onPress: ()')),
);
Expand All @@ -245,8 +243,6 @@ class SampleTurboModuleExample extends React.Component<{||}, State> {
console.log(`onSubmit: (${JSON.stringify(value)})`),
),
);
Windows]
*/
}

componentWillUnmount() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ void ReactModuleBuilderMock::AddSyncMethod(hstring const &name, SyncMethodDelega
m_syncMethods.emplace(name, method);
}

void ReactModuleBuilderMock::AddEventEmitter(hstring const &, EventEmitterInitializerDelegate const &) noexcept {}

JSValueObject ReactModuleBuilderMock::GetConstants() noexcept {
auto constantWriter = MakeJSValueTreeWriter();
constantWriter.WriteObjectBegin();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ struct ReactModuleBuilderMock {
void AddConstantProvider(ConstantProviderDelegate const &constantProvider) noexcept;
void AddMethod(hstring const &name, MethodReturnType returnType, MethodDelegate const &method) noexcept;
void AddSyncMethod(hstring const &name, SyncMethodDelegate const &method) noexcept;
void AddEventEmitter(hstring const &name, EventEmitterInitializerDelegate const &emitter) noexcept;

private:
MethodDelegate GetMethod0(std::wstring const &methodName) const noexcept;
Expand Down Expand Up @@ -218,6 +219,7 @@ struct ReactModuleBuilderImpl : implements<ReactModuleBuilderImpl, IReactModuleB
void AddConstantProvider(ConstantProviderDelegate const &constantProvider) noexcept;
void AddMethod(hstring const &name, MethodReturnType returnType, MethodDelegate const &method) noexcept;
void AddSyncMethod(hstring const &name, SyncMethodDelegate const &method) noexcept;
void AddEventEmitter(hstring const &name, EventEmitterInitializerDelegate const &emitter) noexcept;

private:
ReactModuleBuilderMock &m_mock;
Expand Down Expand Up @@ -352,4 +354,10 @@ inline void ReactModuleBuilderImpl::AddSyncMethod(hstring const &name, SyncMetho
m_mock.AddSyncMethod(name, method);
}

inline void ReactModuleBuilderImpl::AddEventEmitter(
hstring const &name,
EventEmitterInitializerDelegate const &emitter) noexcept {
m_mock.AddEventEmitter(name, emitter);
}

} // namespace winrt::Microsoft::ReactNative
4 changes: 2 additions & 2 deletions vnext/Microsoft.ReactNative.Cxx/ModuleRegistration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ModuleRegistration::ModuleRegistration(wchar_t const *moduleName) noexcept : m_m

void AddAttributedModules(IReactPackageBuilder const &packageBuilder, bool useTurboModules) noexcept {
for (auto const *reg = ModuleRegistration::Head(); reg != nullptr; reg = reg->Next()) {
if (useTurboModules)
if (useTurboModules || reg->ShouldRegisterAsTurboModule())
packageBuilder.AddTurboModule(reg->ModuleName(), reg->MakeModuleProvider());
else
packageBuilder.AddModule(reg->ModuleName(), reg->MakeModuleProvider());
Expand All @@ -30,7 +30,7 @@ bool TryAddAttributedModule(
bool useTurboModule) noexcept {
for (auto const *reg = ModuleRegistration::Head(); reg != nullptr; reg = reg->Next()) {
if (moduleName == reg->ModuleName()) {
if (useTurboModule) {
if (useTurboModule || reg->ShouldRegisterAsTurboModule()) {
packageBuilder.AddTurboModule(moduleName, reg->MakeModuleProvider());
} else {
packageBuilder.AddModule(moduleName, reg->MakeModuleProvider());
Expand Down
Loading