Skip to content
Draft
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# Spec Codegen uses LF
*Spec.g.h eol=lf
/vnext/codegen/** eol=lf
/vnext/Microsoft.ReactNative.IntegrationTests/codegen/** eol=lf
/packages/sample-apps/codegen/** eol=lf

# Force Visual Studio project files (mostly XML) to CRLF
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Enable ability to codegen JSI C++ TurboModule specs",
"packageName": "@react-native-windows/cli",
"email": "30809111+acoates-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Enable ability to codegen JSI C++ TurboModule specs",
"packageName": "@react-native-windows/codegen",
"email": "30809111+acoates-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Update codegen version",
"packageName": "react-native-windows",
"email": "30809111+acoates-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
15 changes: 11 additions & 4 deletions packages/@react-native-windows/cli/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,25 @@ export class CodeGenWindows {
? path.join(this.root, pkgJson.codegenConfig.jsSrcsDir)
: this.root;

const generators = pkgJson.codegenConfig.windows.generators ?? [
'modulesWindows',
];

const jsRootPathRelative = path.relative(process.cwd(), jsRootDir);
const options = {
files: [
`${jsRootPathRelative}${
jsRootPathRelative ? '/' : ''
}**/*Native*.[jt]s`,
],
namespace: projectNamespace,
libraryName: projectName,
outdir: path.join(this.root, 'codegen'),
methodonly: false,
ts: false,
methodOnly: false,
modulesCxx: generators.indexOf('modulesCxx') !== -1,
modulesTypeScriptTypes:
generators.indexOf('modulesTypeScriptTypes') !== -1,
modulesWindows: generators.indexOf('modulesWindows') !== -1,
namespace: projectNamespace,
outputDirectory: path.join(this.root, 'codegen'),
test: !!this.options.check,
};

Expand Down
16 changes: 13 additions & 3 deletions packages/@react-native-windows/codegen/src/Cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,27 @@ const argv = yargs.options({
array: true,
describe: 'glob patterns for files which contains specs',
},
ts: {
modulesTypeScriptTypes: {
type: 'boolean',
describe: 'generate turbo module definition files in TypeScript',
default: false,
},
methodonly: {
modulesCxx: {
type: 'boolean',
describe: 'generate C++ JSI turbo module spec files',
default: false,
},
modulesWindows: {
type: 'boolean',
describe: 'generate turbo module spec files for REACT_MODULE',
default: false,
},
methodOnly: {
type: 'boolean',
describe: 'generate only method metadata in C++ turbo module spec',
default: false,
},
outdir: {
outputDirectory: {
type: 'string',
describe: 'output directory',
default: 'codegen',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ struct ::_MODULE_NAME_::Spec : winrt::Microsoft::ReactNative::TurboModuleSpec {
`;

export function createNM2Generator({
methodOnly,
namespace,
methodonly,
}: {
methodOnly: boolean;
namespace: string;
methodonly: boolean;
}) {
return (
_libraryName: string,
Expand Down Expand Up @@ -85,7 +85,7 @@ ${methods[0]}

// prepare constants
const constants = generateValidateConstants(nativeModule, aliases);
if (constants !== undefined && !methodonly) {
if (constants !== undefined && !methodOnly) {
tuples = `
static constexpr auto constants = std::tuple{
${constants[0]}
Expand Down
100 changes: 66 additions & 34 deletions packages/@react-native-windows/codegen/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ const schemaValidator = require(path.resolve(

interface Options {
libraryName: string;
schema: SchemaType;
outputDirectory: string;
methodOnly: boolean;
modulesCxx: boolean;
moduleSpecName: string;
modulesTypeScriptTypes: boolean;
modulesWindows: boolean;
namespace: string;
methodonly: boolean;
ts: boolean;
outputDirectory: string;
schema: SchemaType;
}

interface Config {
Expand Down Expand Up @@ -179,12 +181,14 @@ export function combineSchemas(files: string[]): SchemaType {
export function generate(
{
libraryName,
schema,
outputDirectory,
methodOnly,
modulesCxx,
moduleSpecName,
modulesTypeScriptTypes,
modulesWindows,
namespace,
methodonly,
ts,
outputDirectory,
schema,
}: Options,
{/*generators,*/ test}: Config,
): boolean {
Expand All @@ -204,10 +208,18 @@ export function generate(
);

const generateNM2 = createNM2Generator({
methodOnly,
namespace,
methodonly,
});

const generateJsiModuleH = require(path.resolve(
rncodegenPath,
'lib/generators/modules/GenerateModuleH',
)).generate;
const generateJsiModuleCpp = require(path.resolve(
rncodegenPath,
'lib/generators/modules/GenerateModuleCpp',
)).generate;
const generatorPropsH = require(path.resolve(
rncodegenPath,
'lib/generators/components/GeneratePropsH',
Expand Down Expand Up @@ -237,33 +249,43 @@ export function generate(
'lib/generators/components/GenerateEventEmitterCpp',
)).generate;

normalizeFileMap(
generateNM2(libraryName, schema, moduleSpecName),
outputDirectory,
generatedFiles,
);
const moduleGenerators = [];

if (ts) {
normalizeFileMap(
generateTypeScript(libraryName, schema, moduleSpecName),
outputDirectory,
generatedFiles,
);
if (modulesWindows) {
moduleGenerators.push(generateNM2);
}

if (modulesCxx) {
moduleGenerators.push(generateJsiModuleH);
moduleGenerators.push(generateJsiModuleCpp);
}

if (modulesTypeScriptTypes) {
moduleGenerators.push(generateTypeScript);
}

moduleGenerators.forEach(generator => {
const generated: Map<string, string> = generator(
libraryName,
schema,
moduleSpecName,
);
normalizeFileMap(generated, outputDirectory, generatedFiles);
});

if (
Object.keys(schema.modules).some(
moduleName => schema.modules[moduleName].type === 'Component',
)
) {
const componentGenerators = [
generatorPropsH,
generatorPropsCPP,
generatorShadowNodeH,
generatorShadowNodeCPP,
generatorComponentDescriptorH,
generatorEventEmitterH,
generatorEventEmitterCPP,
generatorEventEmitterH,
generatorPropsCPP,
generatorPropsH,
generatorShadowNodeCPP,
generatorShadowNodeH,
];

componentGenerators.forEach(generator => {
Expand All @@ -287,10 +309,12 @@ export type CodeGenOptions = {
file?: string;
files?: string[];
libraryName: string;
outdir: string;
methodOnly: boolean;
modulesCxx: boolean;
modulesTypeScriptTypes: boolean;
modulesWindows: boolean;
namespace: string;
methodonly: boolean;
ts: boolean;
outputDirectory: string;
test: boolean;
};

Expand All @@ -304,17 +328,25 @@ export function runCodeGen(options: CodeGenOptions): boolean {

const libraryName = options.libraryName;
const moduleSpecName = 'moduleSpecName';
const outputDirectory = options.outdir;
const {namespace, methodonly, ts} = options;
const {
methodOnly,
modulesCxx,
modulesTypeScriptTypes,
modulesWindows,
namespace,
outputDirectory,
} = options;
return generate(
{
libraryName,
schema,
outputDirectory,
methodOnly,
modulesCxx,
moduleSpecName,
modulesTypeScriptTypes,
modulesWindows,
namespace,
methodonly,
ts,
outputDirectory,
schema,
},
{generators: [], test: options.test},
);
Expand Down
101 changes: 101 additions & 0 deletions packages/sample-apps/codegen/NativeMyJsiModuleSpec.g.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@

/*
* This file is auto-generated from a NativeModule spec file in js.
*
* This is a C++ Spec class that should be used with MakeTurboModuleProvider to register native modules
* in a way that also verifies at compile time that the native module matches the interface required
* by the TurboModule JS spec.
*/
#pragma once
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pragma

This file does not look right yet. I guess it is still a TODO item.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats wrong with it? -- This should be the NM2 spec for the same module. (We happen to be using the c++ JSI spec to implement it in this case rather than this one)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it work? I do not see generated code with the custom attributes. The spec cannot be verified if there are no custom attributes.


#include "NativeModules.h"
#include <tuple>

namespace SampleLibraryCodegen {

REACT_STRUCT(MyJsiModuleSpec_Constants)
struct MyJsiModuleSpec_Constants {
REACT_FIELD(const1)
bool const1;
REACT_FIELD(const2)
double const2;
REACT_FIELD(const3)
std::string const3;
};

struct MyJsiModuleSpec : winrt::Microsoft::ReactNative::TurboModuleSpec {
static constexpr auto constants = std::tuple{
TypedConstant<MyJsiModuleSpec_Constants>{0},
};
static constexpr auto methods = std::tuple{
Method<void() noexcept>{0, L"voidFunc"},
SyncMethod<bool(bool) noexcept>{1, L"getBool"},
SyncMethod<double(double) noexcept>{2, L"getNumber"},
SyncMethod<std::string(std::string) noexcept>{3, L"getString"},
SyncMethod<::React::JSValueArray(::React::JSValueArray) noexcept>{4, L"getArray"},
SyncMethod<::React::JSValue(::React::JSValue) noexcept>{5, L"getObject"},
SyncMethod<::React::JSValue(double, std::string, ::React::JSValue) noexcept>{6, L"getValue"},
Method<void(Callback<std::string>) noexcept>{7, L"getValueWithCallback"},
Method<void(bool, Promise<::React::JSValue>) noexcept>{8, L"getValueWithPromise"},
};

template <class TModule>
static constexpr void ValidateModule() noexcept {
constexpr auto constantCheckResults = CheckConstants<TModule, MyJsiModuleSpec>();
constexpr auto methodCheckResults = CheckMethods<TModule, MyJsiModuleSpec>();

REACT_SHOW_CONSTANT_SPEC_ERRORS(
0,
"MyJsiModuleSpec_Constants",
" REACT_GET_CONSTANTS(GetConstants) MyJsiModuleSpec_Constants GetConstants() noexcept {/*implementation*/}\n"
" REACT_GET_CONSTANTS(GetConstants) static MyJsiModuleSpec_Constants GetConstants() noexcept {/*implementation*/}\n");

REACT_SHOW_METHOD_SPEC_ERRORS(
0,
"voidFunc",
" REACT_METHOD(voidFunc) void voidFunc() noexcept { /* implementation */ }\n"
" REACT_METHOD(voidFunc) static void voidFunc() noexcept { /* implementation */ }\n");
REACT_SHOW_METHOD_SPEC_ERRORS(
1,
"getBool",
" REACT_SYNC_METHOD(getBool) bool getBool(bool arg) noexcept { /* implementation */ }\n"
" REACT_SYNC_METHOD(getBool) static bool getBool(bool arg) noexcept { /* implementation */ }\n");
REACT_SHOW_METHOD_SPEC_ERRORS(
2,
"getNumber",
" REACT_SYNC_METHOD(getNumber) double getNumber(double arg) noexcept { /* implementation */ }\n"
" REACT_SYNC_METHOD(getNumber) static double getNumber(double arg) noexcept { /* implementation */ }\n");
REACT_SHOW_METHOD_SPEC_ERRORS(
3,
"getString",
" REACT_SYNC_METHOD(getString) std::string getString(std::string arg) noexcept { /* implementation */ }\n"
" REACT_SYNC_METHOD(getString) static std::string getString(std::string arg) noexcept { /* implementation */ }\n");
REACT_SHOW_METHOD_SPEC_ERRORS(
4,
"getArray",
" REACT_SYNC_METHOD(getArray) ::React::JSValueArray getArray(::React::JSValueArray && arg) noexcept { /* implementation */ }\n"
" REACT_SYNC_METHOD(getArray) static ::React::JSValueArray getArray(::React::JSValueArray && arg) noexcept { /* implementation */ }\n");
REACT_SHOW_METHOD_SPEC_ERRORS(
5,
"getObject",
" REACT_SYNC_METHOD(getObject) ::React::JSValue getObject(::React::JSValue && arg) noexcept { /* implementation */ }\n"
" REACT_SYNC_METHOD(getObject) static ::React::JSValue getObject(::React::JSValue && arg) noexcept { /* implementation */ }\n");
REACT_SHOW_METHOD_SPEC_ERRORS(
6,
"getValue",
" REACT_SYNC_METHOD(getValue) ::React::JSValue getValue(double x, std::string y, ::React::JSValue && z) noexcept { /* implementation */ }\n"
" REACT_SYNC_METHOD(getValue) static ::React::JSValue getValue(double x, std::string y, ::React::JSValue && z) noexcept { /* implementation */ }\n");
REACT_SHOW_METHOD_SPEC_ERRORS(
7,
"getValueWithCallback",
" REACT_METHOD(getValueWithCallback) void getValueWithCallback(std::function<void(std::string)> const & callback) noexcept { /* implementation */ }\n"
" REACT_METHOD(getValueWithCallback) static void getValueWithCallback(std::function<void(std::string)> const & callback) noexcept { /* implementation */ }\n");
REACT_SHOW_METHOD_SPEC_ERRORS(
8,
"getValueWithPromise",
" REACT_METHOD(getValueWithPromise) void getValueWithPromise(bool error, ::React::ReactPromise<::React::JSValue> &&result) noexcept { /* implementation */ }\n"
" REACT_METHOD(getValueWithPromise) static void getValueWithPromise(bool error, ::React::ReactPromise<::React::JSValue> &&result) noexcept { /* implementation */ }\n");
}
};

} // namespace SampleLibraryCodegen
Loading