Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions packages/react-native-codegen/src/CodegenSchema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,8 @@ export interface StringLiteralTypeAnnotation {
readonly value: string;
}

export interface StringLiteralUnionTypeAnnotation {
readonly type: 'StringLiteralUnionTypeAnnotation';
readonly types: StringLiteralTypeAnnotation[];
}
export type StringLiteralUnionTypeAnnotation =
UnionTypeAnnotation<StringLiteralTypeAnnotation>;

export interface NativeModuleNumberTypeAnnotation {
readonly type: 'NumberTypeAnnotation';
Expand Down Expand Up @@ -383,11 +381,6 @@ export interface NativeModulePromiseTypeAnnotation {
readonly elementType: Nullable<NativeModuleBaseTypeAnnotation> | VoidTypeAnnotation;
}

export type UnionTypeAnnotationMemberType =
| 'NumberTypeAnnotation'
| 'ObjectTypeAnnotation'
| 'StringTypeAnnotation';

export type NativeModuleUnionTypeAnnotationMemberType =
| NativeModuleObjectTypeAnnotation
| StringLiteralTypeAnnotation
Expand All @@ -397,10 +390,8 @@ export type NativeModuleUnionTypeAnnotationMemberType =
| StringTypeAnnotation
| NumberTypeAnnotation;

export interface NativeModuleUnionTypeAnnotation {
readonly type: 'UnionTypeAnnotation';
readonly memberType: UnionTypeAnnotationMemberType;
}
export type NativeModuleUnionTypeAnnotation =
UnionTypeAnnotation<NativeModuleUnionTypeAnnotationMemberType>;

export interface NativeModuleMixedTypeAnnotation {
readonly type: 'MixedTypeAnnotation';
Expand Down
18 changes: 5 additions & 13 deletions packages/react-native-codegen/src/CodegenSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,8 @@ export type BooleanLiteralTypeAnnotation = $ReadOnly<{
value: boolean,
}>;

export type StringLiteralUnionTypeAnnotation = $ReadOnly<{
type: 'StringLiteralUnionTypeAnnotation',
types: $ReadOnlyArray<StringLiteralTypeAnnotation>,
}>;
export type StringLiteralUnionTypeAnnotation =
UnionTypeAnnotation<StringLiteralTypeAnnotation>;

export type VoidTypeAnnotation = $ReadOnly<{
type: 'VoidTypeAnnotation',
Expand Down Expand Up @@ -372,11 +370,6 @@ export type NativeModulePromiseTypeAnnotation = $ReadOnly<{
elementType: VoidTypeAnnotation | Nullable<NativeModuleBaseTypeAnnotation>,
}>;

export type UnionTypeAnnotationMemberType =
| 'NumberTypeAnnotation'
| 'ObjectTypeAnnotation'
| 'StringTypeAnnotation';

export type NativeModuleUnionTypeAnnotationMemberType =
| NativeModuleObjectTypeAnnotation
| StringLiteralTypeAnnotation
Expand All @@ -386,10 +379,8 @@ export type NativeModuleUnionTypeAnnotationMemberType =
| StringTypeAnnotation
| NumberTypeAnnotation;

export type NativeModuleUnionTypeAnnotation = $ReadOnly<{
type: 'UnionTypeAnnotation',
memberType: UnionTypeAnnotationMemberType,
}>;
export type NativeModuleUnionTypeAnnotation =
UnionTypeAnnotation<NativeModuleUnionTypeAnnotationMemberType>;

export type NativeModuleMixedTypeAnnotation = $ReadOnly<{
type: 'MixedTypeAnnotation',
Expand Down Expand Up @@ -473,6 +464,7 @@ export type CompleteTypeAnnotation =
| UnsafeAnyTypeAnnotation
| ArrayTypeAnnotation<CompleteTypeAnnotation>
| ObjectTypeAnnotation<CompleteTypeAnnotation>
| NativeModuleUnionTypeAnnotationMemberType
// Components
| CommandTypeAnnotation
| CompleteReservedTypeAnnotation;
58 changes: 58 additions & 0 deletions packages/react-native-codegen/src/generators/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

'use strict';

import type {NativeModuleUnionTypeAnnotation} from '../CodegenSchema';

function capitalize(string: string): string {
return string.charAt(0).toUpperCase() + string.slice(1);
}
Expand Down Expand Up @@ -44,10 +46,66 @@ 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,
];

class HeterogeneousUnionError extends Error {
constructor() {
super(`Non-homogenous union member types`);
}
}

function parseValidUnionType(
annotation: NativeModuleUnionTypeAnnotation,
): ValidUnionType {
const isUnionOfType = (types: $ReadOnlyArray<string>): 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 HeterogeneousUnionError();
} else {
throw new Error(
`Unsupported union member types: ${invalidTypes.join(', ')}"`,
);
}
}

module.exports = {
capitalize,
indent,
parseValidUnionType,
toPascalCase,
toSafeCppString,
getEnumName,
HeterogeneousUnionError,
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
PropTypeAnnotation,
} from '../../CodegenSchema';

const {getEnumName, toSafeCppString} = require('../Utils');
const {getEnumName, parseValidUnionType, toSafeCppString} = require('../Utils');

function toIntEnumValueName(propName: string, value: number): string {
return `${toSafeCppString(propName)}${value}`;
Expand Down Expand Up @@ -61,7 +61,40 @@ function getCppArrayTypeForAnnotation(
case 'Int32TypeAnnotation':
case 'MixedTypeAnnotation':
return `std::vector<${getCppTypeForAnnotation(typeElement.type)}>`;
case 'StringLiteralUnionTypeAnnotation':
case 'UnionTypeAnnotation':
const validUnionType = parseValidUnionType(typeElement);
switch (validUnionType) {
case 'boolean':
return `std::vector<${getCppTypeForAnnotation('BooleanTypeAnnotation')}>`;
case 'number':
// Have type-upgraded Number to Double in this case to match the Int32TypeAnnotation, FloatTypeAnnotation & DoubleTypeAnnotation
return `std::vector<${getCppTypeForAnnotation('DoubleTypeAnnotation')}>`;
case 'object':
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)}>`;
case 'string':
if (
typeElement.types.every(
({type}) => type === '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)}>`;
}
// Unions of strings and string literals are treated as just strings
return `std::vector<${getCppTypeForAnnotation('StringTypeAnnotation')}>`;
default:
(validUnionType: empty);
throw new Error(`Unsupported union member type`);
}
case 'ObjectTypeAnnotation':
if (!structParts) {
throw new Error(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
} from '../../CodegenSchema';

const {indent} = require('../Utils');
const {parseValidUnionType} = require('../Utils');
const {IncludeTemplate, generateEventStructName} = require('./CppHelpers');

// File path -> contents
Expand Down Expand Up @@ -207,7 +208,11 @@ function handleArrayElementType(
loopLocalVariable,
val => `jsi::valueFromDynamic(runtime, ${val})`,
);
case 'StringLiteralUnionTypeAnnotation':
case 'UnionTypeAnnotation':
const validUnionType = parseValidUnionType(elementType);
if (validUnionType !== 'string') {
throw new Error('Invalid since it is a union of non strings');
}
return setValueAtIndex(
propertyName,
indexVariable,
Expand Down Expand Up @@ -320,7 +325,11 @@ function generateSetters(
usingEvent,
prop => `jsi::valueFromDynamic(runtime, ${prop})`,
);
case 'StringLiteralUnionTypeAnnotation':
case 'UnionTypeAnnotation':
const validUnionType = parseValidUnionType(typeAnnotation);
if (validUnionType !== 'string') {
throw new Error('Invalid since it is a union of non strings');
}
return generateSetter(
parentPropertyName,
eventProperty.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type {
SchemaType,
} from '../../CodegenSchema';

const {indent, toSafeCppString} = require('../Utils');
const {indent, parseValidUnionType, toSafeCppString} = require('../Utils');
const {
generateEventStructName,
getCppArrayTypeForAnnotation,
Expand Down Expand Up @@ -118,17 +118,21 @@ function getNativeTypeFromAnnotation(
eventProperty: NamedShape<EventTypeAnnotation>,
nameParts: $ReadOnlyArray<string>,
): string {
const {type} = eventProperty.typeAnnotation;

switch (type) {
const {typeAnnotation} = eventProperty;
switch (typeAnnotation.type) {
case 'BooleanTypeAnnotation':
case 'StringTypeAnnotation':
case 'Int32TypeAnnotation':
case 'DoubleTypeAnnotation':
case 'FloatTypeAnnotation':
case 'MixedTypeAnnotation':
return getCppTypeForAnnotation(type);
case 'StringLiteralUnionTypeAnnotation':
return getCppTypeForAnnotation(typeAnnotation.type);
case 'UnionTypeAnnotation':
const validUnionType = parseValidUnionType(typeAnnotation);
if (validUnionType !== 'string') {
throw new Error('Invalid since it is a union of non strings');
}
return generateEventStructName([...nameParts, eventProperty.name]);
case 'ObjectTypeAnnotation':
return generateEventStructName([...nameParts, eventProperty.name]);
case 'ArrayTypeAnnotation':
Expand All @@ -143,8 +147,10 @@ function getNativeTypeFromAnnotation(
eventProperty.name,
]);
default:
(type: empty);
throw new Error(`Received invalid event property type ${type}`);
(typeAnnotation.type: empty);
throw new Error(
`Received invalid event property type ${typeAnnotation.type}`,
);
}
}
function generateEnum(
Expand Down Expand Up @@ -188,7 +194,11 @@ function handleGenerateStructForArray(
nameParts.concat([name]),
nullthrows(elementType.properties),
);
} else if (elementType.type === 'StringLiteralUnionTypeAnnotation') {
} else if (elementType.type === 'UnionTypeAnnotation') {
const validUnionType = parseValidUnionType(elementType);
if (validUnionType !== 'string') {
throw new Error('Invalid since it is a union of non strings');
}
generateEnum(
structs,
elementType.types.map(literal => literal.value),
Expand Down Expand Up @@ -251,7 +261,11 @@ function generateStruct(
nullthrows(typeAnnotation.properties),
);
return;
case 'StringLiteralUnionTypeAnnotation':
case 'UnionTypeAnnotation':
const validUnionType = parseValidUnionType(typeAnnotation);
if (validUnionType !== 'string') {
throw new Error('Invalid since it is a union of non strings');
}
generateEnum(
structs,
typeAnnotation.types.map(literal => literal.value),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1273,7 +1273,7 @@ const EVENT_PROPS: SchemaType = {
typeAnnotation: {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'StringLiteralUnionTypeAnnotation',
type: 'UnionTypeAnnotation',
types: [
{
type: 'StringLiteralTypeAnnotation',
Expand Down Expand Up @@ -1383,7 +1383,7 @@ const EVENT_PROPS: SchemaType = {
name: 'orientation',
optional: false,
typeAnnotation: {
type: 'StringLiteralUnionTypeAnnotation',
type: 'UnionTypeAnnotation',
types: [
{
type: 'StringLiteralTypeAnnotation',
Expand Down
Loading
Loading