diff --git a/packages/react-native-codegen/src/CodegenSchema.d.ts b/packages/react-native-codegen/src/CodegenSchema.d.ts index 2d28bbfe7e271c..3c1ba4b9f4bb1d 100644 --- a/packages/react-native-codegen/src/CodegenSchema.d.ts +++ b/packages/react-native-codegen/src/CodegenSchema.d.ts @@ -26,6 +26,10 @@ export interface FloatTypeAnnotation { readonly type: 'FloatTypeAnnotation'; } +export interface NumberTypeAnnotation { + readonly type: 'NumberTypeAnnotation'; +} + export interface BooleanTypeAnnotation { readonly type: 'BooleanTypeAnnotation'; } @@ -42,6 +46,21 @@ export interface VoidTypeAnnotation { readonly type: 'VoidTypeAnnotation'; } +export interface NumberLiteralTypeAnnotation { + readonly type: 'NumberLiteralTypeAnnotation'; + readonly value: number; +} + +export interface StringLiteralTypeAnnotation { + readonly type: 'StringLiteralTypeAnnotation'; + readonly value: string; +} + +export interface BooleanLiteralTypeAnnotation { + readonly type: 'BooleanLiteralTypeAnnotation'; + readonly value: boolean; +} + export interface ObjectTypeAnnotation { readonly type: 'ObjectTypeAnnotation'; readonly properties: readonly NamedShape[]; @@ -49,6 +68,18 @@ export interface ObjectTypeAnnotation { readonly baseTypes?: readonly string[] | undefined; } +export interface UnionTypeAnnotation { + readonly type: 'UnionTypeAnnotation'; + readonly types: readonly T[]; +} + +// TODO(T72031674): TupleTypeAnnotation is added for parity with UnionTypeAnnotation +// to support future implementation. Currently limited to String and Number literals. +export interface TupleTypeAnnotation { + readonly type: 'TupleTypeAnnotation'; + readonly types: StringLiteralTypeAnnotation | NumberLiteralTypeAnnotation; +} + export interface MixedTypeAnnotation { readonly type: 'MixedTypeAnnotation'; } @@ -304,10 +335,14 @@ export interface NativeModuleStringLiteralTypeAnnotation { readonly value: string; } -export interface StringLiteralUnionTypeAnnotation { - readonly type: 'StringLiteralUnionTypeAnnotation'; - readonly types: NativeModuleStringLiteralTypeAnnotation[]; -} +export type StringLiteralUnionTypeAnnotation = + UnionTypeAnnotation; + +export type NumberLiteralUnionTypeAnnotation = + UnionTypeAnnotation; + +export type BooleanLiteralUnionTypeAnnotation = + UnionTypeAnnotation; export interface NativeModuleNumberTypeAnnotation { readonly type: 'NumberTypeAnnotation'; @@ -369,15 +404,17 @@ export interface NativeModulePromiseTypeAnnotation { readonly elementType: Nullable | VoidTypeAnnotation; } -export type UnionTypeAnnotationMemberType = - | 'NumberTypeAnnotation' - | 'ObjectTypeAnnotation' - | 'StringTypeAnnotation'; +export type NativeModuleUnionTypeAnnotationMemberType = + | NativeModuleObjectTypeAnnotation + | StringLiteralTypeAnnotation + | NumberLiteralTypeAnnotation + | BooleanLiteralTypeAnnotation + | BooleanTypeAnnotation + | StringTypeAnnotation + | NumberTypeAnnotation; -export interface NativeModuleUnionTypeAnnotation { - readonly type: 'UnionTypeAnnotation'; - readonly memberType: UnionTypeAnnotationMemberType; -} +export type NativeModuleUnionTypeAnnotation = + UnionTypeAnnotation; export interface NativeModuleMixedTypeAnnotation { readonly type: 'MixedTypeAnnotation'; diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index 99299dafc3c043..d94e410ee5f681 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -30,6 +30,10 @@ export type FloatTypeAnnotation = $ReadOnly<{ type: 'FloatTypeAnnotation', }>; +export type NumberTypeAnnotation = $ReadOnly<{ + type: 'NumberTypeAnnotation', +}>; + export type BooleanTypeAnnotation = $ReadOnly<{ type: 'BooleanTypeAnnotation', }>; @@ -52,11 +56,20 @@ export type StringLiteralTypeAnnotation = $ReadOnly<{ value: string, }>; -export type StringLiteralUnionTypeAnnotation = $ReadOnly<{ - type: 'StringLiteralUnionTypeAnnotation', - types: $ReadOnlyArray, +export type BooleanLiteralTypeAnnotation = $ReadOnly<{ + type: 'BooleanLiteralTypeAnnotation', + value: boolean, }>; +export type StringLiteralUnionTypeAnnotation = + UnionTypeAnnotation; + +export type NumberLiteralUnionTypeAnnotation = + UnionTypeAnnotation; + +export type BooleanLiteralUnionTypeAnnotation = + UnionTypeAnnotation; + export type VoidTypeAnnotation = $ReadOnly<{ type: 'VoidTypeAnnotation', }>; @@ -68,6 +81,18 @@ export type ObjectTypeAnnotation<+T> = $ReadOnly<{ baseTypes?: $ReadOnlyArray, }>; +export type UnionTypeAnnotation<+T> = $ReadOnly<{ + type: 'UnionTypeAnnotation', + types: $ReadOnlyArray, +}>; + +// TODO(T72031674): TupleTypeAnnotation is added for parity with UnionTypeAnnotation +// to support future implementation. Currently limited to String and Number literals. +export type TupleTypeAnnotation = $ReadOnly<{ + type: 'TupleTypeAnnotation', + types: StringLiteralTypeAnnotation | NumberLiteralTypeAnnotation, +}>; + export type MixedTypeAnnotation = $ReadOnly<{ type: 'MixedTypeAnnotation', }>; @@ -358,15 +383,17 @@ export type NativeModulePromiseTypeAnnotation = $ReadOnly<{ elementType: VoidTypeAnnotation | Nullable, }>; -export type UnionTypeAnnotationMemberType = - | 'NumberTypeAnnotation' - | 'ObjectTypeAnnotation' - | 'StringTypeAnnotation'; +export type NativeModuleUnionTypeAnnotationMemberType = + | NativeModuleObjectTypeAnnotation + | StringLiteralTypeAnnotation + | NumberLiteralTypeAnnotation + | BooleanLiteralTypeAnnotation + | BooleanTypeAnnotation + | StringTypeAnnotation + | NumberTypeAnnotation; -export type NativeModuleUnionTypeAnnotation = $ReadOnly<{ - type: 'UnionTypeAnnotation', - memberType: UnionTypeAnnotationMemberType, -}>; +export type NativeModuleUnionTypeAnnotation = + UnionTypeAnnotation; export type NativeModuleMixedTypeAnnotation = $ReadOnly<{ type: 'MixedTypeAnnotation', @@ -396,6 +423,7 @@ export type NativeModuleBaseTypeAnnotation = | StringLiteralUnionTypeAnnotation | NativeModuleNumberTypeAnnotation | NumberLiteralTypeAnnotation + | BooleanLiteralTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation diff --git a/packages/react-native-codegen/src/generators/Utils.js b/packages/react-native-codegen/src/generators/Utils.js index da692692daafac..63a9a9dabafdc4 100644 --- a/packages/react-native-codegen/src/generators/Utils.js +++ b/packages/react-native-codegen/src/generators/Utils.js @@ -10,6 +10,8 @@ 'use strict'; +import type {NativeModuleUnionTypeAnnotation} from '../CodegenSchema'; + function capitalize(string: string): string { return string.charAt(0).toUpperCase() + string.slice(1); } @@ -44,9 +46,58 @@ function getEnumName(moduleName: string, origEnumName: string): string { return `${moduleName}${uppercasedPropName}`; } +type ValidUnionType = 'boolean' | 'number' | 'object' | 'string'; +const NumberTypes = ['NumberTypeAnnotation', 'NumberLiteralTypeAnnotation']; +const StringTypes = ['StringTypeAnnotation', 'StringLiteralTypeAnnotation']; +const ObjectTypes = ['ObjectTypeAnnotation']; +const BooleanTypes = ['BooleanTypeAnnotation', 'BooleanLiteralTypeAnnotation']; +const ValidUnionTypes = [ + ...NumberTypes, + ...ObjectTypes, + ...StringTypes, + ...BooleanTypes, +]; + +function parseValidUnionType( + annotation: NativeModuleUnionTypeAnnotation, +): ValidUnionType { + const isUnionOfType = (types: $ReadOnlyArray): boolean => { + return annotation.types.every(memberTypeAnnotation => + types.includes(memberTypeAnnotation.type), + ); + }; + if (isUnionOfType(BooleanTypes)) { + return 'boolean'; + } + if (isUnionOfType(NumberTypes)) { + return 'number'; + } + if (isUnionOfType(ObjectTypes)) { + return 'object'; + } + if (isUnionOfType(StringTypes)) { + return 'string'; + } + + const invalidTypes = annotation.types.filter(member => { + return !ValidUnionTypes.includes(member.type); + }); + + // Check if union members are all supported but not homogeneous + // (e.g., mix of number and boolean) + if (invalidTypes.length === 0) { + throw new Error(`Non-homogenous union member types`); + } else { + throw new Error( + `Unsupported union member types: ${invalidTypes.join(', ')}"`, + ); + } +} + module.exports = { capitalize, indent, + parseValidUnionType, toPascalCase, toSafeCppString, getEnumName, diff --git a/packages/react-native-codegen/src/generators/components/CppHelpers.js b/packages/react-native-codegen/src/generators/components/CppHelpers.js index fa3d56629fec56..e4a1f5bdcc37be 100644 --- a/packages/react-native-codegen/src/generators/components/CppHelpers.js +++ b/packages/react-native-codegen/src/generators/components/CppHelpers.js @@ -12,11 +12,23 @@ import type { EventTypeAnnotation, NamedShape, + NativeModuleUnionTypeAnnotation, PropTypeAnnotation, } from '../../CodegenSchema'; const {getEnumName, toSafeCppString} = require('../Utils'); +const NumberTypes = ['NumberTypeAnnotation', 'NumberLiteralTypeAnnotation']; +const StringTypes = ['StringTypeAnnotation', 'StringLiteralTypeAnnotation']; +const ObjectTypes = ['ObjectTypeAnnotation']; +const BooleanTypes = ['BooleanTypeAnnotation', 'BooleanLiteralTypeAnnotation']; +const ValidUnionTypes = [ + ...NumberTypes, + ...ObjectTypes, + ...StringTypes, + ...BooleanTypes, +]; + function toIntEnumValueName(propName: string, value: number): string { return `${toSafeCppString(propName)}${value}`; } @@ -61,7 +73,54 @@ function getCppArrayTypeForAnnotation( case 'Int32TypeAnnotation': case 'MixedTypeAnnotation': return `std::vector<${getCppTypeForAnnotation(typeElement.type)}>`; - case 'StringLiteralUnionTypeAnnotation': + case 'UnionTypeAnnotation': + const union: NativeModuleUnionTypeAnnotation = typeElement; + const isUnionOfType = (types: $ReadOnlyArray): boolean => { + return union.types.every(memberTypeAnnotation => + types.includes(memberTypeAnnotation.type), + ); + }; + + if (isUnionOfType(NumberTypes)) { + return `std::vector<${getCppTypeForAnnotation('DoubleTypeAnnotation')}>`; + } + + if (isUnionOfType(ObjectTypes)) { + if (!structParts) { + throw new Error( + `Trying to generate the event emitter for an Array of ${typeElement.type} without informations to generate the generic type`, + ); + } + return `std::vector<${generateEventStructName(structParts)}>`; + } + + if (isUnionOfType(['StringTypeAnnotation'])) { + return `std::vector<${getCppTypeForAnnotation('StringTypeAnnotation')}>`; + } + if (isUnionOfType(['StringLiteralTypeAnnotation'])) { + if (!structParts) { + throw new Error( + `Trying to generate the event emitter for an Array of ${typeElement.type} without informations to generate the generic type`, + ); + } + return `std::vector<${generateEventStructName(structParts)}>`; + } + + if (isUnionOfType(BooleanTypes)) { + return `std::vector<${getCppTypeForAnnotation('BooleanTypeAnnotation')}>`; + } + + const invalidTypes = union.types.filter(member => { + return !ValidUnionTypes.includes(member.type); + }); + + if (invalidTypes.length === 0) { + throw new Error(`Non-homogenous union member types`); + } else { + throw new Error( + `Unsupported union member types: ${invalidTypes.join(', ')}`, + ); + } case 'ObjectTypeAnnotation': if (!structParts) { throw new Error( diff --git a/packages/react-native-codegen/src/generators/components/GenerateEventEmitterCpp.js b/packages/react-native-codegen/src/generators/components/GenerateEventEmitterCpp.js index 6d9b72cd2909ae..a890b238c675aa 100644 --- a/packages/react-native-codegen/src/generators/components/GenerateEventEmitterCpp.js +++ b/packages/react-native-codegen/src/generators/components/GenerateEventEmitterCpp.js @@ -207,7 +207,7 @@ function handleArrayElementType( loopLocalVariable, val => `jsi::valueFromDynamic(runtime, ${val})`, ); - case 'StringLiteralUnionTypeAnnotation': + case 'UnionTypeAnnotation': return setValueAtIndex( propertyName, indexVariable, @@ -320,7 +320,7 @@ function generateSetters( usingEvent, prop => `jsi::valueFromDynamic(runtime, ${prop})`, ); - case 'StringLiteralUnionTypeAnnotation': + case 'UnionTypeAnnotation': return generateSetter( parentPropertyName, eventProperty.name, diff --git a/packages/react-native-codegen/src/generators/components/GenerateEventEmitterH.js b/packages/react-native-codegen/src/generators/components/GenerateEventEmitterH.js index 0c3468dbc61c81..0546b13f6a8f52 100644 --- a/packages/react-native-codegen/src/generators/components/GenerateEventEmitterH.js +++ b/packages/react-native-codegen/src/generators/components/GenerateEventEmitterH.js @@ -128,7 +128,7 @@ function getNativeTypeFromAnnotation( case 'FloatTypeAnnotation': case 'MixedTypeAnnotation': return getCppTypeForAnnotation(type); - case 'StringLiteralUnionTypeAnnotation': + case 'UnionTypeAnnotation': case 'ObjectTypeAnnotation': return generateEventStructName([...nameParts, eventProperty.name]); case 'ArrayTypeAnnotation': @@ -188,7 +188,7 @@ function handleGenerateStructForArray( nameParts.concat([name]), nullthrows(elementType.properties), ); - } else if (elementType.type === 'StringLiteralUnionTypeAnnotation') { + } else if (elementType.type === 'UnionTypeAnnotation') { generateEnum( structs, elementType.types.map(literal => literal.value), @@ -251,7 +251,7 @@ function generateStruct( nullthrows(typeAnnotation.properties), ); return; - case 'StringLiteralUnionTypeAnnotation': + case 'UnionTypeAnnotation': generateEnum( structs, typeAnnotation.types.map(literal => literal.value), diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js index 2779f16572e10d..9c85b82a59cb85 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js @@ -29,7 +29,12 @@ import type {AliasResolver} from './Utils'; const {unwrapNullable} = require('../../parsers/parsers-commons'); const {wrapOptional} = require('../TypeUtils/Cxx'); -const {getEnumName, toPascalCase, toSafeCppString} = require('../Utils'); +const { + getEnumName, + parseValidUnionType, + toPascalCase, + toSafeCppString, +} = require('../Utils'); const { createAliasResolver, getModules, @@ -92,10 +97,10 @@ function serializeArg( return wrap(val => `${val}.asString(rt)`); case 'StringLiteralTypeAnnotation': return wrap(val => `${val}.asString(rt)`); - case 'StringLiteralUnionTypeAnnotation': - return wrap(val => `${val}.asString(rt)`); case 'BooleanTypeAnnotation': return wrap(val => `${val}.asBool()`); + case 'BooleanLiteralTypeAnnotation': + return wrap(val => `${val}.asBool()`); case 'EnumDeclaration': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -124,17 +129,19 @@ function serializeArg( case 'GenericObjectTypeAnnotation': return wrap(val => `${val}.asObject(rt)`); case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(realTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return wrap(val => `${val}.asBool()`); + case 'number': return wrap(val => `${val}.asNumber()`); - case 'ObjectTypeAnnotation': + case 'object': return wrap(val => `${val}.asObject(rt)`); - case 'StringTypeAnnotation': + case 'string': return wrap(val => `${val}.asString(rt)`); default: - throw new Error( - `Unsupported union member type for param "${arg.name}, found: ${realTypeAnnotation.memberType}"`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'ObjectTypeAnnotation': return wrap(val => `${val}.asObject(rt)`); @@ -251,8 +258,6 @@ function translatePrimitiveJSTypeToCpp( return wrapOptional('jsi::String', isRequired); case 'StringLiteralTypeAnnotation': return wrapOptional('jsi::String', isRequired); - case 'StringLiteralUnionTypeAnnotation': - return wrapOptional('jsi::String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); case 'NumberLiteralTypeAnnotation': @@ -265,6 +270,8 @@ function translatePrimitiveJSTypeToCpp( return wrapOptional('int', isRequired); case 'BooleanTypeAnnotation': return wrapOptional('bool', isRequired); + case 'BooleanLiteralTypeAnnotation': + return wrapOptional('bool', isRequired); case 'EnumDeclaration': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -277,15 +284,19 @@ function translatePrimitiveJSTypeToCpp( case 'GenericObjectTypeAnnotation': return wrapOptional('jsi::Object', isRequired); case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(realTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return wrapOptional('bool', isRequired); + case 'number': return wrapOptional('double', isRequired); - case 'ObjectTypeAnnotation': + case 'object': return wrapOptional('jsi::Object', isRequired); - case 'StringTypeAnnotation': + case 'string': return wrapOptional('jsi::String', isRequired); default: - throw new Error(createErrorMessage(realTypeAnnotation.type)); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'ObjectTypeAnnotation': return wrapOptional('jsi::Object', isRequired); diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js index e634125797cae6..ee55413d361615 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js @@ -24,7 +24,7 @@ import type {AliasResolver} from './Utils'; const {unwrapNullable} = require('../../parsers/parsers-commons'); const {wrapOptional} = require('../TypeUtils/Java'); -const {toPascalCase} = require('../Utils'); +const {parseValidUnionType, toPascalCase} = require('../Utils'); const {createAliasResolver, getModules} = require('./Utils'); type FilesOutput = Map; @@ -127,14 +127,28 @@ function translateEventEmitterTypeToJavaType( eventEmitter: NativeModuleEventEmitterShape, imports: Set, ): string { - const type = eventEmitter.typeAnnotation.typeAnnotation.type; - switch (type) { + const typeAnnotation = eventEmitter.typeAnnotation.typeAnnotation; + switch (typeAnnotation.type) { case 'StringTypeAnnotation': return 'String'; case 'StringLiteralTypeAnnotation': return 'String'; - case 'StringLiteralUnionTypeAnnotation': - return 'String'; + case 'UnionTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': + return 'boolean'; + case 'number': + return 'double'; + case 'object': + imports.add('com.facebook.react.bridge.ReadableMap'); + return 'ReadableMap'; + case 'string': + return 'String'; + default: + (validUnionType: empty); + throw new Error(`Unsupported union member type`); + } case 'NumberTypeAnnotation': case 'NumberLiteralTypeAnnotation': case 'FloatTypeAnnotation': @@ -160,7 +174,7 @@ function translateEventEmitterTypeToJavaType( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); default: - (type: empty); + (typeAnnotation.type: empty); throw new Error( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); @@ -200,8 +214,6 @@ function translateFunctionParamToJavaType( return wrapOptional('String', isRequired); case 'StringLiteralTypeAnnotation': return wrapOptional('String', isRequired); - case 'StringLiteralUnionTypeAnnotation': - return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); case 'NumberLiteralTypeAnnotation': @@ -214,6 +226,8 @@ function translateFunctionParamToJavaType( return wrapOptional('double', isRequired); case 'BooleanTypeAnnotation': return wrapOptional('boolean', isRequired); + case 'BooleanLiteralTypeAnnotation': + return wrapOptional('boolean', isRequired); case 'EnumDeclaration': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -224,18 +238,20 @@ function translateFunctionParamToJavaType( throw new Error(createErrorMessage(realTypeAnnotation.type)); } case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(realTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return wrapOptional('boolean', isRequired); + case 'number': return wrapOptional('double', isRequired); - case 'ObjectTypeAnnotation': + case 'object': imports.add('com.facebook.react.bridge.ReadableMap'); return wrapOptional('ReadableMap', isRequired); - case 'StringTypeAnnotation': + case 'string': return wrapOptional('String', isRequired); default: - throw new Error( - `Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'ObjectTypeAnnotation': imports.add('com.facebook.react.bridge.ReadableMap'); @@ -296,8 +312,6 @@ function translateFunctionReturnTypeToJavaType( return wrapOptional('String', isRequired); case 'StringLiteralTypeAnnotation': return wrapOptional('String', isRequired); - case 'StringLiteralUnionTypeAnnotation': - return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); case 'NumberLiteralTypeAnnotation': @@ -310,6 +324,8 @@ function translateFunctionReturnTypeToJavaType( return wrapOptional('double', isRequired); case 'BooleanTypeAnnotation': return wrapOptional('boolean', isRequired); + case 'BooleanLiteralTypeAnnotation': + return wrapOptional('boolean', isRequired); case 'EnumDeclaration': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -320,18 +336,20 @@ function translateFunctionReturnTypeToJavaType( throw new Error(createErrorMessage(realTypeAnnotation.type)); } case 'UnionTypeAnnotation': - switch (realTypeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(realTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return wrapOptional('boolean', isRequired); + case 'number': return wrapOptional('double', isRequired); - case 'ObjectTypeAnnotation': + case 'object': imports.add('com.facebook.react.bridge.WritableMap'); - return wrapOptional('WritableMap', isRequired); - case 'StringTypeAnnotation': + return wrapOptional('ReadableMap', isRequired); + case 'string': return wrapOptional('String', isRequired); default: - throw new Error( - `Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'ObjectTypeAnnotation': imports.add('com.facebook.react.bridge.WritableMap'); @@ -388,6 +406,8 @@ function getFalsyReturnStatementFromReturnType( return nullable ? 'return null;' : 'return 0;'; case 'BooleanTypeAnnotation': return nullable ? 'return null;' : 'return false;'; + case 'BooleanLiteralTypeAnnotation': + return nullable ? 'return null;' : 'return false;'; case 'EnumDeclaration': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -398,24 +418,24 @@ function getFalsyReturnStatementFromReturnType( throw new Error(createErrorMessage(realTypeAnnotation.type)); } case 'UnionTypeAnnotation': - switch (realTypeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(realTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return nullable ? 'return null;' : 'return false;'; + case 'number': return nullable ? 'return null;' : 'return 0;'; - case 'ObjectTypeAnnotation': + case 'object': return 'return null;'; - case 'StringTypeAnnotation': + case 'string': return nullable ? 'return null;' : 'return "";'; default: - throw new Error( - `Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'StringTypeAnnotation': return nullable ? 'return null;' : 'return "";'; case 'StringLiteralTypeAnnotation': return nullable ? 'return null;' : 'return "";'; - case 'StringLiteralUnionTypeAnnotation': - return nullable ? 'return null;' : 'return "";'; case 'ObjectTypeAnnotation': return 'return null;'; case 'GenericObjectTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js index e9ece53f47d867..691c5a30c2e51d 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js @@ -23,6 +23,7 @@ import type { import type {AliasResolver} from './Utils'; const {unwrapNullable} = require('../../parsers/parsers-commons'); +const {parseValidUnionType} = require('../Utils'); const {createAliasResolver, getModules} = require('./Utils'); type FilesOutput = Map; @@ -167,10 +168,10 @@ function translateReturnTypeToKind( return 'StringKind'; case 'StringLiteralTypeAnnotation': return 'StringKind'; - case 'StringLiteralUnionTypeAnnotation': - return 'StringKind'; case 'BooleanTypeAnnotation': return 'BooleanKind'; + case 'BooleanLiteralTypeAnnotation': + return 'BooleanKind'; case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -183,17 +184,19 @@ function translateReturnTypeToKind( ); } case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(realTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return 'BooleanKind'; + case 'number': return 'NumberKind'; - case 'ObjectTypeAnnotation': + case 'object': return 'ObjectKind'; - case 'StringTypeAnnotation': + case 'string': return 'StringKind'; default: - throw new Error( - `Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'NumberTypeAnnotation': return 'NumberKind'; @@ -252,10 +255,10 @@ function translateParamTypeToJniType( return 'Ljava/lang/String;'; case 'StringLiteralTypeAnnotation': return 'Ljava/lang/String;'; - case 'StringLiteralUnionTypeAnnotation': - return 'Ljava/lang/String;'; case 'BooleanTypeAnnotation': return !isRequired ? 'Ljava/lang/Boolean;' : 'Z'; + case 'BooleanLiteralTypeAnnotation': + return !isRequired ? 'Ljava/lang/Boolean;' : 'Z'; case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -268,17 +271,19 @@ function translateParamTypeToJniType( ); } case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(realTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return !isRequired ? 'Ljava/lang/Boolean;' : 'Z'; + case 'number': return !isRequired ? 'Ljava/lang/Double;' : 'D'; - case 'ObjectTypeAnnotation': + case 'object': return 'Lcom/facebook/react/bridge/ReadableMap;'; - case 'StringTypeAnnotation': + case 'string': return 'Ljava/lang/String;'; default: - throw new Error( - `Unsupported union prop value, found: ${realTypeAnnotation.memberType}"`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'NumberTypeAnnotation': return !isRequired ? 'Ljava/lang/Double;' : 'D'; @@ -334,10 +339,10 @@ function translateReturnTypeToJniType( return 'Ljava/lang/String;'; case 'StringLiteralTypeAnnotation': return 'Ljava/lang/String;'; - case 'StringLiteralUnionTypeAnnotation': - return 'Ljava/lang/String;'; case 'BooleanTypeAnnotation': return nullable ? 'Ljava/lang/Boolean;' : 'Z'; + case 'BooleanLiteralTypeAnnotation': + return nullable ? 'Ljava/lang/Boolean;' : 'Z'; case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -350,17 +355,19 @@ function translateReturnTypeToJniType( ); } case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(realTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return nullable ? 'Ljava/lang/Boolean;' : 'Z'; + case 'number': return nullable ? 'Ljava/lang/Double;' : 'D'; - case 'ObjectTypeAnnotation': - return 'Lcom/facebook/react/bridge/WritableMap;'; - case 'StringTypeAnnotation': + case 'object': + return 'Lcom/facebook/react/bridge/ReadableMap;'; + case 'string': return 'Ljava/lang/String;'; default: - throw new Error( - `Unsupported union member type, found: ${realTypeAnnotation.memberType}"`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'NumberTypeAnnotation': return nullable ? 'Ljava/lang/Double;' : 'D'; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js index db243d9f3024a2..3f46ca9c5da355 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js @@ -11,6 +11,7 @@ 'use strict'; import type { + BooleanLiteralTypeAnnotation, BooleanTypeAnnotation, DoubleTypeAnnotation, FloatTypeAnnotation, @@ -35,7 +36,7 @@ const { unwrapNullable, wrapNullable, } = require('../../../parsers/parsers-commons'); -const {capitalize} = require('../../Utils'); +const {capitalize, parseValidUnionType} = require('../../Utils'); type StructContext = 'CONSTANTS' | 'REGULAR'; @@ -65,6 +66,7 @@ export type StructTypeAnnotation = | StringLiteralUnionTypeAnnotation | NativeModuleNumberTypeAnnotation | NumberLiteralTypeAnnotation + | BooleanLiteralTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation @@ -127,27 +129,29 @@ class StructCollector { case 'MixedTypeAnnotation': throw new Error('Mixed types are unsupported in structs'); case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'StringTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': return wrapNullable(nullable, { - type: 'StringTypeAnnotation', + type: 'BooleanTypeAnnotation', }); - case 'NumberTypeAnnotation': + case 'number': return wrapNullable(nullable, { type: 'NumberTypeAnnotation', }); - case 'ObjectTypeAnnotation': + case 'object': // This isn't smart enough to actually know how to generate the // options on the native side. So we just treat it as an unknown object type return wrapNullable(nullable, { type: 'GenericObjectTypeAnnotation', }); + case 'string': + return wrapNullable(nullable, { + type: 'StringTypeAnnotation', + }); default: - (typeAnnotation.memberType: empty); - throw new Error( - 'Union types are unsupported in structs' + - JSON.stringify(typeAnnotation), - ); + (validUnionType: empty); + throw new Error(`Unsupported union member types`); } default: { return wrapNullable(nullable, typeAnnotation); diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js index 9cee0665e8f396..dcc0e7fff2a4e8 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js @@ -19,7 +19,7 @@ const {wrapOptional: wrapCxxOptional} = require('../../../TypeUtils/Cxx'); const { wrapOptional: wrapObjCOptional, } = require('../../../TypeUtils/Objective-C'); -const {capitalize} = require('../../../Utils'); +const {capitalize, parseValidUnionType} = require('../../../Utils'); const {getNamespacedStructName, getSafePropertyName} = require('../Utils'); const StructTemplate = ({ @@ -96,8 +96,21 @@ function toObjCType( return 'NSString *'; case 'StringLiteralTypeAnnotation': return 'NSString *'; - case 'StringLiteralUnionTypeAnnotation': - return 'NSString *'; + case 'UnionTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': + return wrapCxxOptional('bool', isRequired); + case 'number': + return wrapCxxOptional('double', isRequired); + case 'object': + return wrapObjCOptional('id', isRequired); + case 'string': + return 'NSString *'; + default: + (validUnionType: empty); + throw new Error(`Unsupported union member type`); + } case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'NumberLiteralTypeAnnotation': @@ -110,6 +123,8 @@ function toObjCType( return wrapCxxOptional('double', isRequired); case 'BooleanTypeAnnotation': return wrapCxxOptional('bool', isRequired); + case 'BooleanLiteralTypeAnnotation': + return wrapCxxOptional('bool', isRequired); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -181,8 +196,21 @@ function toObjCValue( return value; case 'StringLiteralTypeAnnotation': return value; - case 'StringLiteralUnionTypeAnnotation': - return value; + case 'UnionTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': + return wrapPrimitive('BOOL'); + case 'number': + return wrapPrimitive('double'); + case 'object': + return value; + case 'string': + return value; + default: + (validUnionType: empty); + throw new Error(`Unsupported union member type`); + } case 'NumberTypeAnnotation': return wrapPrimitive('double'); case 'NumberLiteralTypeAnnotation': @@ -195,6 +223,8 @@ function toObjCValue( return wrapPrimitive('double'); case 'BooleanTypeAnnotation': return wrapPrimitive('BOOL'); + case 'BooleanLiteralTypeAnnotation': + return wrapPrimitive('BOOL'); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js index 9fca06510ad933..f633f909437d02 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js @@ -19,7 +19,7 @@ const {wrapOptional: wrapCxxOptional} = require('../../../TypeUtils/Cxx'); const { wrapOptional: wrapObjCOptional, } = require('../../../TypeUtils/Objective-C'); -const {capitalize} = require('../../../Utils'); +const {capitalize, parseValidUnionType} = require('../../../Utils'); const {getNamespacedStructName, getSafePropertyName} = require('../Utils'); const StructTemplate = ({ @@ -87,8 +87,21 @@ function toObjCType( return 'NSString *'; case 'StringLiteralTypeAnnotation': return 'NSString *'; - case 'StringLiteralUnionTypeAnnotation': - return 'NSString *'; + case 'UnionTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': + return wrapCxxOptional('bool', isRequired); + case 'number': + return wrapCxxOptional('double', isRequired); + case 'object': + return wrapObjCOptional('id', isRequired); + case 'string': + return 'NSString *'; + default: + (validUnionType: empty); + throw new Error(`Unsupported union member type`); + } case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'NumberLiteralTypeAnnotation': @@ -101,6 +114,8 @@ function toObjCType( return wrapCxxOptional('double', isRequired); case 'BooleanTypeAnnotation': return wrapCxxOptional('bool', isRequired); + case 'BooleanLiteralTypeAnnotation': + return wrapCxxOptional('bool', isRequired); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -171,8 +186,21 @@ function toObjCValue( return RCTBridgingTo('String'); case 'StringLiteralTypeAnnotation': return RCTBridgingTo('String'); - case 'StringLiteralUnionTypeAnnotation': - return RCTBridgingTo('String'); + case 'UnionTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': + return RCTBridgingTo('Bool'); + case 'number': + return RCTBridgingTo('Double'); + case 'object': + return value; + case 'string': + return RCTBridgingTo('String'); + default: + (validUnionType: empty); + throw new Error(`Unsupported union member type`); + } case 'NumberTypeAnnotation': return RCTBridgingTo('Double'); case 'NumberLiteralTypeAnnotation': @@ -185,6 +213,8 @@ function toObjCValue( return RCTBridgingTo('Double'); case 'BooleanTypeAnnotation': return RCTBridgingTo('Bool'); + case 'BooleanLiteralTypeAnnotation': + return RCTBridgingTo('Bool'); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js index fd1afff066fbf7..42aca1934b5816 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js @@ -10,20 +10,33 @@ import type {NativeModuleEventEmitterShape} from '../../../CodegenSchema'; -const {toPascalCase} = require('../../Utils'); +const {parseValidUnionType, toPascalCase} = require('../../Utils'); function getEventEmitterTypeObjCType( eventEmitter: NativeModuleEventEmitterShape, ): string { - const type = eventEmitter.typeAnnotation.typeAnnotation.type; + const typeAnnotation = eventEmitter.typeAnnotation.typeAnnotation; - switch (type) { + switch (typeAnnotation.type) { case 'StringTypeAnnotation': return 'NSString *_Nonnull'; case 'StringLiteralTypeAnnotation': return 'NSString *_Nonnull'; - case 'StringLiteralUnionTypeAnnotation': - return 'NSString *_Nonnull'; + case 'UnionTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': + return 'BOOL'; + case 'number': + return 'NSNumber *_Nonnull'; + case 'object': + return 'NSDictionary *'; + case 'string': + return 'NSString *_Nonnull'; + default: + (validUnionType: empty); + throw new Error(`Unsupported union member type`); + } case 'NumberTypeAnnotation': case 'NumberLiteralTypeAnnotation': return 'NSNumber *_Nonnull'; @@ -44,7 +57,7 @@ function getEventEmitterTypeObjCType( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); default: - (type: empty); + (typeAnnotation.type: empty); throw new Error( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js index 8e0a56472ff8b3..8a9c76f76f3eb8 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js @@ -25,7 +25,7 @@ const { wrapNullable, } = require('../../../parsers/parsers-commons'); const {wrapOptional} = require('../../TypeUtils/Objective-C'); -const {capitalize} = require('../../Utils'); +const {capitalize, parseValidUnionType} = require('../../Utils'); const {getNamespacedStructName} = require('./Utils'); const invariant = require('invariant'); @@ -259,8 +259,21 @@ function getParamObjCType( return notStruct(wrapOptional('NSString *', !nullable)); case 'StringLiteralTypeAnnotation': return notStruct(wrapOptional('NSString *', !nullable)); - case 'StringLiteralUnionTypeAnnotation': - return notStruct(wrapOptional('NSString *', !nullable)); + case 'UnionTypeAnnotation': + const validUnionType = parseValidUnionType(structTypeAnnotation); + switch (validUnionType) { + case 'boolean': + return notStruct(isRequired ? 'BOOL' : 'NSNumber *'); + case 'number': + return notStruct(isRequired ? 'double' : 'NSNumber *'); + case 'object': + return notStruct(wrapOptional('NSDictionary *', !nullable)); + case 'string': + return notStruct(wrapOptional('NSString *', !nullable)); + default: + (validUnionType: empty); + throw new Error(`Unsupported union member type`); + } case 'NumberTypeAnnotation': return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'NumberLiteralTypeAnnotation': @@ -273,6 +286,8 @@ function getParamObjCType( return notStruct(isRequired ? 'NSInteger' : 'NSNumber *'); case 'BooleanTypeAnnotation': return notStruct(isRequired ? 'BOOL' : 'NSNumber *'); + case 'BooleanLiteralTypeAnnotation': + return notStruct(isRequired ? 'BOOL' : 'NSNumber *'); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -340,10 +355,6 @@ function getReturnObjCType( // TODO: Can NSString * returns not be _Nullable? // In the legacy codegen, we don't surround NSSTring * with _Nullable return wrapOptional('NSString *', isRequired); - case 'StringLiteralUnionTypeAnnotation': - // TODO: Can NSString * returns not be _Nullable? - // In the legacy codegen, we don't surround NSSTring * with _Nullable - return wrapOptional('NSString *', isRequired); case 'NumberTypeAnnotation': return wrapOptional('NSNumber *', isRequired); case 'NumberLiteralTypeAnnotation': @@ -356,6 +367,8 @@ function getReturnObjCType( return wrapOptional('NSNumber *', isRequired); case 'BooleanTypeAnnotation': return wrapOptional('NSNumber *', isRequired); + case 'BooleanLiteralTypeAnnotation': + return wrapOptional('NSNumber *', isRequired); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': @@ -368,19 +381,21 @@ function getReturnObjCType( ); } case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': return wrapOptional('NSNumber *', isRequired); - case 'ObjectTypeAnnotation': + case 'number': + return wrapOptional('NSNumber *', isRequired); + case 'object': return wrapOptional('NSDictionary *', isRequired); - case 'StringTypeAnnotation': + case 'string': // TODO: Can NSString * returns not be _Nullable? // In the legacy codegen, we don't surround NSSTring * with _Nullable return wrapOptional('NSString *', isRequired); default: - throw new Error( - `Unsupported union return type for ${methodName}, found: ${typeAnnotation.memberType}"`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member type`); } case 'GenericObjectTypeAnnotation': return wrapOptional('NSDictionary *', isRequired); @@ -414,8 +429,6 @@ function getReturnJSType( return 'StringKind'; case 'StringLiteralTypeAnnotation': return 'StringKind'; - case 'StringLiteralUnionTypeAnnotation': - return 'StringKind'; case 'NumberTypeAnnotation': return 'NumberKind'; case 'NumberLiteralTypeAnnotation': @@ -428,6 +441,8 @@ function getReturnJSType( return 'NumberKind'; case 'BooleanTypeAnnotation': return 'BooleanKind'; + case 'BooleanLiteralTypeAnnotation': + return 'BooleanKind'; case 'GenericObjectTypeAnnotation': return 'ObjectKind'; case 'EnumDeclaration': @@ -442,17 +457,19 @@ function getReturnJSType( ); } case 'UnionTypeAnnotation': - switch (typeAnnotation.memberType) { - case 'NumberTypeAnnotation': + const validUnionType = parseValidUnionType(typeAnnotation); + switch (validUnionType) { + case 'boolean': + return 'BooleanKind'; + case 'number': return 'NumberKind'; - case 'ObjectTypeAnnotation': + case 'object': return 'ObjectKind'; - case 'StringTypeAnnotation': + case 'string': return 'StringKind'; default: - throw new Error( - `Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`, - ); + (validUnionType: empty); + throw new Error(`Unsupported union member types`); } default: (typeAnnotation.type: 'MixedTypeAnnotation'); diff --git a/packages/react-native-codegen/src/parsers/errors.js b/packages/react-native-codegen/src/parsers/errors.js index d86cc1d9ae2ca1..ba5b3604efbc92 100644 --- a/packages/react-native-codegen/src/parsers/errors.js +++ b/packages/react-native-codegen/src/parsers/errors.js @@ -10,7 +10,6 @@ 'use strict'; -import type {UnionTypeAnnotationMemberType} from '../CodegenSchema'; import type {Parser} from './parser'; export type ParserType = 'Flow' | 'TypeScript'; @@ -337,26 +336,6 @@ class UnsupportedEnumDeclarationParserError extends ParserError { } } -/** - * Union parsing errors - */ - -class UnsupportedUnionTypeAnnotationParserError extends ParserError { - constructor( - nativeModuleName: string, - arrayElementTypeAST: $FlowFixMe, - types: UnionTypeAnnotationMemberType[], - ) { - super( - nativeModuleName, - arrayElementTypeAST, - `Union members must be of the same type, but multiple types were found ${types.join( - ', ', - )}'.`, - ); - } -} - /** * Module parsing errors */ @@ -460,7 +439,6 @@ module.exports = { UnsupportedFunctionParamTypeAnnotationParserError, UnsupportedFunctionReturnTypeAnnotationParserError, UnsupportedEnumDeclarationParserError, - UnsupportedUnionTypeAnnotationParserError, UnsupportedModuleEventEmitterTypePropertyParserError, UnsupportedModuleEventEmitterPropertyParserError, UnsupportedModulePropertyParserError, diff --git a/packages/react-native-codegen/src/parsers/flow/components/events.js b/packages/react-native-codegen/src/parsers/flow/components/events.js index ba900707aaae0e..c8f41e125c1b0a 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/events.js +++ b/packages/react-native-codegen/src/parsers/flow/components/events.js @@ -115,7 +115,7 @@ function extractArrayElementType( }; case 'UnionTypeAnnotation': return { - type: 'StringLiteralUnionTypeAnnotation', + type: 'UnionTypeAnnotation', types: typeAnnotation.types.map(option => ({ type: 'StringLiteralTypeAnnotation', value: parser.getLiteralValue(option), 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 51972f39ae03f6..33d858d8ba8a77 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -244,7 +244,18 @@ function translateTypeAnnotation( ); } case 'UnionTypeAnnotation': { - return emitUnion(nullable, hasteModuleName, typeAnnotation, parser); + return emitUnion( + nullable, + hasteModuleName, + typeAnnotation, + types, + aliasMap, + enumMap, + tryParse, + cxxOnly, + translateTypeAnnotation, + parser, + ); } case 'NumberLiteralTypeAnnotation': { return emitNumberLiteral(nullable, typeAnnotation.value); diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index c8224d4e44e729..d251d7976dbcd5 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -18,10 +18,10 @@ import type { NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleParamTypeAnnotation, + NativeModuleUnionTypeAnnotationMemberType, Nullable, PropTypeAnnotation, SchemaType, - UnionTypeAnnotationMemberType, } from '../../CodegenSchema'; import type {ParserType} from '../errors'; import type { @@ -111,7 +111,7 @@ class FlowParser implements Parser { remapUnionTypeAnnotationMemberNames( membersTypes: $FlowFixMe[], - ): UnionTypeAnnotationMemberType[] { + ): NativeModuleUnionTypeAnnotationMemberType[] { const remapLiteral = (item: $FlowFixMe) => { return item.type .replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation') @@ -121,12 +121,6 @@ class FlowParser implements Parser { return [...new Set(membersTypes.map(remapLiteral))]; } - getStringLiteralUnionTypeAnnotationStringLiterals( - membersTypes: $FlowFixMe[], - ): string[] { - return membersTypes.map((item: $FlowFixMe) => item.value); - } - parseFile(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8'); diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index f151fd1e2f7248..4e25ed6495c5ef 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -18,10 +18,10 @@ import type { NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleParamTypeAnnotation, + NativeModuleUnionTypeAnnotationMemberType, Nullable, PropTypeAnnotation, SchemaType, - UnionTypeAnnotationMemberType, } from '../CodegenSchema'; import type {ParserType} from './errors'; import type { @@ -146,15 +146,7 @@ export interface Parser { */ remapUnionTypeAnnotationMemberNames( types: $FlowFixMe, - ): UnionTypeAnnotationMemberType[]; - /** - * Given a union annotation members types, it returns an array of string literals. - * @parameter membersTypes: union annotation members types - * @returns: an array of string literals. - */ - getStringLiteralUnionTypeAnnotationStringLiterals( - types: $FlowFixMe, - ): string[]; + ): NativeModuleUnionTypeAnnotationMemberType[]; /** * Given the name of a file, it returns a Schema. * @parameter filename: the name of the file. diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 7b22d8a0a5e97c..5ce04ca1139697 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -18,10 +18,10 @@ import type { NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleParamTypeAnnotation, + NativeModuleUnionTypeAnnotationMemberType, Nullable, PropTypeAnnotation, SchemaType, - UnionTypeAnnotationMemberType, } from '../CodegenSchema'; import type {ParserType} from './errors'; import type { @@ -105,13 +105,7 @@ export class MockedParser implements Parser { remapUnionTypeAnnotationMemberNames( membersTypes: $FlowFixMe[], - ): UnionTypeAnnotationMemberType[] { - return []; - } - - getStringLiteralUnionTypeAnnotationStringLiterals( - membersTypes: $FlowFixMe[], - ): string[] { + ): NativeModuleUnionTypeAnnotationMemberType[] { return []; } diff --git a/packages/react-native-codegen/src/parsers/parsers-primitives.js b/packages/react-native-codegen/src/parsers/parsers-primitives.js index 27097a0b0b6f09..3dd4b5bf5e4743 100644 --- a/packages/react-native-codegen/src/parsers/parsers-primitives.js +++ b/packages/react-native-codegen/src/parsers/parsers-primitives.js @@ -30,12 +30,12 @@ import type { NativeModuleTypeAliasTypeAnnotation, NativeModuleTypeAnnotation, NativeModuleUnionTypeAnnotation, + NativeModuleUnionTypeAnnotationMemberType, Nullable, NumberLiteralTypeAnnotation, ObjectTypeAnnotation, ReservedTypeAnnotation, StringLiteralTypeAnnotation, - StringLiteralUnionTypeAnnotation, StringTypeAnnotation, VoidTypeAnnotation, } from '../CodegenSchema'; @@ -51,11 +51,7 @@ const { throwIfPartialNotAnnotatingTypeParameter, throwIfPartialWithMoreParameter, } = require('./error-utils'); -const { - ParserError, - UnsupportedTypeAnnotationParserError, - UnsupportedUnionTypeAnnotationParserError, -} = require('./errors'); +const {ParserError, UnsupportedTypeAnnotationParserError} = require('./errors'); const { assertGenericTypeAnnotationHasExactlyOneTypeParameter, translateFunctionTypeAnnotation, @@ -420,61 +416,50 @@ function emitUnion( nullable: boolean, hasteModuleName: string, typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, + tryParse: ParserErrorCapturer, + cxxOnly: boolean, + translateTypeAnnotation: $FlowFixMe, parser: Parser, -): Nullable< - NativeModuleUnionTypeAnnotation | StringLiteralUnionTypeAnnotation, -> { - // Get all the literals by type - // Verify they are all the same - // If string, persist as StringLiteralUnionType - // If number, persist as NumberTypeAnnotation (TODO: Number literal) - - const unionTypes = parser.remapUnionTypeAnnotationMemberNames( - typeAnnotation.types, +): Nullable { + const unparsedMemberTypes: $ReadOnlyArray<$FlowFixMe> = + (typeAnnotation.types: $ReadOnlyArray<$FlowFixMe>); + + const memberTypes = unparsedMemberTypes.map( + (memberType: $FlowFixMe): NativeModuleUnionTypeAnnotationMemberType => { + switch (memberType.type) { + case 'NumberLiteralTypeAnnotation': + case 'StringLiteralTypeAnnotation': + case 'BooleanLiteralTypeAnnotation': + case 'TSLiteralType': + case 'GenericTypeAnnotation': + case 'TSTypeLiteral': + case 'ObjectTypeAnnotation': + return translateTypeAnnotation( + hasteModuleName, + memberType, + types, + aliasMap, + enumMap, + tryParse, + cxxOnly, + parser, + ); + default: + throw new UnsupportedTypeAnnotationParserError( + hasteModuleName, + memberType, + parser.language(), + ); + } + }, ); - // Only support unionTypes of the same kind - if (unionTypes.length > 1) { - throw new UnsupportedUnionTypeAnnotationParserError( - hasteModuleName, - typeAnnotation, - unionTypes, - ); - } - - if (unionTypes[0] === 'StringTypeAnnotation') { - // Reprocess as a string literal union - return emitStringLiteralUnion( - nullable, - hasteModuleName, - typeAnnotation, - parser, - ); - } - return wrapNullable(nullable, { type: 'UnionTypeAnnotation', - memberType: unionTypes[0], - }); -} - -function emitStringLiteralUnion( - nullable: boolean, - hasteModuleName: string, - typeAnnotation: $FlowFixMe, - parser: Parser, -): Nullable { - const stringLiterals = - parser.getStringLiteralUnionTypeAnnotationStringLiterals( - typeAnnotation.types, - ); - - return wrapNullable(nullable, { - type: 'StringLiteralUnionTypeAnnotation', - types: stringLiterals.map(stringLiteral => ({ - type: 'StringLiteralTypeAnnotation', - value: stringLiteral, - })), + types: memberTypes, }); } @@ -752,7 +737,7 @@ function emitUnionProp( name, optional, typeAnnotation: { - type: 'StringLiteralUnionTypeAnnotation', + type: 'UnionTypeAnnotation', types: typeAnnotation.types.map(option => ({ type: 'StringLiteralTypeAnnotation', value: parser.getLiteralValue(option), diff --git a/packages/react-native-codegen/src/parsers/typescript/components/events.js b/packages/react-native-codegen/src/parsers/typescript/components/events.js index 1a2e038756bda3..9a97288a98f53e 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/events.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/events.js @@ -127,7 +127,7 @@ function extractArrayElementType( }; case 'TSUnionType': return { - type: 'StringLiteralUnionTypeAnnotation', + type: 'UnionTypeAnnotation', types: typeAnnotation.types.map(option => ({ type: 'StringLiteralTypeAnnotation', value: parser.getLiteralValue(option), 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 15f96966660d6b..56573e49645c72 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -398,7 +398,18 @@ function translateTypeAnnotation( ); } case 'TSUnionType': { - return emitUnion(nullable, hasteModuleName, typeAnnotation, parser); + return emitUnion( + nullable, + hasteModuleName, + typeAnnotation, + types, + aliasMap, + enumMap, + tryParse, + cxxOnly, + translateTypeAnnotation, + parser, + ); } case 'TSLiteralType': { const literal = typeAnnotation.literal; diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index edbaf368b526e0..60416eb6a1b3ff 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -18,10 +18,10 @@ import type { NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleParamTypeAnnotation, + NativeModuleUnionTypeAnnotationMemberType, Nullable, PropTypeAnnotation, SchemaType, - UnionTypeAnnotationMemberType, } from '../../CodegenSchema'; import type {ParserType} from '../errors'; import type { @@ -109,7 +109,7 @@ class TypeScriptParser implements Parser { remapUnionTypeAnnotationMemberNames( membersTypes: Array<$FlowFixMe>, - ): Array { + ): Array { const remapLiteral = (item: $FlowFixMe) => { return item.literal ? item.literal.type @@ -123,12 +123,6 @@ class TypeScriptParser implements Parser { return [...new Set(membersTypes.map(remapLiteral))]; } - getStringLiteralUnionTypeAnnotationStringLiterals( - membersTypes: Array<$FlowFixMe>, - ): Array { - return membersTypes.map((item: $FlowFixMe) => item.literal.value); - } - parseFile(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8');