From a15fc102e63eb3b37852ca45fe4c65e894ecef7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 17 Jun 2025 02:24:22 -0700 Subject: [PATCH] Add support for opaque types in Flow codegen for native modules (#52052) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52052 Changelog: [General][Added] - Add support for Flow opaque types in codegen for native modules This allows us to codegen native modules that expose opaque types, but the implementation sees the type the same way they're visible in the JS spec. Reviewed By: yungsters Differential Revision: D76741112 fbshipit-source-id: 100ca9aa7f93d35120c52153f756436c9c380b07 --- .../__tests__/checkModuleSnaps-test.js | 5 +- .../modules/__test_fixtures__/fixtures.js | 32 ++++++ .../module-parser-snapshot-test.js.snap | 102 ++++++++++++++++++ .../src/parsers/flow/parser.js | 6 ++ .../src/parsers/parsers-commons.js | 3 +- 5 files changed, 146 insertions(+), 2 deletions(-) diff --git a/packages/react-native-codegen/src/parsers/consistency/__tests__/checkModuleSnaps-test.js b/packages/react-native-codegen/src/parsers/consistency/__tests__/checkModuleSnaps-test.js index c8d0fbeaacb..d1685f84dc4 100644 --- a/packages/react-native-codegen/src/parsers/consistency/__tests__/checkModuleSnaps-test.js +++ b/packages/react-native-codegen/src/parsers/consistency/__tests__/checkModuleSnaps-test.js @@ -18,7 +18,10 @@ const flowFixtures = require('../../flow/modules/__test_fixtures__/fixtures.js') const tsFixtures = require('../../typescript/modules/__test_fixtures__/fixtures.js'); const {compareSnaps, compareTsArraySnaps} = require('../compareSnaps.js'); -const flowExtraCases = ['PROMISE_WITH_COMMONLY_USED_TYPES']; +const flowExtraCases = [ + 'NATIVE_MODULE_WITH_OPAQUE_TYPES', + 'PROMISE_WITH_COMMONLY_USED_TYPES', +]; const tsExtraCases = [ 'NATIVE_MODULE_WITH_ARRAY2_WITH_ALIAS', 'NATIVE_MODULE_WITH_ARRAY2_WITH_UNION_AND_TOUPLE', diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js index 0665460506d..f87e0d67683 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js @@ -691,6 +691,37 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; +const NATIVE_MODULE_WITH_OPAQUE_TYPES = ` +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +'use strict'; + +import type {TurboModule} from '../RCTExport'; +import * as TurboModuleRegistry from '../TurboModuleRegistry'; + +export opaque type Task = mixed; +export opaque type TimeoutID = number; + +export interface Spec extends TurboModule { + +createTask: (callback: () => void) => Task; + +cancelTask: (task: Task) => void; + + +setTimeout: (callback: () => void) => TimeoutID; + +clearTimeout: (timeoutID: TimeoutID) => void; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + const ANDROID_ONLY_NATIVE_MODULE = ` /** * Copyright (c) Meta Platforms, Inc. and affiliates. @@ -976,6 +1007,7 @@ module.exports = { NATIVE_MODULE_WITH_UNION, NATIVE_MODULE_WITH_UNION_RETURN_TYPES, NATIVE_MODULE_WITH_EVENT_EMITTERS, + NATIVE_MODULE_WITH_OPAQUE_TYPES, EMPTY_NATIVE_MODULE, ANDROID_ONLY_NATIVE_MODULE, IOS_ONLY_NATIVE_MODULE, diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap index 1d40eacb654..d76198a01b8 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap +++ b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap @@ -2187,6 +2187,108 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OBJECT_W }" `; +exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OPAQUE_TYPES 1`] = ` +"{ + 'modules': { + 'NativeSampleTurboModule': { + 'type': 'NativeModule', + 'aliasMap': {}, + 'enumMap': {}, + 'spec': { + 'eventEmitters': [], + 'methods': [ + { + 'name': 'createTask', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'GenericObjectTypeAnnotation' + }, + 'params': [ + { + 'name': 'callback', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'VoidTypeAnnotation' + }, + 'params': [] + } + } + ] + } + }, + { + 'name': 'cancelTask', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'VoidTypeAnnotation' + }, + 'params': [ + { + 'name': 'task', + 'optional': false, + 'typeAnnotation': { + 'type': 'GenericObjectTypeAnnotation' + } + } + ] + } + }, + { + 'name': 'setTimeout', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'NumberTypeAnnotation' + }, + 'params': [ + { + 'name': 'callback', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'VoidTypeAnnotation' + }, + 'params': [] + } + } + ] + } + }, + { + 'name': 'clearTimeout', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'VoidTypeAnnotation' + }, + 'params': [ + { + 'name': 'timeoutID', + 'optional': false, + 'typeAnnotation': { + 'type': 'NumberTypeAnnotation' + } + } + ] + } + } + ] + }, + 'moduleName': 'SampleTurboModule' + } + } +}" +`; + exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PARTIALS 1`] = ` "{ 'modules': { diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index dc3a9314b47..4369892edab 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -296,6 +296,7 @@ class FlowParser implements Parser { if ( node.declaration != null && (node.declaration.type === 'TypeAlias' || + node.declaration.type === 'OpaqueType' || node.declaration.type === 'InterfaceDeclaration') ) { types[node.declaration.id.name] = node.declaration; @@ -309,6 +310,7 @@ class FlowParser implements Parser { types[node.declaration.id.name] = node.declaration; } else if ( node.type === 'TypeAlias' || + node.type === 'OpaqueType' || node.type === 'InterfaceDeclaration' || node.type === 'EnumDeclaration' ) { @@ -543,6 +545,10 @@ class FlowParser implements Parser { } nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe { + if (typeAnnotation.type === 'OpaqueType') { + return typeAnnotation.impltype; + } + return typeAnnotation.right; } diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index d7e08629869..9e7ea4ef72f 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -1233,7 +1233,8 @@ function handleGenericTypeAnnotation( let node; switch (resolvedTypeAnnotation.type) { - case parser.typeAlias: { + case parser.typeAlias: + case 'OpaqueType': { typeResolutionStatus = getTypeResolutionStatus( 'alias', typeAnnotation,