-
-
Notifications
You must be signed in to change notification settings - Fork 150
Add TurboModule and CodegenNativeComponent bindings for React Native New Architecture #840
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, this should be enough:
module CodegenNativeComponent = {
type options = {
interfaceOnly?: bool,
paperComponentName?: string,
paperComponentNameDeprecated?: string,
excludedPlatforms?: array<[#iOS | #android]>,
}
type nativeComponentType<'props> = React.component<'props>
@module("react-native")
external codegenNativeComponent: (
string,
~options: options=?,
) => nativeComponentType<'props> = "codegenNativeComponent"
}
module TurboModuleRegistry = {
@module("react-native") @scope("TurboModuleRegistry")
external get: string => nullable<'t> = "get"
@module("react-native") @scope("TurboModuleRegistry")
external getEnforcing: string => 't = "getEnforcing"
}
hi @Freddy03h, Thanks for reviewing the PR. Let me give you more insights. This PR introduces ReScript support for React Native's codegen system. Currently, React Native codegen works by parsing TypeScript or Flow files using respective AST parsers (like I've implemented a Flow-like AST parser for ReScript that translates ReScript syntax into the same Flow AST format that the existing codegen pipeline expects. This allows developers to write native module and component specifications in ReScript while maintaining full compatibility with the existing codegen infrastructure. Could you please review the implementation and suggest any improvements? I'm particularly interested in feedback on the best Rescript syntax for this for AST transformation logic and whether the ReScript-to-Flow AST mapping aligns well with the existing codegen expectations. NativeTurboModule.resopen ReactNative
open TurboModule
type spec = {
...turboModule,
initialise: ((option<string>, option<string>) => unit) => unit,
getSession: (
Js.Dict.t<string>,
Js.Dict.t<string>,
array<Js.Json.t>,
(option<string>, option<string>) => unit,
) => unit,
exit: string => unit,
}
let hyperHeadlessModule: get<spec> = get("HyperHeadlessModule") NativeButtonComponent.resopen ReactNative
open CodegenNativeComponent
module NativeButton = {
type props = {
...View.viewProps,
title: string,
color?: string,
}
let make: nativeComponentType<props> = codegenNativeComponent("NativeButton", ~options=makeOptions(~interfaceOnly=true, ()))
} |
hi @ricku44 |
Shared code works well, I'm wondering how should we add ReScript-specific validations that React Native enforces. Codegen supports TypeScript and Flow primarily and not JS maybe for type enforcement at the JavaScript-native boundary, preventing runtime crashes and ensuring performance. NativeModule and NativeComponent registration patterns involves getConstants and ViewProps enforcement. Works ✅import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeLocalStorage'); Fails ❌import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
export interface Spec {}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeLocalStorage'); Fails ❌import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {}
export default TurboModuleRegistry.getEnforcing('NativeLocalStorage'); Fails ❌import {TurboModuleRegistry} from 'react-native';
export interface Spec {
getConstants(): {};
}
export default TurboModuleRegistry.getEnforcing('NativeLocalStorage'); Works ✅import type {ViewProps} from 'react-native';
import {codegenNativeComponent} from 'react-native';
export interface NativeProps extends ViewProps {
sourceURL?: string;
}
export default codegenNativeComponent<NativeProps>('CustomWebView'); Fails ❌import {codegenNativeComponent} from 'react-native';
export interface NativeProps {
sourceURL?: string;
}
export default codegenNativeComponent<NativeProps>('CustomWebView'); Fails ❌import type {ViewProps} from 'react-native';
import {codegenNativeComponent} from 'react-native';
export interface NativeProps extends ViewProps {
sourceURL?: string;
}
export default codegenNativeComponent('CustomWebView'); |
I think this is more than just bindings. I can't help you with the AST Parser or even if you want to use genType (I never used it). The only things I can't tell about your bindings is that you should prefer |
Should we make the flow consistent in NativeModule.res as well? |
@Freddy03h PR is ready for review. |
Changes Made
src/apis/TurboModule.res
: Core TurboModule type definitions with proper inheritance enforcementsrc/apis/CodegenNativeComponent.res
: Bindings for native component code generationTesting