diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index ab0ff056e79613..c431e335f2fa8e 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -223,6 +223,7 @@ export type NullableTypeAnnotation<+T: NativeModuleTypeAnnotation> = $ReadOnly<{ export type NativeModuleSchema = $ReadOnly<{ type: 'NativeModule', aliasMap: NativeModuleAliasMap, + enumMap: NativeModuleEnumMap, spec: NativeModuleSpec, moduleName: string, // Use for modules that are not used on other platforms. @@ -239,6 +240,10 @@ export type NativeModulePropertyShape = NamedShape< Nullable, >; +export type NativeModuleEnumMap = $ReadOnly<{ + [enumName: string]: NativeModuleEnumDeclarationWithMembers, +}>; + export type NativeModuleAliasMap = $ReadOnly<{ [aliasName: string]: NativeModuleObjectTypeAnnotation, }>; @@ -287,11 +292,30 @@ export type NativeModuleBooleanTypeAnnotation = $ReadOnly<{ type: 'BooleanTypeAnnotation', }>; +export type NativeModuleEnumMembers = $ReadOnlyArray< + $ReadOnly<{ + name: string, + value: string, + }>, +>; + +export type NativeModuleMemberType = + | 'NumberTypeAnnotation' + | 'StringTypeAnnotation'; + export type NativeModuleEnumDeclaration = $ReadOnly<{ + name: string, type: 'EnumDeclaration', - memberType: 'NumberTypeAnnotation' | 'StringTypeAnnotation', + memberType: NativeModuleMemberType, }>; +export type NativeModuleEnumDeclarationWithMembers = { + name: string, + type: 'EnumDeclarationWithMembers', + memberType: NativeModuleMemberType, + members: NativeModuleEnumMembers, +}; + export type NativeModuleGenericObjectTypeAnnotation = $ReadOnly<{ type: 'GenericObjectTypeAnnotation', }>; diff --git a/packages/react-native-codegen/src/generators/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/generators/__test_fixtures__/fixtures.js index 2a60dc45f888a4..8678249f5a8207 100644 --- a/packages/react-native-codegen/src/generators/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/generators/__test_fixtures__/fixtures.js @@ -42,6 +42,7 @@ const SCHEMA_WITH_TM_AND_FC: SchemaType = { NativeCalculator: { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: { properties: [ { diff --git a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js index 0f838c2d836abd..5764facb3bc32e 100644 --- a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js @@ -17,6 +17,7 @@ const EMPTY_NATIVE_MODULES: SchemaType = { NativeSampleTurboModule: { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: { properties: [], }, @@ -30,6 +31,7 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { NativeSampleTurboModule: { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: { properties: [ { @@ -342,6 +344,7 @@ const TWO_MODULES_DIFFERENT_FILES: SchemaType = { NativeSampleTurboModule: { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: { properties: [ { @@ -362,6 +365,7 @@ const TWO_MODULES_DIFFERENT_FILES: SchemaType = { NativeSampleTurboModule2: { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: { properties: [ { @@ -399,6 +403,7 @@ const COMPLEX_OBJECTS: SchemaType = { NativeSampleTurboModule: { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: { properties: [ { @@ -854,6 +859,7 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { ], }, }, + enumMap: {}, spec: { properties: [ { @@ -1138,6 +1144,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { ], }, }, + enumMap: {}, spec: { properties: [ { @@ -1333,6 +1340,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { type: 'ObjectTypeAnnotation', }, }, + enumMap: {}, spec: { properties: [ { @@ -1508,6 +1516,7 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = { ], }, }, + enumMap: {}, spec: { properties: [ { @@ -1648,6 +1657,7 @@ const SAMPLE_WITH_UPPERCASE_NAME: SchemaType = { modules: { NativeSampleTurboModule: { type: 'NativeModule', + enumMap: {}, aliasMap: {}, spec: { properties: [], diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js index bd97c0f757a406..95ef4306ebee73 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js @@ -277,6 +277,7 @@ describe('parseObjectProperty', () => { const moduleName = 'testModuleName'; const types = {['wrongName']: 'wrongType'}; const aliasMap = {}; + const enumMap = {}; const tryParse = () => null; const cxxOnly = false; const nullable = true; @@ -305,6 +306,7 @@ describe('parseObjectProperty', () => { moduleName, types, aliasMap, + enumMap, tryParse, cxxOnly, nullable, @@ -338,6 +340,7 @@ describe('parseObjectProperty', () => { moduleName, types, aliasMap, + enumMap, tryParse, cxxOnly, nullable, @@ -375,6 +378,7 @@ describe('buildSchemaFromConfigType', () => { const moduleSchemaMock = { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: {properties: []}, moduleName: '', }; @@ -743,6 +747,7 @@ describe('buildSchema', () => { fileName: { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: { properties: [ { diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js index 92f391d3bcb24f..2825b8050eb64a 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js @@ -29,6 +29,7 @@ const { emitStringish, emitMixed, typeAliasResolution, + typeEnumResolution, } = require('../parsers-primitives.js'); const {MockedParser} = require('../parserMock'); const {emitUnion} = require('../parsers-primitives'); @@ -264,14 +265,14 @@ describe('typeAliasResolution', () => { ], }; - describe('when typeAliasResolutionStatus is successful', () => { - const typeAliasResolutionStatus = {successful: true, aliasName: 'Foo'}; + describe('when typeResolution is successful', () => { + const typeResolution = {successful: true, type: 'alias', name: 'Foo'}; describe('when nullable is true', () => { it('returns nullable TypeAliasTypeAnnotation and map it in aliasMap', () => { const aliasMap = {}; const result = typeAliasResolution( - typeAliasResolutionStatus, + typeResolution, objectTypeAnnotation, aliasMap, true, @@ -292,7 +293,7 @@ describe('typeAliasResolution', () => { it('returns non nullable TypeAliasTypeAnnotation and map it in aliasMap', () => { const aliasMap = {}; const result = typeAliasResolution( - typeAliasResolutionStatus, + typeResolution, objectTypeAnnotation, aliasMap, false, @@ -307,14 +308,14 @@ describe('typeAliasResolution', () => { }); }); - describe('when typeAliasResolutionStatus is not successful', () => { - const typeAliasResolutionStatus = {successful: false}; + describe('when typeResolution is not successful', () => { + const typeResolution = {successful: false}; describe('when nullable is true', () => { it('returns nullable ObjectTypeAnnotation', () => { const aliasMap = {}; const result = typeAliasResolution( - typeAliasResolutionStatus, + typeResolution, objectTypeAnnotation, aliasMap, true, @@ -332,7 +333,7 @@ describe('typeAliasResolution', () => { it('returns non nullable ObjectTypeAnnotation', () => { const aliasMap = {}; const result = typeAliasResolution( - typeAliasResolutionStatus, + typeResolution, objectTypeAnnotation, aliasMap, false, @@ -345,6 +346,91 @@ describe('typeAliasResolution', () => { }); }); +describe('typeEnumResolution', () => { + const strMembers = [ + { + name: 'Hello', + value: 'hello', + }, + { + name: 'Goodbye', + value: 'goodbye', + }, + ]; + + const numMembers = [ + { + name: 'On', + value: '1', + }, + { + name: 'Off', + value: '0', + }, + ]; + + describe('when typeResolution is successful', () => { + describe('when nullable is true', () => { + it('returns nullable EnumDeclaration and map it in enumMap', () => { + const enumMap = {}; + const result = typeEnumResolution( + true /* nullable */, + 'Foo' /* name */, + 'StringTypeAnnotation', + strMembers, + enumMap, + ); + + expect(enumMap).toEqual({ + Foo: { + type: 'EnumDeclarationWithMembers', + name: 'Foo', + memberType: 'StringTypeAnnotation', + members: strMembers, + }, + }); + + expect(result).toEqual({ + type: 'NullableTypeAnnotation', + typeAnnotation: { + name: 'Foo', + type: 'EnumDeclaration', + memberType: 'StringTypeAnnotation', + }, + }); + }); + }); + + describe('when nullable is false', () => { + it('returns non nullable TypeAliasTypeAnnotation and map it in aliasMap', () => { + const enumMap = {}; + const result = typeEnumResolution( + false /* nullable */, + 'Foo' /* name */, + 'NumberTypeAnnotation', + numMembers, + enumMap, + ); + + expect(enumMap).toEqual({ + Foo: { + type: 'EnumDeclarationWithMembers', + name: 'Foo', + memberType: 'NumberTypeAnnotation', + members: numMembers, + }, + }); + + expect(result).toEqual({ + name: 'Foo', + type: 'EnumDeclaration', + memberType: 'NumberTypeAnnotation', + }); + }); + }); + }); +}); + describe('emitPromise', () => { const moduleName = 'testModuleName'; @@ -362,6 +448,8 @@ describe('emitPromise', () => { {}, /* aliasMap: {...NativeModuleAliasMap} */ {}, + /* enumMap: {...NativeModuleEnumMap} */ + {}, /* tryParse: ParserErrorCapturer */ // $FlowFixMe[missing-local-annot] function (_: () => T) { @@ -991,6 +1079,8 @@ describe('emitArrayType', () => { {}, /* aliasMap: {...NativeModuleAliasMap} */ {}, + /* enumMap: {...NativeModuleEnumMap} */ + {}, /* cxxOnly: boolean */ false, nullable, diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js index 8024079b0fb683..8734095f43f2d4 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/failures.js @@ -211,6 +211,60 @@ export interface Spec2 extends TurboModule { `; +const EMPTY_ENUM_NATIVE_MODULE = ` +/** + * 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 enum SomeEnum { +} + +export interface Spec extends TurboModule { + +getEnums: (a: SomeEnum) => string; +} + +export default TurboModuleRegistry.getEnforcing('EmptyEnumNativeModule'); +`; + +const MIXED_VALUES_ENUM_NATIVE_MODULE = ` +/** + * 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 enum SomeEnum { + NUM = 1, + STR = 'str', +} + +export interface Spec extends TurboModule { + +getEnums: (a: SomeEnum) => string; +} + +export default TurboModuleRegistry.getEnforcing('MixedValuesEnumNativeModule'); +`; + module.exports = { NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT, NATIVE_MODULES_WITH_UNNAMED_PARAMS, @@ -220,4 +274,6 @@ module.exports = { TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT, NATIVE_MODULES_WITH_NOT_ONLY_METHODS, TWO_NATIVE_EXTENDING_TURBO_MODULE, + EMPTY_ENUM_NATIVE_MODULE, + MIXED_VALUES_ENUM_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 ce01f78c2627f5..815aaf5f61c388 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 @@ -1,5 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`RN Codegen Flow Parser Fails with error message EMPTY_ENUM_NATIVE_MODULE 1`] = `"Enums should have at least one member and enum member values can not be mixed- they all must be either blank, number, or string values."`; + +exports[`RN Codegen Flow Parser Fails with error message MIXED_VALUES_ENUM_NATIVE_MODULE 1`] = `"Enums should have at least one member and enum member values can not be mixed- they all must be either blank, number, or string values."`; + exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT 1`] = `"Module NativeSampleTurboModule: Generic 'Array' must have type parameters."`; exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM 1`] = `"Module NativeSampleTurboModule: Generic 'Array' must have type parameters."`; @@ -22,6 +26,7 @@ exports[`RN Codegen Flow Parser can generate fixture ANDROID_ONLY_NATIVE_MODULE 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [] }, @@ -40,6 +45,72 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`] 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': { + 'Quality': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Quality', + 'memberType': 'StringTypeAnnotation', + 'members': [ + { + 'name': 'SD', + 'value': 'SD' + }, + { + 'name': 'HD', + 'value': 'HD' + } + ] + }, + 'Resolution': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Resolution', + 'memberType': 'NumberTypeAnnotation', + 'members': [ + { + 'name': 'Low', + 'value': 720 + }, + { + 'name': 'High', + 'value': 1080 + } + ] + }, + 'Floppy': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Floppy', + 'memberType': 'NumberTypeAnnotation', + 'members': [ + { + 'name': 'LowDensity', + 'value': 0.72 + }, + { + 'name': 'HighDensity', + 'value': 1.44 + } + ] + }, + 'StringOptions': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'StringOptions', + 'memberType': 'StringTypeAnnotation', + 'members': [ + { + 'name': 'One', + 'value': 'one' + }, + { + 'name': 'Two', + 'value': 'two' + }, + { + 'name': 'Three', + 'value': 'three' + } + ] + } + }, 'spec': { 'properties': [ { @@ -89,6 +160,7 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`] 'name': 'quality', 'optional': false, 'typeAnnotation': { + 'name': 'Quality', 'type': 'EnumDeclaration', 'memberType': 'StringTypeAnnotation' } @@ -97,6 +169,7 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`] 'name': 'resolution', 'optional': true, 'typeAnnotation': { + 'name': 'Resolution', 'type': 'EnumDeclaration', 'memberType': 'NumberTypeAnnotation' } @@ -105,6 +178,7 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`] 'name': 'floppy', 'optional': false, 'typeAnnotation': { + 'name': 'Floppy', 'type': 'EnumDeclaration', 'memberType': 'NumberTypeAnnotation' } @@ -113,6 +187,7 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`] 'name': 'stringOptions', 'optional': false, 'typeAnnotation': { + 'name': 'StringOptions', 'type': 'EnumDeclaration', 'memberType': 'StringTypeAnnotation' } @@ -221,6 +296,7 @@ exports[`RN Codegen Flow Parser can generate fixture EMPTY_NATIVE_MODULE 1`] = ` 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [] }, @@ -236,6 +312,72 @@ exports[`RN Codegen Flow Parser can generate fixture IOS_ONLY_NATIVE_MODULE 1`] 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': { + 'Quality': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Quality', + 'memberType': 'StringTypeAnnotation', + 'members': [ + { + 'name': 'SD', + 'value': 'SD' + }, + { + 'name': 'HD', + 'value': 'HD' + } + ] + }, + 'Resolution': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Resolution', + 'memberType': 'NumberTypeAnnotation', + 'members': [ + { + 'name': 'Low', + 'value': 720 + }, + { + 'name': 'High', + 'value': 1080 + } + ] + }, + 'Floppy': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Floppy', + 'memberType': 'NumberTypeAnnotation', + 'members': [ + { + 'name': 'LowDensity', + 'value': 0.72 + }, + { + 'name': 'HighDensity', + 'value': 1.44 + } + ] + }, + 'StringOptions': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'StringOptions', + 'memberType': 'StringTypeAnnotation', + 'members': [ + { + 'name': 'One', + 'value': 'one' + }, + { + 'name': 'Two', + 'value': 'two' + }, + { + 'name': 'Three', + 'value': 'three' + } + ] + } + }, 'spec': { 'properties': [ { @@ -251,6 +393,7 @@ exports[`RN Codegen Flow Parser can generate fixture IOS_ONLY_NATIVE_MODULE 1`] 'name': 'quality', 'optional': false, 'typeAnnotation': { + 'name': 'Quality', 'type': 'EnumDeclaration', 'memberType': 'StringTypeAnnotation' } @@ -259,6 +402,7 @@ exports[`RN Codegen Flow Parser can generate fixture IOS_ONLY_NATIVE_MODULE 1`] 'name': 'resolution', 'optional': true, 'typeAnnotation': { + 'name': 'Resolution', 'type': 'EnumDeclaration', 'memberType': 'NumberTypeAnnotation' } @@ -267,6 +411,7 @@ exports[`RN Codegen Flow Parser can generate fixture IOS_ONLY_NATIVE_MODULE 1`] 'name': 'floppy', 'optional': false, 'typeAnnotation': { + 'name': 'Floppy', 'type': 'EnumDeclaration', 'memberType': 'NumberTypeAnnotation' } @@ -275,6 +420,7 @@ exports[`RN Codegen Flow Parser can generate fixture IOS_ONLY_NATIVE_MODULE 1`] 'name': 'stringOptions', 'optional': false, 'typeAnnotation': { + 'name': 'StringOptions', 'type': 'EnumDeclaration', 'memberType': 'StringTypeAnnotation' } @@ -333,6 +479,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ALIASES ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -496,6 +643,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ARRAY_WI 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -537,6 +685,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ARRAY_WI 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -572,6 +721,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_AR 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -638,6 +788,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_PA 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -730,6 +881,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_CALLBACK 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -790,6 +942,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_ 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -849,6 +1002,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_ 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1061,6 +1215,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_ 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1165,6 +1320,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_FLOAT_AN 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1253,6 +1409,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_NESTED_A ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1310,6 +1467,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_NULLABLE 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1361,6 +1519,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OBJECT_W ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1457,6 +1616,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PARTIALS ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1568,6 +1728,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PARTIALS ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1656,6 +1817,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PROMISE ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1715,6 +1877,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ROOT_TAG 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1752,6 +1915,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_SIMPLE_O 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1787,6 +1951,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_UNION 1` 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1848,6 +2013,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_UNSAFE_O 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1921,6 +2087,7 @@ exports[`RN Codegen Flow Parser can generate fixture PROMISE_WITH_COMMONLY_USED_ ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { diff --git a/packages/react-native-codegen/src/parsers/flow/modules/index.js b/packages/react-native-codegen/src/parsers/flow/modules/index.js index d8fba8e465eff2..e27d3933dd5dd7 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -13,11 +13,13 @@ import type { NamedShape, NativeModuleAliasMap, + NativeModuleEnumMap, NativeModuleBaseTypeAnnotation, NativeModuleTypeAnnotation, NativeModulePropertyShape, NativeModuleSchema, Nullable, + NativeModuleMemberType, } from '../../../CodegenSchema'; import type {Parser} from '../../parser'; @@ -30,7 +32,6 @@ const { wrapNullable, assertGenericTypeAnnotationHasExactlyOneTypeParameter, parseObjectProperty, - translateDefault, buildPropertySchema, } = require('../../parsers-commons'); const { @@ -51,12 +52,13 @@ const { emitMixed, emitUnion, typeAliasResolution, - translateArrayTypeAnnotation, + typeEnumResolution, } = require('../../parsers-primitives'); const { UnsupportedTypeAnnotationParserError, IncorrectModuleRegistryCallArgumentTypeParserError, + UnsupportedGenericParserError, } = require('../../errors'); const { @@ -80,11 +82,12 @@ function translateTypeAnnotation( flowTypeAnnotation: $FlowFixMe, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, parser: Parser, ): Nullable { - const {nullable, typeAnnotation, typeAliasResolutionStatus} = + const {nullable, typeAnnotation, typeResolutionStatus} = resolveTypeAnnotation(flowTypeAnnotation, types); switch (typeAnnotation.type) { @@ -101,6 +104,7 @@ function translateTypeAnnotation( nullable, types, aliasMap, + enumMap, tryParse, cxxOnly, translateTypeAnnotation, @@ -114,6 +118,7 @@ function translateTypeAnnotation( parser, types, aliasMap, + enumMap, cxxOnly, nullable, translateTypeAnnotation, @@ -132,6 +137,7 @@ function translateTypeAnnotation( typeAnnotation.typeParameters.params[0], types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -181,6 +187,7 @@ function translateTypeAnnotation( prop.value, types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -191,11 +198,9 @@ function translateTypeAnnotation( return emitObject(nullable, properties); } default: { - return translateDefault( + throw new UnsupportedGenericParserError( hasteModuleName, typeAnnotation, - types, - nullable, parser, ); } @@ -216,6 +221,7 @@ function translateTypeAnnotation( propertyType, types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -240,6 +246,7 @@ function translateTypeAnnotation( hasteModuleName, types, aliasMap, + enumMap, tryParse, cxxOnly, nullable, @@ -253,7 +260,7 @@ function translateTypeAnnotation( }; return typeAliasResolution( - typeAliasResolutionStatus, + typeResolutionStatus, objectTypeAnnotation, aliasMap, nullable, @@ -278,6 +285,7 @@ function translateTypeAnnotation( typeAnnotation, types, aliasMap, + enumMap, tryParse, cxxOnly, translateTypeAnnotation, @@ -301,6 +309,50 @@ function translateTypeAnnotation( return emitGenericObject(nullable); } } + case 'EnumStringBody': + case 'EnumNumberBody': { + if (!typeResolutionStatus.successful) { + throw new UnsupportedTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + language, + ); + } + + if (!typeAnnotation.members || typeAnnotation.members.length === 0) { + // passing mixed members to flow would result in a flow error + // if the tool is launched ignoring that error, the enum would appear like not having enums + throw new Error( + 'Enums should have at least one member and enum member values can not be mixed- they all must be either blank, number, or string values.', + ); + } + + const enumType: ?NativeModuleMemberType = + typeAnnotation.type === 'EnumStringBody' + ? 'StringTypeAnnotation' + : typeAnnotation.type === 'EnumNumberBody' + ? 'NumberTypeAnnotation' + : null; + + if (!enumType) { + throw new Error( + 'Enum values must be either blank, number, or string values.', + ); + } + + const enumMembers = typeAnnotation.members.map(member => ({ + name: member.id.name, + value: member.init?.value ?? member.id.name, + })); + + return typeEnumResolution( + nullable, + typeResolutionStatus.name, + enumType, + enumMembers, + enumMap, + ); + } default: { throw new UnsupportedTypeAnnotationParserError( hasteModuleName, @@ -430,16 +482,20 @@ function buildModuleSchema( .filter(property => property.type === 'ObjectTypeProperty') .map(property => { const aliasMap: {...NativeModuleAliasMap} = {}; + const enumMap: {...NativeModuleEnumMap} = {}; return tryParse(() => ({ aliasMap: aliasMap, + enumMap: enumMap, propertyShape: buildPropertySchema( hasteModuleName, property, types, aliasMap, + enumMap, tryParse, cxxOnly, resolveTypeAnnotation, @@ -450,9 +506,13 @@ function buildModuleSchema( }) .filter(Boolean) .reduce( - (moduleSchema: NativeModuleSchema, {aliasMap, propertyShape}) => ({ + ( + moduleSchema: NativeModuleSchema, + {aliasMap, enumMap, propertyShape}, + ) => ({ type: 'NativeModule', aliasMap: {...moduleSchema.aliasMap, ...aliasMap}, + enumMap: {...moduleSchema.enumMap, ...enumMap}, spec: { properties: [...moduleSchema.spec.properties, propertyShape], }, @@ -462,6 +522,7 @@ function buildModuleSchema( { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: {properties: []}, moduleName, excludedPlatforms: diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index 561ae77a3f19fd..c7b745827582c1 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -54,16 +54,6 @@ class FlowParser implements Parser { return property.key.name; } - getMaybeEnumMemberType(maybeEnumDeclaration: $FlowFixMe): string { - return maybeEnumDeclaration.body.type - .replace('EnumNumberBody', 'NumberTypeAnnotation') - .replace('EnumStringBody', 'StringTypeAnnotation'); - } - - isEnumDeclaration(maybeEnumDeclaration: $FlowFixMe): boolean { - return maybeEnumDeclaration.type === 'EnumDeclaration'; - } - language(): ParserType { return 'Flow'; } diff --git a/packages/react-native-codegen/src/parsers/flow/utils.js b/packages/react-native-codegen/src/parsers/flow/utils.js index f172d8ec4c8283..d8939913389f0f 100644 --- a/packages/react-native-codegen/src/parsers/flow/utils.js +++ b/packages/react-native-codegen/src/parsers/flow/utils.js @@ -10,7 +10,7 @@ 'use strict'; -import type {TypeAliasResolutionStatus, TypeDeclarationMap} from '../utils'; +import type {TypeResolutionStatus, TypeDeclarationMap} from '../utils'; /** * This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias @@ -61,7 +61,7 @@ function resolveTypeAnnotation( ): { nullable: boolean, typeAnnotation: $FlowFixMe, - typeAliasResolutionStatus: TypeAliasResolutionStatus, + typeResolutionStatus: TypeResolutionStatus, } { invariant( typeAnnotation != null, @@ -70,7 +70,7 @@ function resolveTypeAnnotation( let node = typeAnnotation; let nullable = false; - let typeAliasResolutionStatus: TypeAliasResolutionStatus = { + let typeResolutionStatus: TypeResolutionStatus = { successful: false, }; @@ -78,34 +78,49 @@ function resolveTypeAnnotation( if (node.type === 'NullableTypeAnnotation') { nullable = true; node = node.typeAnnotation; - } else if (node.type === 'GenericTypeAnnotation') { - typeAliasResolutionStatus = { - successful: true, - aliasName: node.id.name, - }; - const resolvedTypeAnnotation = types[node.id.name]; - if ( - resolvedTypeAnnotation == null || - resolvedTypeAnnotation.type === 'EnumDeclaration' - ) { - break; - } + continue; + } - invariant( - resolvedTypeAnnotation.type === 'TypeAlias', - `GenericTypeAnnotation '${node.id.name}' must resolve to a TypeAlias. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`, - ); + if (node.type !== 'GenericTypeAnnotation') { + break; + } - node = resolvedTypeAnnotation.right; - } else { + const resolvedTypeAnnotation = types[node.id.name]; + if (resolvedTypeAnnotation == null) { break; } + + switch (resolvedTypeAnnotation.type) { + case 'TypeAlias': { + typeResolutionStatus = { + successful: true, + type: 'alias', + name: node.id.name, + }; + node = resolvedTypeAnnotation.right; + break; + } + case 'EnumDeclaration': { + typeResolutionStatus = { + successful: true, + type: 'enum', + name: node.id.name, + }; + node = resolvedTypeAnnotation.body; + break; + } + default: { + throw new TypeError( + `A non GenericTypeAnnotation must be a type declaration ('TypeAlias') or enum ('EnumDeclaration'). Instead, got the unsupported ${resolvedTypeAnnotation.type}.`, + ); + } + } } return { nullable: nullable, typeAnnotation: node, - typeAliasResolutionStatus, + typeResolutionStatus, }; } diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index 9f73635e6eae65..f8b523432e4824 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -41,18 +41,6 @@ export interface Parser { * @throws if property does not contain a property declaration. */ getKeyName(property: $FlowFixMe, hasteModuleName: string): string; - /** - * Given a type declaration, it possibly returns the name of the Enum type. - * @parameter maybeEnumDeclaration: an object possibly containing an Enum declaration. - * @returns: the name of the Enum type. - */ - getMaybeEnumMemberType(maybeEnumDeclaration: $FlowFixMe): string; - /** - * Given a type declaration, it returns a boolean specifying if is an Enum declaration. - * @parameter maybeEnumDeclaration: an object possibly containing an Enum declaration. - * @returns: a boolean specifying if is an Enum declaration. - */ - isEnumDeclaration(maybeEnumDeclaration: $FlowFixMe): boolean; /** * @returns: the Parser language. */ diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index c3caff7aaacd79..03adcce1ffb2b5 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -61,16 +61,6 @@ export class MockedParser implements Parser { return property.key.name; } - getMaybeEnumMemberType(maybeEnumDeclaration: $FlowFixMe): string { - return maybeEnumDeclaration.body.type - .replace('EnumNumberBody', 'NumberTypeAnnotation') - .replace('EnumStringBody', 'StringTypeAnnotation'); - } - - isEnumDeclaration(maybeEnumDeclaration: $FlowFixMe): boolean { - return maybeEnumDeclaration.type === 'EnumDeclaration'; - } - language(): ParserType { return 'Flow'; } diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index 9f0310107b833a..2509d384da96e8 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -15,7 +15,6 @@ import type { NamedShape, NativeModuleAliasMap, NativeModuleBaseTypeAnnotation, - NativeModuleEnumDeclaration, NativeModuleSchema, NativeModuleTypeAnnotation, NativeModuleFunctionTypeAnnotation, @@ -45,12 +44,11 @@ const { const { MissingTypeParameterGenericParserError, MoreThanOneTypeParameterGenericParserError, - UnsupportedEnumDeclarationParserError, - UnsupportedGenericParserError, UnnamedFunctionParamParserError, } = require('./errors'); const invariant = require('invariant'); +import type {NativeModuleEnumMap} from '../CodegenSchema'; function wrapModuleSchema( nativeModuleSchema: NativeModuleSchema, @@ -135,6 +133,7 @@ function parseObjectProperty( hasteModuleName: string, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, nullable: boolean, @@ -157,6 +156,7 @@ function parseObjectProperty( languageTypeAnnotation, types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -183,43 +183,6 @@ function parseObjectProperty( }; } -function translateDefault( - hasteModuleName: string, - typeAnnotation: $FlowFixMe, - types: TypeDeclarationMap, - nullable: boolean, - parser: Parser, -): Nullable { - const maybeEnumDeclaration = - types[parser.nameForGenericTypeAnnotation(typeAnnotation)]; - - if (maybeEnumDeclaration && parser.isEnumDeclaration(maybeEnumDeclaration)) { - const memberType = parser.getMaybeEnumMemberType(maybeEnumDeclaration); - - if ( - memberType === 'NumberTypeAnnotation' || - memberType === 'StringTypeAnnotation' - ) { - return wrapNullable(nullable, { - type: 'EnumDeclaration', - memberType: memberType, - }); - } else { - throw new UnsupportedEnumDeclarationParserError( - hasteModuleName, - typeAnnotation, - memberType, - ); - } - } - - throw new UnsupportedGenericParserError( - hasteModuleName, - typeAnnotation, - parser, - ); -} - function translateFunctionTypeAnnotation( hasteModuleName: string, // TODO(T108222691): Use flow-types for @babel/parser @@ -227,6 +190,7 @@ function translateFunctionTypeAnnotation( functionTypeAnnotation: $FlowFixMe, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, translateTypeAnnotation: $FlowFixMe, @@ -252,6 +216,7 @@ function translateFunctionTypeAnnotation( parser.getParameterTypeAnnotation(param), types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -292,6 +257,7 @@ function translateFunctionTypeAnnotation( parser.getFunctionTypeAnnotationReturnType(functionTypeAnnotation), types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -326,6 +292,7 @@ function buildPropertySchema( property: $FlowFixMe, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, resolveTypeAnnotation: $FlowFixMe, @@ -363,6 +330,7 @@ function buildPropertySchema( value, types, aliasMap, + enumMap, tryParse, cxxOnly, translateTypeAnnotation, @@ -471,7 +439,6 @@ module.exports = { assertGenericTypeAnnotationHasExactlyOneTypeParameter, isObjectProperty, parseObjectProperty, - translateDefault, translateFunctionTypeAnnotation, buildPropertySchema, buildSchemaFromConfigType, diff --git a/packages/react-native-codegen/src/parsers/parsers-primitives.js b/packages/react-native-codegen/src/parsers/parsers-primitives.js index 23a7495b1654af..17e17922568a4f 100644 --- a/packages/react-native-codegen/src/parsers/parsers-primitives.js +++ b/packages/react-native-codegen/src/parsers/parsers-primitives.js @@ -16,6 +16,7 @@ import type { DoubleTypeAnnotation, Int32TypeAnnotation, NativeModuleAliasMap, + NativeModuleEnumMap, NativeModuleBaseTypeAnnotation, NativeModuleTypeAnnotation, NativeModuleFloatTypeAnnotation, @@ -31,11 +32,14 @@ import type { StringTypeAnnotation, VoidTypeAnnotation, NativeModuleObjectTypeAnnotation, + NativeModuleMemberType, + NativeModuleEnumMembers, + NativeModuleEnumDeclaration, } from '../CodegenSchema'; import type {Parser} from './parser'; import type { ParserErrorCapturer, - TypeAliasResolutionStatus, + TypeResolutionStatus, TypeDeclarationMap, } from './utils'; @@ -102,6 +106,7 @@ function emitFunction( typeAnnotation: $FlowFixMe, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, translateTypeAnnotation: $FlowFixMe, @@ -113,6 +118,7 @@ function emitFunction( typeAnnotation, types, aliasMap, + enumMap, tryParse, cxxOnly, translateTypeAnnotation, @@ -136,7 +142,7 @@ function emitString(nullable: boolean): Nullable { } function typeAliasResolution( - typeAliasResolutionStatus: TypeAliasResolutionStatus, + typeResolution: TypeResolutionStatus, objectTypeAnnotation: ObjectTypeAnnotation< Nullable, >, @@ -145,14 +151,14 @@ function typeAliasResolution( ): | Nullable | Nullable>> { - if (!typeAliasResolutionStatus.successful) { + if (!typeResolution.successful) { return wrapNullable(nullable, objectTypeAnnotation); } /** * All aliases RHS are required. */ - aliasMap[typeAliasResolutionStatus.aliasName] = objectTypeAnnotation; + aliasMap[typeResolution.name] = objectTypeAnnotation; /** * Nullability of type aliases is transitive. @@ -185,7 +191,28 @@ function typeAliasResolution( */ return wrapNullable(nullable, { type: 'TypeAliasTypeAnnotation', - name: typeAliasResolutionStatus.aliasName, + name: typeResolution.name, + }); +} + +function typeEnumResolution( + nullable: boolean, + name: string, + memberType: NativeModuleMemberType, + members: NativeModuleEnumMembers, + enumMap: {...NativeModuleEnumMap}, +): Nullable { + enumMap[name] = { + type: 'EnumDeclarationWithMembers', + name, + memberType, + members, + }; + + return wrapNullable(nullable, { + name: name, + type: 'EnumDeclaration', + memberType, }); } @@ -196,6 +223,7 @@ function emitPromise( nullable: boolean, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, translateTypeAnnotation: $FlowFixMe, @@ -223,6 +251,7 @@ function emitPromise( typeAnnotation.typeParameters.params[0], types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -291,6 +320,7 @@ function translateArrayTypeAnnotation( hasteModuleName: string, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, cxxOnly: boolean, arrayType: 'Array' | 'ReadonlyArray', elementType: $FlowFixMe, @@ -310,6 +340,7 @@ function translateArrayTypeAnnotation( elementType, types, aliasMap, + enumMap, /** * TODO(T72031674): Ensure that all ParsingErrors that are thrown * while parsing the array element don't get captured and collected. @@ -349,6 +380,7 @@ function emitArrayType( parser: Parser, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, cxxOnly: boolean, nullable: boolean, translateTypeAnnotation: $FlowFixMe, @@ -363,6 +395,7 @@ function emitArrayType( hasteModuleName, types, aliasMap, + enumMap, cxxOnly, typeAnnotation.type, typeAnnotation.typeParameters.params[0], @@ -390,5 +423,6 @@ module.exports = { emitMixed, emitUnion, typeAliasResolution, + typeEnumResolution, translateArrayTypeAnnotation, }; diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/failures.js b/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/failures.js index f25e8b3d10347a..a8d08e8395ff9a 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/failures.js @@ -156,6 +156,58 @@ export interface Spec2 extends TurboModule { } `; +const EMPTY_ENUM_NATIVE_MODULE = ` +/** + * 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. + * + * @format + */ + +import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; +import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; + +export enum SomeEnum { +} + +export interface Spec extends TurboModule { + readonly getEnums: (a: SomeEnum) => string; +} + +export default TurboModuleRegistry.getEnforcing( + 'EmptyEnumNativeModule', +); +`; + +const MIXED_VALUES_ENUM_NATIVE_MODULE = ` +/** + * 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. + * + * @format + */ + +import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; +import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; + +export enum SomeEnum { + NUM = 1, + STR = 'str', +} + +export interface Spec extends TurboModule { + readonly getEnums: (a: SomeEnum) => string; +} + +export default TurboModuleRegistry.getEnforcing( + 'MixedValuesEnumNativeModule', +); +`; + module.exports = { NATIVE_MODULES_WITH_UNNAMED_PARAMS, NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE, @@ -164,4 +216,6 @@ module.exports = { TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT, NATIVE_MODULES_WITH_NOT_ONLY_METHODS, TWO_NATIVE_EXTENDING_TURBO_MODULE, + EMPTY_ENUM_NATIVE_MODULE, + MIXED_VALUES_ENUM_NATIVE_MODULE, }; diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap b/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap index 60f9fbaba5a0b0..9fb7409bde1129 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap +++ b/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap @@ -1,5 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`RN Codegen TypeScript Parser Fails with error message EMPTY_ENUM_NATIVE_MODULE 1`] = `"Enums should have at least one member."`; + +exports[`RN Codegen TypeScript Parser Fails with error message MIXED_VALUES_ENUM_NATIVE_MODULE 1`] = `"Enum values can not be mixed. They all must be either blank, number, or string values."`; + exports[`RN Codegen TypeScript Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT 1`] = `"Module NativeSampleTurboModule: Generic 'Array' must have type parameters."`; exports[`RN Codegen TypeScript Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM 1`] = `"Module NativeSampleTurboModule: Generic 'Array' must have type parameters."`; @@ -20,6 +24,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture ANDROID_ONLY_NATIVE_M 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [] }, @@ -38,6 +43,72 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': { + 'Quality': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Quality', + 'memberType': 'StringTypeAnnotation', + 'members': [ + { + 'name': 'SD', + 'value': 'SD' + }, + { + 'name': 'HD', + 'value': 'HD' + } + ] + }, + 'Resolution': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Resolution', + 'memberType': 'NumberTypeAnnotation', + 'members': [ + { + 'name': 'Low', + 'value': 720 + }, + { + 'name': 'High', + 'value': 1080 + } + ] + }, + 'Floppy': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Floppy', + 'memberType': 'NumberTypeAnnotation', + 'members': [ + { + 'name': 'LowDensity', + 'value': 0.72 + }, + { + 'name': 'HighDensity', + 'value': 1.44 + } + ] + }, + 'StringOptions': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'StringOptions', + 'memberType': 'StringTypeAnnotation', + 'members': [ + { + 'name': 'One', + 'value': 'one' + }, + { + 'name': 'Two', + 'value': 'two' + }, + { + 'name': 'Three', + 'value': 'three' + } + ] + } + }, 'spec': { 'properties': [ { @@ -87,6 +158,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL 'name': 'quality', 'optional': false, 'typeAnnotation': { + 'name': 'Quality', 'type': 'EnumDeclaration', 'memberType': 'StringTypeAnnotation' } @@ -95,6 +167,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL 'name': 'resolution', 'optional': true, 'typeAnnotation': { + 'name': 'Resolution', 'type': 'EnumDeclaration', 'memberType': 'NumberTypeAnnotation' } @@ -103,6 +176,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL 'name': 'floppy', 'optional': false, 'typeAnnotation': { + 'name': 'Floppy', 'type': 'EnumDeclaration', 'memberType': 'NumberTypeAnnotation' } @@ -111,6 +185,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL 'name': 'stringOptions', 'optional': false, 'typeAnnotation': { + 'name': 'StringOptions', 'type': 'EnumDeclaration', 'memberType': 'StringTypeAnnotation' } @@ -219,6 +294,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture EMPTY_NATIVE_MODULE 1 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [] }, @@ -234,6 +310,72 @@ exports[`RN Codegen TypeScript Parser can generate fixture IOS_ONLY_NATIVE_MODUL 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': { + 'Quality': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Quality', + 'memberType': 'StringTypeAnnotation', + 'members': [ + { + 'name': 'SD', + 'value': 'SD' + }, + { + 'name': 'HD', + 'value': 'HD' + } + ] + }, + 'Resolution': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Resolution', + 'memberType': 'NumberTypeAnnotation', + 'members': [ + { + 'name': 'Low', + 'value': 720 + }, + { + 'name': 'High', + 'value': 1080 + } + ] + }, + 'Floppy': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'Floppy', + 'memberType': 'NumberTypeAnnotation', + 'members': [ + { + 'name': 'LowDensity', + 'value': 0.72 + }, + { + 'name': 'HighDensity', + 'value': 1.44 + } + ] + }, + 'StringOptions': { + 'type': 'EnumDeclarationWithMembers', + 'name': 'StringOptions', + 'memberType': 'StringTypeAnnotation', + 'members': [ + { + 'name': 'One', + 'value': 'one' + }, + { + 'name': 'Two', + 'value': 'two' + }, + { + 'name': 'Three', + 'value': 'three' + } + ] + } + }, 'spec': { 'properties': [ { @@ -249,6 +391,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture IOS_ONLY_NATIVE_MODUL 'name': 'quality', 'optional': false, 'typeAnnotation': { + 'name': 'Quality', 'type': 'EnumDeclaration', 'memberType': 'StringTypeAnnotation' } @@ -257,6 +400,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture IOS_ONLY_NATIVE_MODUL 'name': 'resolution', 'optional': true, 'typeAnnotation': { + 'name': 'Resolution', 'type': 'EnumDeclaration', 'memberType': 'NumberTypeAnnotation' } @@ -265,6 +409,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture IOS_ONLY_NATIVE_MODUL 'name': 'floppy', 'optional': false, 'typeAnnotation': { + 'name': 'Floppy', 'type': 'EnumDeclaration', 'memberType': 'NumberTypeAnnotation' } @@ -273,6 +418,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture IOS_ONLY_NATIVE_MODUL 'name': 'stringOptions', 'optional': false, 'typeAnnotation': { + 'name': 'StringOptions', 'type': 'EnumDeclaration', 'memberType': 'StringTypeAnnotation' } @@ -331,6 +477,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AL ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -494,6 +641,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AR 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -535,6 +683,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AR 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -570,6 +719,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AR 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -611,6 +761,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AR 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -646,6 +797,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_BA 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -712,6 +864,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_BA 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -778,6 +931,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_BA 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -870,6 +1024,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_CA 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -930,6 +1085,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_CO 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -989,6 +1145,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_CO 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1048,6 +1205,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_CO 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1260,6 +1418,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_CO 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1364,6 +1523,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_FL 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1452,6 +1612,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_NE ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1613,6 +1774,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_NE ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1670,6 +1832,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_NU 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1721,6 +1884,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_OB ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1817,6 +1981,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_PA ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -1928,6 +2093,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_PA ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -2016,6 +2182,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_PR ] } }, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -2075,6 +2242,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_RO 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -2112,6 +2280,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_SI 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -2147,6 +2316,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_UN 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { @@ -2208,6 +2378,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_UN 'NativeSampleTurboModule': { 'type': 'NativeModule', 'aliasMap': {}, + 'enumMap': {}, 'spec': { 'properties': [ { diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/index.js b/packages/react-native-codegen/src/parsers/typescript/modules/index.js index 0d9c431ef2a22a..3059a2c0463e98 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -13,11 +13,13 @@ import type { NamedShape, NativeModuleAliasMap, + NativeModuleEnumMap, NativeModuleBaseTypeAnnotation, NativeModulePropertyShape, NativeModuleTypeAnnotation, NativeModuleSchema, Nullable, + NativeModuleMemberType, } from '../../../CodegenSchema'; import type {Parser} from '../../parser'; @@ -29,9 +31,9 @@ const {resolveTypeAnnotation, getTypes} = require('../utils'); const { parseObjectProperty, - translateDefault, buildPropertySchema, } = require('../../parsers-commons'); +const {typeEnumResolution} = require('../../parsers-primitives'); const { emitArrayType, @@ -81,11 +83,12 @@ function translateTypeAnnotation( typeScriptTypeAnnotation: $FlowFixMe, types: TypeDeclarationMap, aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, parser: Parser, ): Nullable { - const {nullable, typeAnnotation, typeAliasResolutionStatus} = + const {nullable, typeAnnotation, typeResolutionStatus} = resolveTypeAnnotation(typeScriptTypeAnnotation, types); switch (typeAnnotation.type) { @@ -94,6 +97,7 @@ function translateTypeAnnotation( hasteModuleName, types, aliasMap, + enumMap, cxxOnly, 'Array', typeAnnotation.elementType, @@ -111,6 +115,7 @@ function translateTypeAnnotation( hasteModuleName, types, aliasMap, + enumMap, cxxOnly, 'ReadonlyArray', typeAnnotation.typeAnnotation.elementType, @@ -139,6 +144,7 @@ function translateTypeAnnotation( nullable, types, aliasMap, + enumMap, tryParse, cxxOnly, translateTypeAnnotation, @@ -152,6 +158,7 @@ function translateTypeAnnotation( parser, types, aliasMap, + enumMap, cxxOnly, nullable, translateTypeAnnotation, @@ -199,6 +206,7 @@ function translateTypeAnnotation( member.typeAnnotation.typeAnnotation, types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -210,11 +218,9 @@ function translateTypeAnnotation( return emitObject(nullable, properties); } default: { - return translateDefault( + throw new UnsupportedGenericParserError( hasteModuleName, typeAnnotation, - types, - nullable, parser, ); } @@ -234,6 +240,7 @@ function translateTypeAnnotation( }, types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -255,6 +262,7 @@ function translateTypeAnnotation( hasteModuleName, types, aliasMap, + enumMap, tryParse, cxxOnly, nullable, @@ -275,7 +283,7 @@ function translateTypeAnnotation( } return typeAliasResolution( - typeAliasResolutionStatus, + typeResolutionStatus, objectTypeAnnotation, aliasMap, nullable, @@ -296,6 +304,7 @@ function translateTypeAnnotation( propertyType, types, aliasMap, + enumMap, tryParse, cxxOnly, parser, @@ -317,6 +326,7 @@ function translateTypeAnnotation( hasteModuleName, types, aliasMap, + enumMap, tryParse, cxxOnly, nullable, @@ -330,12 +340,59 @@ function translateTypeAnnotation( }; return typeAliasResolution( - typeAliasResolutionStatus, + typeResolutionStatus, objectTypeAnnotation, aliasMap, nullable, ); } + case 'TSEnumDeclaration': { + if (!typeResolutionStatus.successful) { + throw new UnsupportedTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + language, + ); + } + + if (!typeAnnotation.members || typeAnnotation.members.length === 0) { + throw new Error('Enums should have at least one member.'); + } + + const enumInitializer = typeAnnotation.members[0].initializer; + typeAnnotation.members.forEach(member => { + if (member.initializer?.type !== enumInitializer?.type) { + throw new Error( + 'Enum values can not be mixed. They all must be either blank, number, or string values.', + ); + } + }); + + const enumType: ?NativeModuleMemberType = + !enumInitializer || enumInitializer.type === 'StringLiteral' + ? 'StringTypeAnnotation' + : enumInitializer.type === 'NumericLiteral' + ? 'NumberTypeAnnotation' + : null; + if (!enumType) { + throw new Error( + 'Enum values must be either blank, number, or string values.', + ); + } + + const members = typeAnnotation.members.map(member => ({ + name: member.id.name, + value: member.initializer?.value ?? member.id.name, + })); + + return typeEnumResolution( + nullable, + typeResolutionStatus.name, + enumType, + members, + enumMap, + ); + } case 'TSBooleanKeyword': { return emitBoolean(nullable); } @@ -355,6 +412,7 @@ function translateTypeAnnotation( typeAnnotation, types, aliasMap, + enumMap, tryParse, cxxOnly, translateTypeAnnotation, @@ -503,17 +561,21 @@ function buildModuleSchema( ) .map(property => { const aliasMap: {...NativeModuleAliasMap} = {}; + const enumMap: {...NativeModuleEnumMap} = {}; return tryParse(() => ({ aliasMap: aliasMap, + enumMap: enumMap, propertyShape: buildPropertySchema( hasteModuleName, property, types, aliasMap, + enumMap, tryParse, cxxOnly, resolveTypeAnnotation, @@ -524,10 +586,14 @@ function buildModuleSchema( }) .filter(Boolean) .reduce( - (moduleSchema: NativeModuleSchema, {aliasMap, propertyShape}) => { + ( + moduleSchema: NativeModuleSchema, + {aliasMap, enumMap, propertyShape}, + ) => { return { type: 'NativeModule', aliasMap: {...moduleSchema.aliasMap, ...aliasMap}, + enumMap: {...moduleSchema.enumMap, ...enumMap}, spec: { properties: [...moduleSchema.spec.properties, propertyShape], }, @@ -538,6 +604,7 @@ function buildModuleSchema( { type: 'NativeModule', aliasMap: {}, + enumMap: {}, spec: {properties: []}, moduleName: moduleName, excludedPlatforms: diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index 40b4af14d8ed95..b325b261540f21 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -54,20 +54,6 @@ class TypeScriptParser implements Parser { return property.key.name; } - getMaybeEnumMemberType(maybeEnumDeclaration: $FlowFixMe): string { - if (maybeEnumDeclaration.members[0].initializer) { - return maybeEnumDeclaration.members[0].initializer.type - .replace('NumericLiteral', 'NumberTypeAnnotation') - .replace('StringLiteral', 'StringTypeAnnotation'); - } - - return 'StringTypeAnnotation'; - } - - isEnumDeclaration(maybeEnumDeclaration: $FlowFixMe): boolean { - return maybeEnumDeclaration.type === 'TSEnumDeclaration'; - } - language(): ParserType { return 'TypeScript'; } diff --git a/packages/react-native-codegen/src/parsers/typescript/utils.js b/packages/react-native-codegen/src/parsers/typescript/utils.js index 725b74968ae6cc..ae72b030f640c7 100644 --- a/packages/react-native-codegen/src/parsers/typescript/utils.js +++ b/packages/react-native-codegen/src/parsers/typescript/utils.js @@ -10,7 +10,7 @@ 'use strict'; -import type {TypeAliasResolutionStatus, TypeDeclarationMap} from '../utils'; +import type {TypeResolutionStatus, TypeDeclarationMap} from '../utils'; const {parseTopLevelType} = require('./parseTopLevelType'); @@ -56,7 +56,7 @@ function resolveTypeAnnotation( ): { nullable: boolean, typeAnnotation: $FlowFixMe, - typeAliasResolutionStatus: TypeAliasResolutionStatus, + typeResolutionStatus: TypeResolutionStatus, } { invariant( typeAnnotation != null, @@ -68,7 +68,7 @@ function resolveTypeAnnotation( ? typeAnnotation.typeAnnotation : typeAnnotation; let nullable = false; - let typeAliasResolutionStatus: TypeAliasResolutionStatus = { + let typeResolutionStatus: TypeResolutionStatus = { successful: false, }; @@ -77,40 +77,55 @@ function resolveTypeAnnotation( nullable = nullable || topLevelType.optional; node = topLevelType.type; - if (node.type === 'TSTypeReference') { - typeAliasResolutionStatus = { - successful: true, - aliasName: node.typeName.name, - }; - const resolvedTypeAnnotation = types[node.typeName.name]; - if ( - resolvedTypeAnnotation == null || - resolvedTypeAnnotation.type === 'TSEnumDeclaration' - ) { + if (node.type !== 'TSTypeReference') { + break; + } + + const resolvedTypeAnnotation = types[node.typeName.name]; + if (resolvedTypeAnnotation == null) { + break; + } + + switch (resolvedTypeAnnotation.type) { + case 'TSTypeAliasDeclaration': { + typeResolutionStatus = { + successful: true, + type: 'alias', + name: node.typeName.name, + }; + node = resolvedTypeAnnotation.typeAnnotation; break; } - - switch (resolvedTypeAnnotation.type) { - case 'TSTypeAliasDeclaration': - node = resolvedTypeAnnotation.typeAnnotation; - break; - case 'TSInterfaceDeclaration': - node = resolvedTypeAnnotation; - break; - default: - throw new Error( - `GenericTypeAnnotation '${node.typeName.name}' must resolve to a TSTypeAliasDeclaration or a TSInterfaceDeclaration. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`, - ); + case 'TSInterfaceDeclaration': { + typeResolutionStatus = { + successful: true, + type: 'alias', + name: node.typeName.name, + }; + node = resolvedTypeAnnotation; + break; + } + case 'TSEnumDeclaration': { + typeResolutionStatus = { + successful: true, + type: 'enum', + name: node.typeName.name, + }; + node = resolvedTypeAnnotation; + break; + } + default: { + throw new TypeError( + `A non GenericTypeAnnotation must be a type declaration ('TSTypeAliasDeclaration'), an interface ('TSInterfaceDeclaration'), or enum ('TSEnumDeclaration'). Instead, got the unsupported ${resolvedTypeAnnotation.type}.`, + ); } - } else { - break; } } return { nullable: nullable, typeAnnotation: node, - typeAliasResolutionStatus, + typeResolutionStatus, }; } diff --git a/packages/react-native-codegen/src/parsers/utils.js b/packages/react-native-codegen/src/parsers/utils.js index f3ad6483e06eaa..299bddef91d143 100644 --- a/packages/react-native-codegen/src/parsers/utils.js +++ b/packages/react-native-codegen/src/parsers/utils.js @@ -16,10 +16,11 @@ const path = require('path'); export type TypeDeclarationMap = {[declarationName: string]: $FlowFixMe}; -export type TypeAliasResolutionStatus = +export type TypeResolutionStatus = | $ReadOnly<{ + type: 'alias' | 'enum', successful: true, - aliasName: string, + name: string, }> | $ReadOnly<{ successful: false,