From e8d86f45f5a10ade48c22a7773aab50b1d02c63b Mon Sep 17 00:00:00 2001 From: Vojtech Novak Date: Mon, 24 Feb 2025 14:01:09 +0100 Subject: [PATCH] rebuild codegen --- .../lib/CodegenSchema.d.ts | 99 ++++--- .../lib/CodegenSchema.js.flow | 132 ++++++---- .../cli/combine/combine-js-to-schema-cli.js | 6 + .../combine/combine-js-to-schema-cli.js.flow | 6 + .../lib/cli/combine/combine-js-to-schema.js | 2 +- .../cli/combine/combine-js-to-schema.js.flow | 2 +- .../lib/cli/combine/combine-schemas-cli.js | 30 ++- .../cli/combine/combine-schemas-cli.js.flow | 19 +- .../lib/cli/generators/generate-all.js | 5 +- .../lib/cli/generators/generate-all.js.flow | 3 +- .../generators/__test_fixtures__/fixtures.js | 3 + .../__test_fixtures__/fixtures.js.flow | 3 + .../lib/generators/components/CppHelpers.js | 2 +- .../generators/components/CppHelpers.js.flow | 2 +- .../components/GenerateEventEmitterCpp.js | 8 +- .../GenerateEventEmitterCpp.js.flow | 8 +- .../components/GenerateEventEmitterH.js | 18 +- .../components/GenerateEventEmitterH.js.flow | 18 +- .../components/GeneratePropsJavaDelegate.js | 5 +- .../GeneratePropsJavaDelegate.js.flow | 5 +- .../components/GeneratePropsJavaInterface.js | 2 +- .../GeneratePropsJavaInterface.js.flow | 2 +- .../PojoCollector.js.flow | 8 +- .../lib/generators/components/JavaHelpers.js | 6 + .../generators/components/JavaHelpers.js.flow | 14 +- .../components/__test_fixtures__/fixtures.js | 36 ++- .../__test_fixtures__/fixtures.js.flow | 36 ++- .../generators/modules/GenerateModuleCpp.js | 6 + .../modules/GenerateModuleCpp.js.flow | 6 + .../lib/generators/modules/GenerateModuleH.js | 23 +- .../modules/GenerateModuleH.js.flow | 29 ++- .../modules/GenerateModuleJavaSpec.js | 35 ++- .../modules/GenerateModuleJavaSpec.js.flow | 35 ++- .../modules/GenerateModuleJniCpp.js | 18 ++ .../modules/GenerateModuleJniCpp.js.flow | 18 ++ .../GenerateModuleObjCpp/StructCollector.js | 23 +- .../StructCollector.js.flow | 39 ++- .../header/serializeConstantsStruct.js | 12 + .../header/serializeConstantsStruct.js.flow | 12 + .../header/serializeRegularStruct.js | 12 + .../header/serializeRegularStruct.js.flow | 12 + .../serializeEventEmitter.js | 17 +- .../serializeEventEmitter.js.flow | 18 +- .../GenerateModuleObjCpp/serializeMethod.js | 22 ++ .../serializeMethod.js.flow | 22 ++ .../modules/__test_fixtures__/fixtures.js | 239 +++++++++++++++-- .../__test_fixtures__/fixtures.js.flow | 241 ++++++++++++++++-- .../lib/parsers/errors.js | 8 + .../lib/parsers/errors.js.flow | 10 + .../components/__test_fixtures__/failures.js | 14 +- .../__test_fixtures__/failures.js.flow | 14 +- .../components/__test_fixtures__/fixtures.js | 31 ++- .../__test_fixtures__/fixtures.js.flow | 31 ++- .../lib/parsers/flow/components/commands.js | 70 ++++- .../parsers/flow/components/commands.js.flow | 73 +++++- .../lib/parsers/flow/components/events.js | 9 +- .../parsers/flow/components/events.js.flow | 9 +- .../modules/__test_fixtures__/failures.js | 57 +++++ .../__test_fixtures__/failures.js.flow | 59 +++++ .../modules/__test_fixtures__/fixtures.js | 35 ++- .../__test_fixtures__/fixtures.js.flow | 36 ++- .../lib/parsers/flow/modules/index.js | 17 +- .../lib/parsers/flow/modules/index.js.flow | 18 +- .../lib/parsers/flow/parser.js | 33 ++- .../lib/parsers/flow/parser.js.flow | 38 ++- .../lib/parsers/parser.js.flow | 14 +- .../lib/parsers/parserMock.js | 23 +- .../lib/parsers/parserMock.js.flow | 32 ++- .../lib/parsers/parsers-primitives.js | 61 ++++- .../lib/parsers/parsers-primitives.js.flow | 81 +++++- .../components/__test_fixtures__/fixtures.js | 31 ++- .../__test_fixtures__/fixtures.js.flow | 31 ++- .../parsers/typescript/components/commands.js | 47 +++- .../typescript/components/commands.js.flow | 52 +++- .../parsers/typescript/components/events.js | 9 +- .../typescript/components/events.js.flow | 9 +- .../modules/__test_fixtures__/failures.js | 53 ++++ .../__test_fixtures__/failures.js.flow | 55 ++++ .../modules/__test_fixtures__/fixtures.js | 34 ++- .../__test_fixtures__/fixtures.js.flow | 35 ++- .../lib/parsers/typescript/modules/index.js | 31 +++ .../parsers/typescript/modules/index.js.flow | 33 +++ .../lib/parsers/typescript/parser.js | 128 ++++++++-- .../lib/parsers/typescript/parser.js.flow | 84 ++++-- 84 files changed, 2338 insertions(+), 386 deletions(-) diff --git a/packages/react-native-codegen/lib/CodegenSchema.d.ts b/packages/react-native-codegen/lib/CodegenSchema.d.ts index 0a99ea06e16521..a941609790ce72 100644 --- a/packages/react-native-codegen/lib/CodegenSchema.d.ts +++ b/packages/react-native-codegen/lib/CodegenSchema.d.ts @@ -38,11 +38,6 @@ export interface StringTypeAnnotation { readonly type: 'StringTypeAnnotation'; } -export interface StringEnumTypeAnnotation { - readonly type: 'StringEnumTypeAnnotation'; - readonly options: readonly string[]; -} - export interface VoidTypeAnnotation { readonly type: 'VoidTypeAnnotation'; } @@ -127,16 +122,11 @@ export type EventTypeAnnotation = | FloatTypeAnnotation | Int32TypeAnnotation | MixedTypeAnnotation - | StringEnumTypeAnnotation + | StringLiteralUnionTypeAnnotation | ObjectTypeAnnotation - | { - readonly type: 'ArrayTypeAnnotation'; - readonly elementType: EventTypeAnnotation - }; + | ArrayTypeAnnotation -export type ArrayTypeAnnotation = { - readonly type: 'ArrayTypeAnnotation'; - readonly elementType: +export type ComponentArrayTypeAnnotation = ArrayTypeAnnotation< | BooleanTypeAnnotation | StringTypeAnnotation | DoubleTypeAnnotation @@ -149,10 +139,25 @@ export type ArrayTypeAnnotation = { } | ObjectTypeAnnotation | ReservedPropTypeAnnotation - | { - readonly type: 'ArrayTypeAnnotation'; - readonly elementType: ObjectTypeAnnotation; - }; + | ArrayTypeAnnotation> +>; + +export type ComponentCommandArrayTypeAnnotation = ArrayTypeAnnotation< + | BooleanTypeAnnotation + | StringTypeAnnotation + | DoubleTypeAnnotation + | FloatTypeAnnotation + | Int32TypeAnnotation + // Mixed is not great. This generally means its a type alias to another type + // like an object or union. Ideally we'd encode that type in the schema so the compat-check can + // validate those deeper objects for breaking changes and the generators can do something smarter. + // As of now, the generators just create ReadableMap or (const NSArray *) which are untyped + | MixedTypeAnnotation +>; + +export interface ArrayTypeAnnotation { + readonly type: 'ArrayTypeAnnotation'; + readonly elementType: T; } export type PropTypeAnnotation = @@ -188,7 +193,7 @@ export type PropTypeAnnotation = } | ReservedPropTypeAnnotation | ObjectTypeAnnotation - | ArrayTypeAnnotation + | ComponentArrayTypeAnnotation | MixedTypeAnnotation; export interface ReservedPropTypeAnnotation { @@ -214,7 +219,7 @@ export type CommandParamTypeAnnotation = | DoubleTypeAnnotation | FloatTypeAnnotation | StringTypeAnnotation - | ArrayTypeAnnotation; + | ComponentCommandArrayTypeAnnotation; export interface ReservedTypeAnnotation { readonly type: 'ReservedTypeAnnotation'; @@ -273,23 +278,36 @@ export type NativeModuleObjectTypeAnnotation = ObjectTypeAnnotation< Nullable >; -export interface NativeModuleArrayTypeAnnotation> { - readonly type: 'ArrayTypeAnnotation'; - /** - * TODO(T72031674): Migrate all our NativeModule specs to not use - * invalid Array ElementTypes. Then, make the elementType required. - */ - readonly elementType: T | UnsafeAnyTypeAnnotation; -} +/** + * TODO(T72031674): Migrate all our NativeModule specs to not use + * invalid Array ElementTypes. Then, make the elementType required. + */ +interface NativeModuleArrayTypeAnnotation extends ArrayTypeAnnotation { } + export interface UnsafeAnyTypeAnnotation { readonly type: 'AnyTypeAnnotation', } +export interface NativeModuleNumberLiteralTypeAnnotation { + readonly type: 'NumberLiteralTypeAnnotation'; + readonly value: number; +} + export interface NativeModuleStringTypeAnnotation { readonly type: 'StringTypeAnnotation'; } +export interface NativeModuleStringLiteralTypeAnnotation { + readonly type: 'StringLiteralTypeAnnotation'; + readonly value: string; +} + +export interface StringLiteralUnionTypeAnnotation { + readonly type: 'StringLiteralUnionTypeAnnotation'; + readonly types: NativeModuleStringLiteralTypeAnnotation[]; +} + export interface NativeModuleNumberTypeAnnotation { readonly type: 'NumberTypeAnnotation'; } @@ -310,10 +328,10 @@ export interface NativeModuleBooleanTypeAnnotation { readonly type: 'BooleanTypeAnnotation'; } -export type NativeModuleEnumMembers = readonly { +export type NativeModuleEnumMember = { readonly name: string; - readonly value: string | number; -}[]; + readonly value: NativeModuleStringLiteralTypeAnnotation | NativeModuleNumberLiteralTypeAnnotation, +}; export type NativeModuleEnumMemberType = | 'NumberTypeAnnotation' @@ -329,7 +347,7 @@ export interface NativeModuleEnumDeclarationWithMembers { name: string; type: 'EnumDeclarationWithMembers'; memberType: NativeModuleEnumMemberType; - members: NativeModuleEnumMembers; + members: readonly NativeModuleEnumMember[]; } export interface NativeModuleGenericObjectTypeAnnotation { @@ -347,7 +365,7 @@ export interface NativeModuleTypeAliasTypeAnnotation { export interface NativeModulePromiseTypeAnnotation { readonly type: 'PromiseTypeAnnotation'; - readonly elementType?: Nullable | undefined; + readonly elementType: Nullable | VoidTypeAnnotation; } export type UnionTypeAnnotationMemberType = @@ -370,21 +388,24 @@ export type NativeModuleEventEmitterBaseTypeAnnotation = | NativeModuleFloatTypeAnnotation | NativeModuleInt32TypeAnnotation | NativeModuleNumberTypeAnnotation + | NativeModuleNumberLiteralTypeAnnotation | NativeModuleStringTypeAnnotation + | NativeModuleStringLiteralTypeAnnotation + | StringLiteralUnionTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleGenericObjectTypeAnnotation | VoidTypeAnnotation; export type NativeModuleEventEmitterTypeAnnotation = | NativeModuleEventEmitterBaseTypeAnnotation - | { - readonly type: 'ArrayTypeAnnotation'; - readonly elementType: NativeModuleEventEmitterBaseTypeAnnotation; - }; + | ArrayTypeAnnotation; export type NativeModuleBaseTypeAnnotation = - | NativeModuleStringTypeAnnotation + NativeModuleStringTypeAnnotation + | NativeModuleStringLiteralTypeAnnotation + | StringLiteralUnionTypeAnnotation | NativeModuleNumberTypeAnnotation + | NativeModuleNumberLiteralTypeAnnotation | NativeModuleInt32TypeAnnotation | NativeModuleDoubleTypeAnnotation | NativeModuleFloatTypeAnnotation @@ -393,10 +414,10 @@ export type NativeModuleBaseTypeAnnotation = | NativeModuleGenericObjectTypeAnnotation | ReservedTypeAnnotation | NativeModuleTypeAliasTypeAnnotation - | NativeModuleArrayTypeAnnotation> | NativeModuleObjectTypeAnnotation | NativeModuleUnionTypeAnnotation - | NativeModuleMixedTypeAnnotation; + | NativeModuleMixedTypeAnnotation + | NativeModuleArrayTypeAnnotation; export type NativeModuleParamTypeAnnotation = | NativeModuleBaseTypeAnnotation diff --git a/packages/react-native-codegen/lib/CodegenSchema.js.flow b/packages/react-native-codegen/lib/CodegenSchema.js.flow index 00a5224aee2ceb..de92abf1f11349 100644 --- a/packages/react-native-codegen/lib/CodegenSchema.js.flow +++ b/packages/react-native-codegen/lib/CodegenSchema.js.flow @@ -37,13 +37,23 @@ export type Int32TypeAnnotation = $ReadOnly<{ type: 'Int32TypeAnnotation', }>; +export type NumberLiteralTypeAnnotation = $ReadOnly<{ + type: 'NumberLiteralTypeAnnotation', + value: number, +}>; + export type StringTypeAnnotation = $ReadOnly<{ type: 'StringTypeAnnotation', }>; -export type StringEnumTypeAnnotation = $ReadOnly<{ - type: 'StringEnumTypeAnnotation', - options: $ReadOnlyArray, +export type StringLiteralTypeAnnotation = $ReadOnly<{ + type: 'StringLiteralTypeAnnotation', + value: string, +}>; + +export type StringLiteralUnionTypeAnnotation = $ReadOnly<{ + type: 'StringLiteralUnionTypeAnnotation', + types: $ReadOnlyArray, }>; export type VoidTypeAnnotation = $ReadOnly<{ @@ -61,7 +71,7 @@ export type MixedTypeAnnotation = $ReadOnly<{ type: 'MixedTypeAnnotation', }>; -type EventEmitterTypeAnnotation = $ReadOnly<{ +export type EventEmitterTypeAnnotation = $ReadOnly<{ type: 'EventEmitterTypeAnnotation', typeAnnotation: NativeModuleEventEmitterTypeAnnotation | $FlowFixMe, }>; @@ -128,32 +138,42 @@ export type EventTypeAnnotation = | FloatTypeAnnotation | Int32TypeAnnotation | MixedTypeAnnotation - | StringEnumTypeAnnotation + | StringLiteralUnionTypeAnnotation | ObjectTypeAnnotation + | ArrayTypeAnnotation; + +export type ComponentArrayTypeAnnotation = ArrayTypeAnnotation< + | BooleanTypeAnnotation + | StringTypeAnnotation + | DoubleTypeAnnotation + | FloatTypeAnnotation + | Int32TypeAnnotation | $ReadOnly<{ - type: 'ArrayTypeAnnotation', - elementType: EventTypeAnnotation, - }>; + type: 'StringEnumTypeAnnotation', + default: string, + options: $ReadOnlyArray, + }> + | ObjectTypeAnnotation + | ReservedPropTypeAnnotation + | ArrayTypeAnnotation>, +>; + +export type ComponentCommandArrayTypeAnnotation = ArrayTypeAnnotation< + | BooleanTypeAnnotation + | StringTypeAnnotation + | DoubleTypeAnnotation + | FloatTypeAnnotation + | Int32TypeAnnotation + // Mixed is not great. This generally means its a type alias to another type + // like an object or union. Ideally we'd encode that type in the schema so the compat-check can + // validate those deeper objects for breaking changes and the generators can do something smarter. + // As of now, the generators just create ReadableMap or (const NSArray *) which are untyped + | MixedTypeAnnotation, +>; -export type ArrayTypeAnnotation = $ReadOnly<{ +export type ArrayTypeAnnotation<+T> = $ReadOnly<{ type: 'ArrayTypeAnnotation', - elementType: - | BooleanTypeAnnotation - | StringTypeAnnotation - | DoubleTypeAnnotation - | FloatTypeAnnotation - | Int32TypeAnnotation - | $ReadOnly<{ - type: 'StringEnumTypeAnnotation', - default: string, - options: $ReadOnlyArray, - }> - | ObjectTypeAnnotation - | ReservedPropTypeAnnotation - | $ReadOnly<{ - type: 'ArrayTypeAnnotation', - elementType: ObjectTypeAnnotation, - }>, + elementType: T, }>; export type PropTypeAnnotation = @@ -189,7 +209,7 @@ export type PropTypeAnnotation = }> | ReservedPropTypeAnnotation | ObjectTypeAnnotation - | ArrayTypeAnnotation + | ComponentArrayTypeAnnotation | MixedTypeAnnotation; export type ReservedPropTypeAnnotation = $ReadOnly<{ @@ -215,7 +235,7 @@ export type CommandParamTypeAnnotation = | DoubleTypeAnnotation | FloatTypeAnnotation | StringTypeAnnotation - | ArrayTypeAnnotation; + | ComponentCommandArrayTypeAnnotation; export type ReservedTypeAnnotation = $ReadOnly<{ type: 'ReservedTypeAnnotation', @@ -277,14 +297,14 @@ export type NativeModuleObjectTypeAnnotation = ObjectTypeAnnotation< export type NativeModuleArrayTypeAnnotation< +T: Nullable, -> = $ReadOnly<{ - type: 'ArrayTypeAnnotation', +> = ArrayTypeAnnotation< + | T /** * TODO(T72031674): Migrate all our NativeModule specs to not use * invalid Array ElementTypes. Then, make the elementType required. */ - elementType: T | UnsafeAnyTypeAnnotation, -}>; + | UnsafeAnyTypeAnnotation, +>; export type UnsafeAnyTypeAnnotation = { type: 'AnyTypeAnnotation', @@ -294,12 +314,10 @@ export type NativeModuleNumberTypeAnnotation = $ReadOnly<{ type: 'NumberTypeAnnotation', }>; -export type NativeModuleEnumMembers = $ReadOnlyArray< - $ReadOnly<{ - name: string, - value: string | number, - }>, ->; +export type NativeModuleEnumMember = { + name: string, + value: StringLiteralTypeAnnotation | NumberLiteralTypeAnnotation, +}; export type NativeModuleEnumMemberType = | 'NumberTypeAnnotation' @@ -315,7 +333,7 @@ export type NativeModuleEnumDeclarationWithMembers = { name: string, type: 'EnumDeclarationWithMembers', memberType: NativeModuleEnumMemberType, - members: NativeModuleEnumMembers, + members: $ReadOnlyArray, }; export type NativeModuleGenericObjectTypeAnnotation = $ReadOnly<{ @@ -333,7 +351,7 @@ export type NativeModuleTypeAliasTypeAnnotation = $ReadOnly<{ export type NativeModulePromiseTypeAnnotation = $ReadOnly<{ type: 'PromiseTypeAnnotation', - elementType?: Nullable, + elementType: VoidTypeAnnotation | Nullable, }>; export type UnionTypeAnnotationMemberType = @@ -356,21 +374,24 @@ type NativeModuleEventEmitterBaseTypeAnnotation = | FloatTypeAnnotation | Int32TypeAnnotation | NativeModuleNumberTypeAnnotation + | NumberLiteralTypeAnnotation | StringTypeAnnotation + | StringLiteralTypeAnnotation + | StringLiteralUnionTypeAnnotation | NativeModuleTypeAliasTypeAnnotation | NativeModuleGenericObjectTypeAnnotation | VoidTypeAnnotation; export type NativeModuleEventEmitterTypeAnnotation = | NativeModuleEventEmitterBaseTypeAnnotation - | { - type: 'ArrayTypeAnnotation', - elementType: NativeModuleEventEmitterBaseTypeAnnotation, - }; + | ArrayTypeAnnotation; export type NativeModuleBaseTypeAnnotation = | StringTypeAnnotation + | StringLiteralTypeAnnotation + | StringLiteralUnionTypeAnnotation | NativeModuleNumberTypeAnnotation + | NumberLiteralTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation @@ -403,3 +424,26 @@ type NativeModuleParamOnlyTypeAnnotation = NativeModuleFunctionTypeAnnotation; type NativeModuleReturnOnlyTypeAnnotation = | NativeModulePromiseTypeAnnotation | VoidTypeAnnotation; + +// Add the allowed component reserved types to the native module union +export type CompleteReservedTypeAnnotation = + | ReservedTypeAnnotation + | { + type: 'ReservedTypeAnnotation', + name: ReservedPropTypeAnnotation['name'], + }; + +// Used by compatibility check which needs to handle all possible types +// This will eventually also include the union of all view manager types +export type CompleteTypeAnnotation = + | NativeModuleTypeAnnotation + | NativeModuleFunctionTypeAnnotation + | NullableTypeAnnotation + | EventEmitterTypeAnnotation + | NativeModuleEnumDeclarationWithMembers + | UnsafeAnyTypeAnnotation + | ArrayTypeAnnotation + | ObjectTypeAnnotation + // Components + | CommandTypeAnnotation + | CompleteReservedTypeAnnotation; diff --git a/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js b/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js index 0c6f74abaabfa0..dcbe3e12e26d54 100644 --- a/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js +++ b/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js @@ -59,11 +59,17 @@ const _require = require('./combine-js-to-schema'), _require.combineSchemasInFileListAndWriteToFile; const yargs = require('yargs'); const argv = yargs + .usage('Usage: $0 [ ...]') .option('p', { + describe: + 'Platforms to generate schema for, this works on filenames: [.].(js|tsx?)', alias: 'platform', + default: null, }) .option('e', { + describe: 'Regular expression to exclude files from schema generation', alias: 'exclude', + default: null, }) .parseSync(); const _argv$_ = _toArray(argv._), diff --git a/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js.flow b/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js.flow index f2c98912efeba4..9fc5e0b2fb8183 100644 --- a/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js.flow +++ b/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema-cli.js.flow @@ -17,11 +17,17 @@ const { const yargs = require('yargs'); const argv = yargs + .usage('Usage: $0 [ ...]') .option('p', { + describe: + 'Platforms to generate schema for, this works on filenames: [.].(js|tsx?)', alias: 'platform', + default: null, }) .option('e', { + describe: 'Regular expression to exclude files from schema generation', alias: 'exclude', + default: null, }) .parseSync(); diff --git a/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js b/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js index c5a105463976ed..29d479b5a78166 100644 --- a/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js +++ b/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js @@ -140,7 +140,7 @@ function combineSchemasInFileListAndWriteToFile( exclude, ) { const combined = combineSchemasInFileList(fileList, platform, exclude); - const formattedSchema = JSON.stringify(combined, null, 2); + const formattedSchema = JSON.stringify(combined); fs.writeFileSync(outfile, formattedSchema); } module.exports = { diff --git a/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js.flow b/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js.flow index 108edf99d81319..2b946c29a928d4 100644 --- a/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js.flow +++ b/packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js.flow @@ -96,7 +96,7 @@ function combineSchemasInFileListAndWriteToFile( exclude: ?RegExp, ): void { const combined = combineSchemasInFileList(fileList, platform, exclude); - const formattedSchema = JSON.stringify(combined, null, 2); + const formattedSchema = JSON.stringify(combined); fs.writeFileSync(outfile, formattedSchema); } diff --git a/packages/react-native-codegen/lib/cli/combine/combine-schemas-cli.js b/packages/react-native-codegen/lib/cli/combine/combine-schemas-cli.js index 0f392a3751ecbd..42903bfa7e240d 100644 --- a/packages/react-native-codegen/lib/cli/combine/combine-schemas-cli.js +++ b/packages/react-native-codegen/lib/cli/combine/combine-schemas-cli.js @@ -46,6 +46,7 @@ for (const file of schemaFiles) { const schema = JSON.parse(fs.readFileSync(file, 'utf8')); if (schema.modules) { for (const specName in schema.modules) { + var _module$excludedPlatf; const module = schema.modules[specName]; if (modules[specName]) { assert.deepEqual( @@ -54,11 +55,20 @@ for (const file of schemaFiles) { `App contained two specs with the same file name '${specName}'. Schemas: ${specNameToFile[specName]}, ${file}. Please rename one of the specs.`, ); } - if ( - module.excludedPlatforms && - module.excludedPlatforms.indexOf(platform) >= 0 - ) { - continue; + const excludedPlatforms = + (_module$excludedPlatf = module.excludedPlatforms) === null || + _module$excludedPlatf === void 0 + ? void 0 + : _module$excludedPlatf.map(excludedPlatform => + excludedPlatform.toLowerCase(), + ); + if (excludedPlatforms != null) { + const cxxOnlyModule = + excludedPlatforms.includes('ios') && + excludedPlatforms.includes('android'); + if (!cxxOnlyModule && excludedPlatforms.includes(platform)) { + continue; + } } modules[specName] = module; specNameToFile[specName] = file; @@ -67,11 +77,7 @@ for (const file of schemaFiles) { } fs.writeFileSync( output, - JSON.stringify( - { - modules, - }, - null, - 2, - ), + JSON.stringify({ + modules, + }), ); diff --git a/packages/react-native-codegen/lib/cli/combine/combine-schemas-cli.js.flow b/packages/react-native-codegen/lib/cli/combine/combine-schemas-cli.js.flow index f7a7b6f6ef0a28..ce8334b199f23f 100644 --- a/packages/react-native-codegen/lib/cli/combine/combine-schemas-cli.js.flow +++ b/packages/react-native-codegen/lib/cli/combine/combine-schemas-cli.js.flow @@ -71,11 +71,18 @@ for (const file of schemaFiles) { ); } - if ( - module.excludedPlatforms && - module.excludedPlatforms.indexOf(platform) >= 0 - ) { - continue; + const excludedPlatforms = module.excludedPlatforms?.map( + excludedPlatform => excludedPlatform.toLowerCase(), + ); + + if (excludedPlatforms != null) { + const cxxOnlyModule = + excludedPlatforms.includes('ios') && + excludedPlatforms.includes('android'); + + if (!cxxOnlyModule && excludedPlatforms.includes(platform)) { + continue; + } } modules[specName] = module; @@ -84,4 +91,4 @@ for (const file of schemaFiles) { } } -fs.writeFileSync(output, JSON.stringify({modules}, null, 2)); +fs.writeFileSync(output, JSON.stringify({modules})); diff --git a/packages/react-native-codegen/lib/cli/generators/generate-all.js b/packages/react-native-codegen/lib/cli/generators/generate-all.js index 5cee549d4b249b..dbe115910c5805 100644 --- a/packages/react-native-codegen/lib/cli/generators/generate-all.js +++ b/packages/react-native-codegen/lib/cli/generators/generate-all.js @@ -16,7 +16,6 @@ const RNCodegen = require('../../generators/RNCodegen.js'); const fs = require('fs'); -const mkdirp = require('mkdirp'); const args = process.argv.slice(2); if (args.length < 3) { throw new Error( @@ -34,7 +33,9 @@ const schemaText = fs.readFileSync(schemaPath, 'utf-8'); if (schemaText == null) { throw new Error(`Can't find schema at ${schemaPath}`); } -mkdirp.sync(outputDirectory); +fs.mkdirSync(outputDirectory, { + recursive: true, +}); let schema; try { schema = JSON.parse(schemaText); diff --git a/packages/react-native-codegen/lib/cli/generators/generate-all.js.flow b/packages/react-native-codegen/lib/cli/generators/generate-all.js.flow index 46d20751020885..2f4482be45e52c 100644 --- a/packages/react-native-codegen/lib/cli/generators/generate-all.js.flow +++ b/packages/react-native-codegen/lib/cli/generators/generate-all.js.flow @@ -16,7 +16,6 @@ const RNCodegen = require('../../generators/RNCodegen.js'); const fs = require('fs'); -const mkdirp = require('mkdirp'); const args = process.argv.slice(2); if (args.length < 3) { @@ -39,7 +38,7 @@ if (schemaText == null) { throw new Error(`Can't find schema at ${schemaPath}`); } -mkdirp.sync(outputDirectory); +fs.mkdirSync(outputDirectory, {recursive: true}); let schema; try { diff --git a/packages/react-native-codegen/lib/generators/__test_fixtures__/fixtures.js b/packages/react-native-codegen/lib/generators/__test_fixtures__/fixtures.js index 03d20b08e97699..edbff2dcc28831 100644 --- a/packages/react-native-codegen/lib/generators/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/lib/generators/__test_fixtures__/fixtures.js @@ -51,6 +51,9 @@ const SCHEMA_WITH_TM_AND_FC = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { diff --git a/packages/react-native-codegen/lib/generators/__test_fixtures__/fixtures.js.flow b/packages/react-native-codegen/lib/generators/__test_fixtures__/fixtures.js.flow index d0ddfeaff730d5..b3d5788df030de 100644 --- a/packages/react-native-codegen/lib/generators/__test_fixtures__/fixtures.js.flow +++ b/packages/react-native-codegen/lib/generators/__test_fixtures__/fixtures.js.flow @@ -53,6 +53,9 @@ const SCHEMA_WITH_TM_AND_FC: SchemaType = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { diff --git a/packages/react-native-codegen/lib/generators/components/CppHelpers.js b/packages/react-native-codegen/lib/generators/components/CppHelpers.js index 21baaaee20bec4..e15ec4dff4fa44 100644 --- a/packages/react-native-codegen/lib/generators/components/CppHelpers.js +++ b/packages/react-native-codegen/lib/generators/components/CppHelpers.js @@ -44,7 +44,7 @@ function getCppArrayTypeForAnnotation(typeElement, structParts) { case 'Int32TypeAnnotation': case 'MixedTypeAnnotation': return `std::vector<${getCppTypeForAnnotation(typeElement.type)}>`; - case 'StringEnumTypeAnnotation': + case 'StringLiteralUnionTypeAnnotation': case 'ObjectTypeAnnotation': if (!structParts) { throw new Error( diff --git a/packages/react-native-codegen/lib/generators/components/CppHelpers.js.flow b/packages/react-native-codegen/lib/generators/components/CppHelpers.js.flow index a208a840e9fa27..944304c62051a2 100644 --- a/packages/react-native-codegen/lib/generators/components/CppHelpers.js.flow +++ b/packages/react-native-codegen/lib/generators/components/CppHelpers.js.flow @@ -61,7 +61,7 @@ function getCppArrayTypeForAnnotation( case 'Int32TypeAnnotation': case 'MixedTypeAnnotation': return `std::vector<${getCppTypeForAnnotation(typeElement.type)}>`; - case 'StringEnumTypeAnnotation': + case 'StringLiteralUnionTypeAnnotation': case 'ObjectTypeAnnotation': if (!structParts) { throw new Error( diff --git a/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterCpp.js b/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterCpp.js index 3cf4edbcf4f0d3..2904d01f2e2c54 100644 --- a/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterCpp.js +++ b/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterCpp.js @@ -71,7 +71,7 @@ function generateSetter( ) { const eventChain = usingEvent ? `$event.${[...propertyParts, propertyName].join('.')}` - : [propertyParts, propertyName].join('.'); + : [...propertyParts, propertyName].join('.'); return `${variableName}.setProperty(runtime, "${propertyName}", ${valueMapper( eventChain, )});`; @@ -121,7 +121,7 @@ function generateArraySetter( ) { const eventChain = usingEvent ? `$event.${[...propertyParts, propertyName].join('.')}` - : [propertyParts, propertyName].join('.'); + : [...propertyParts, propertyName].join('.'); const indexVar = `${propertyName}Index`; const innerLoopVar = `${propertyName}Value`; return ` @@ -170,7 +170,7 @@ function handleArrayElementType( loopLocalVariable, val => `jsi::valueFromDynamic(runtime, ${val})`, ); - case 'StringEnumTypeAnnotation': + case 'StringLiteralUnionTypeAnnotation': return setValueAtIndex( propertyName, indexVariable, @@ -280,7 +280,7 @@ function generateSetters( usingEvent, prop => `jsi::valueFromDynamic(runtime, ${prop})`, ); - case 'StringEnumTypeAnnotation': + case 'StringLiteralUnionTypeAnnotation': return generateSetter( parentPropertyName, eventProperty.name, diff --git a/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterCpp.js.flow b/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterCpp.js.flow index 2f2a829d19955d..da3ef604b2d2e9 100644 --- a/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterCpp.js.flow +++ b/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterCpp.js.flow @@ -104,7 +104,7 @@ function generateSetter( ) { const eventChain = usingEvent ? `$event.${[...propertyParts, propertyName].join('.')}` - : [propertyParts, propertyName].join('.'); + : [...propertyParts, propertyName].join('.'); return `${variableName}.setProperty(runtime, "${propertyName}", ${valueMapper( eventChain, )});`; @@ -157,7 +157,7 @@ function generateArraySetter( ): string { const eventChain = usingEvent ? `$event.${[...propertyParts, propertyName].join('.')}` - : [propertyParts, propertyName].join('.'); + : [...propertyParts, propertyName].join('.'); const indexVar = `${propertyName}Index`; const innerLoopVar = `${propertyName}Value`; return ` @@ -207,7 +207,7 @@ function handleArrayElementType( loopLocalVariable, val => `jsi::valueFromDynamic(runtime, ${val})`, ); - case 'StringEnumTypeAnnotation': + case 'StringLiteralUnionTypeAnnotation': return setValueAtIndex( propertyName, indexVariable, @@ -320,7 +320,7 @@ function generateSetters( usingEvent, prop => `jsi::valueFromDynamic(runtime, ${prop})`, ); - case 'StringEnumTypeAnnotation': + case 'StringLiteralUnionTypeAnnotation': return generateSetter( parentPropertyName, eventProperty.name, diff --git a/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterH.js b/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterH.js index 41a8d4ed377291..579b1ef5ca65f2 100644 --- a/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterH.js +++ b/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterH.js @@ -77,7 +77,7 @@ function getNativeTypeFromAnnotation(componentName, eventProperty, nameParts) { case 'FloatTypeAnnotation': case 'MixedTypeAnnotation': return getCppTypeForAnnotation(type); - case 'StringEnumTypeAnnotation': + case 'StringLiteralUnionTypeAnnotation': case 'ObjectTypeAnnotation': return generateEventStructName([...nameParts, eventProperty.name]); case 'ArrayTypeAnnotation': @@ -130,8 +130,12 @@ function handleGenerateStructForArray( nameParts.concat([name]), nullthrows(elementType.properties), ); - } else if (elementType.type === 'StringEnumTypeAnnotation') { - generateEnum(structs, elementType.options, nameParts.concat([name])); + } else if (elementType.type === 'StringLiteralUnionTypeAnnotation') { + generateEnum( + structs, + elementType.types.map(literal => literal.value), + nameParts.concat([name]), + ); } else if (elementType.type === 'ArrayTypeAnnotation') { handleGenerateStructForArray( structs, @@ -182,8 +186,12 @@ function generateStruct(structs, componentName, nameParts, properties) { nullthrows(typeAnnotation.properties), ); return; - case 'StringEnumTypeAnnotation': - generateEnum(structs, typeAnnotation.options, nameParts.concat([name])); + case 'StringLiteralUnionTypeAnnotation': + generateEnum( + structs, + typeAnnotation.types.map(literal => literal.value), + nameParts.concat([name]), + ); return; default: typeAnnotation.type; diff --git a/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterH.js.flow b/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterH.js.flow index 0e753e24d2c544..c9a945d12e617b 100644 --- a/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterH.js.flow +++ b/packages/react-native-codegen/lib/generators/components/GenerateEventEmitterH.js.flow @@ -128,7 +128,7 @@ function getNativeTypeFromAnnotation( case 'FloatTypeAnnotation': case 'MixedTypeAnnotation': return getCppTypeForAnnotation(type); - case 'StringEnumTypeAnnotation': + case 'StringLiteralUnionTypeAnnotation': case 'ObjectTypeAnnotation': return generateEventStructName([...nameParts, eventProperty.name]); case 'ArrayTypeAnnotation': @@ -188,8 +188,12 @@ function handleGenerateStructForArray( nameParts.concat([name]), nullthrows(elementType.properties), ); - } else if (elementType.type === 'StringEnumTypeAnnotation') { - generateEnum(structs, elementType.options, nameParts.concat([name])); + } else if (elementType.type === 'StringLiteralUnionTypeAnnotation') { + generateEnum( + structs, + elementType.types.map(literal => literal.value), + nameParts.concat([name]), + ); } else if (elementType.type === 'ArrayTypeAnnotation') { handleGenerateStructForArray( structs, @@ -247,8 +251,12 @@ function generateStruct( nullthrows(typeAnnotation.properties), ); return; - case 'StringEnumTypeAnnotation': - generateEnum(structs, typeAnnotation.options, nameParts.concat([name])); + case 'StringLiteralUnionTypeAnnotation': + generateEnum( + structs, + typeAnnotation.types.map(literal => literal.value), + nameParts.concat([name]), + ); return; default: (typeAnnotation.type: empty); diff --git a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaDelegate.js b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaDelegate.js index 21119466548071..d1808654e4905a 100644 --- a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaDelegate.js +++ b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaDelegate.js @@ -38,7 +38,7 @@ package ${packageName}; ${imports} -public class ${className} & ${interfaceClassName}> extends BaseViewManagerDelegate { +public class ${className} & ${interfaceClassName}> extends BaseViewManagerDelegate { public ${className}(U viewManager) { super(viewManager); } @@ -225,7 +225,8 @@ function getDelegateImports(component) { } imports.add('import androidx.annotation.Nullable;'); imports.add('import com.facebook.react.uimanager.BaseViewManagerDelegate;'); - imports.add('import com.facebook.react.uimanager.BaseViewManagerInterface;'); + imports.add('import com.facebook.react.uimanager.BaseViewManager;'); + imports.add('import com.facebook.react.uimanager.LayoutShadowNode;'); return imports; } function generateMethods(propsString, commandsString) { diff --git a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaDelegate.js.flow b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaDelegate.js.flow index a758d69ecc3fb8..b49330a46bebe2 100644 --- a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaDelegate.js.flow +++ b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaDelegate.js.flow @@ -55,7 +55,7 @@ package ${packageName}; ${imports} -public class ${className} & ${interfaceClassName}> extends BaseViewManagerDelegate { +public class ${className} & ${interfaceClassName}> extends BaseViewManagerDelegate { public ${className}(U viewManager) { super(viewManager); } @@ -272,7 +272,8 @@ function getDelegateImports(component: ComponentShape) { } imports.add('import androidx.annotation.Nullable;'); imports.add('import com.facebook.react.uimanager.BaseViewManagerDelegate;'); - imports.add('import com.facebook.react.uimanager.BaseViewManagerInterface;'); + imports.add('import com.facebook.react.uimanager.BaseViewManager;'); + imports.add('import com.facebook.react.uimanager.LayoutShadowNode;'); return imports; } diff --git a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaInterface.js b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaInterface.js index 3779a355a9839d..db58c28b8ca73e 100644 --- a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaInterface.js +++ b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaInterface.js @@ -36,7 +36,7 @@ package ${packageName}; ${imports} -public interface ${className} { +public interface ${className} extends ViewManagerWithGeneratedInterface { ${methods} } `; diff --git a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaInterface.js.flow b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaInterface.js.flow index 28888b203e245b..66ab2267d84fdd 100644 --- a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaInterface.js.flow +++ b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaInterface.js.flow @@ -52,7 +52,7 @@ package ${packageName}; ${imports} -public interface ${className} { +public interface ${className} extends ViewManagerWithGeneratedInterface { ${methods} } `; diff --git a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaPojo/PojoCollector.js.flow b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaPojo/PojoCollector.js.flow index 6f5e19e88fd243..94ca45d8da9ba9 100644 --- a/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaPojo/PojoCollector.js.flow +++ b/packages/react-native-codegen/lib/generators/components/GeneratePropsJavaPojo/PojoCollector.js.flow @@ -11,8 +11,8 @@ 'use strict'; import type { - ArrayTypeAnnotation, BooleanTypeAnnotation, + ComponentArrayTypeAnnotation, DoubleTypeAnnotation, FloatTypeAnnotation, Int32TypeAnnotation, @@ -111,8 +111,10 @@ class PojoCollector { } case 'ArrayTypeAnnotation': { const arrayTypeAnnotation = typeAnnotation; - const elementType: $PropertyType = - arrayTypeAnnotation.elementType; + const elementType: $PropertyType< + ComponentArrayTypeAnnotation, + 'elementType', + > = arrayTypeAnnotation.elementType; const pojoElementType = (() => { switch (elementType.type) { diff --git a/packages/react-native-codegen/lib/generators/components/JavaHelpers.js b/packages/react-native-codegen/lib/generators/components/JavaHelpers.js index 61526fc1801cfc..abb2b9da81c5d7 100644 --- a/packages/react-native-codegen/lib/generators/components/JavaHelpers.js +++ b/packages/react-native-codegen/lib/generators/components/JavaHelpers.js @@ -28,6 +28,11 @@ function toSafeJavaString(input, shouldUpperCaseFirst) { } function getImports(component, type) { const imports = new Set(); + if (type === 'interface') { + imports.add( + 'import com.facebook.react.uimanager.ViewManagerWithGeneratedInterface;', + ); + } component.extendsProps.forEach(extendProps => { switch (extendProps.type) { case 'ReactNativeBuiltInType': @@ -77,6 +82,7 @@ function getImports(component, type) { component.props.forEach(prop => { const typeAnnotation = prop.typeAnnotation; if (typeAnnotation.type === 'ReservedPropTypeAnnotation') { + // $FlowFixMe[incompatible-call] addImportsForNativeName(typeAnnotation.name); } if (typeAnnotation.type === 'ArrayTypeAnnotation') { diff --git a/packages/react-native-codegen/lib/generators/components/JavaHelpers.js.flow b/packages/react-native-codegen/lib/generators/components/JavaHelpers.js.flow index 9b835bb6f91820..90166b9a24dc14 100644 --- a/packages/react-native-codegen/lib/generators/components/JavaHelpers.js.flow +++ b/packages/react-native-codegen/lib/generators/components/JavaHelpers.js.flow @@ -43,6 +43,12 @@ function getImports( ): Set { const imports: Set = new Set(); + if (type === 'interface') { + imports.add( + 'import com.facebook.react.uimanager.ViewManagerWithGeneratedInterface;', + ); + } + component.extendsProps.forEach(extendProps => { switch (extendProps.type) { case 'ReactNativeBuiltInType': @@ -66,12 +72,7 @@ function getImports( | 'EdgeInsetsPrimitive' | 'ImageSourcePrimitive' | 'PointPrimitive' - | 'DimensionPrimitive' - | $TEMPORARY$string<'ColorPrimitive'> - | $TEMPORARY$string<'EdgeInsetsPrimitive'> - | $TEMPORARY$string<'ImageSourcePrimitive'> - | $TEMPORARY$string<'PointPrimitive'> - | $TEMPORARY$string<'DimensionPrimitive'>, + | 'DimensionPrimitive', ) { switch (name) { case 'ColorPrimitive': @@ -107,6 +108,7 @@ function getImports( const typeAnnotation = prop.typeAnnotation; if (typeAnnotation.type === 'ReservedPropTypeAnnotation') { + // $FlowFixMe[incompatible-call] addImportsForNativeName(typeAnnotation.name); } diff --git a/packages/react-native-codegen/lib/generators/components/__test_fixtures__/fixtures.js b/packages/react-native-codegen/lib/generators/components/__test_fixtures__/fixtures.js index e61cd063b453d4..67721249032b07 100644 --- a/packages/react-native-codegen/lib/generators/components/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/lib/generators/components/__test_fixtures__/fixtures.js @@ -1241,8 +1241,17 @@ const EVENT_PROPS = { typeAnnotation: { type: 'ArrayTypeAnnotation', elementType: { - type: 'StringEnumTypeAnnotation', - options: ['YES', 'NO'], + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'YES', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'NO', + }, + ], }, }, }, @@ -1342,8 +1351,17 @@ const EVENT_PROPS = { name: 'orientation', optional: false, typeAnnotation: { - type: 'StringEnumTypeAnnotation', - options: ['landscape', 'portrait'], + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'landscape', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'portrait', + }, + ], }, }, ], @@ -1647,6 +1665,16 @@ const COMMANDS = { type: 'BooleanTypeAnnotation', }, }, + { + name: 'locations', + optional: false, + typeAnnotation: { + type: 'ArrayTypeAnnotation', + elementType: { + type: 'MixedTypeAnnotation', + }, + }, + }, ], returnTypeAnnotation: { type: 'VoidTypeAnnotation', diff --git a/packages/react-native-codegen/lib/generators/components/__test_fixtures__/fixtures.js.flow b/packages/react-native-codegen/lib/generators/components/__test_fixtures__/fixtures.js.flow index 9121c8ad6878ee..3eb0c2d8ce2f82 100644 --- a/packages/react-native-codegen/lib/generators/components/__test_fixtures__/fixtures.js.flow +++ b/packages/react-native-codegen/lib/generators/components/__test_fixtures__/fixtures.js.flow @@ -1263,8 +1263,17 @@ const EVENT_PROPS: SchemaType = { typeAnnotation: { type: 'ArrayTypeAnnotation', elementType: { - type: 'StringEnumTypeAnnotation', - options: ['YES', 'NO'], + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'YES', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'NO', + }, + ], }, }, }, @@ -1364,8 +1373,17 @@ const EVENT_PROPS: SchemaType = { name: 'orientation', optional: false, typeAnnotation: { - type: 'StringEnumTypeAnnotation', - options: ['landscape', 'portrait'], + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'landscape', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'portrait', + }, + ], }, }, ], @@ -1675,6 +1693,16 @@ const COMMANDS: SchemaType = { type: 'BooleanTypeAnnotation', }, }, + { + name: 'locations', + optional: false, + typeAnnotation: { + type: 'ArrayTypeAnnotation', + elementType: { + type: 'MixedTypeAnnotation', + }, + }, + }, ], returnTypeAnnotation: { type: 'VoidTypeAnnotation', diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleCpp.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleCpp.js index fdc9f15fab298a..817793fe6b10c9 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleCpp.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleCpp.js @@ -193,6 +193,10 @@ function serializeArg(moduleName, arg, index, resolveAlias, enumMap) { } case 'StringTypeAnnotation': 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 'EnumDeclaration': @@ -214,6 +218,8 @@ function serializeArg(moduleName, arg, index, resolveAlias, enumMap) { return wrap(val => `${val}.asNumber()`); case 'Int32TypeAnnotation': return wrap(val => `${val}.asNumber()`); + case 'NumberLiteralTypeAnnotation': + return wrap(val => `${val}.asNumber()`); case 'ArrayTypeAnnotation': return wrap(val => `${val}.asObject(rt).asArray(rt)`); case 'FunctionTypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleCpp.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleCpp.js.flow index f0e5ac171b95bd..3c4e9b4fe9f9ee 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleCpp.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleCpp.js.flow @@ -161,6 +161,10 @@ function serializeArg( } case 'StringTypeAnnotation': 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 'EnumDeclaration': @@ -182,6 +186,8 @@ function serializeArg( return wrap(val => `${val}.asNumber()`); case 'Int32TypeAnnotation': return wrap(val => `${val}.asNumber()`); + case 'NumberLiteralTypeAnnotation': + return wrap(val => `${val}.asNumber()`); case 'ArrayTypeAnnotation': return wrap(val => `${val}.asObject(rt).asArray(rt)`); case 'FunctionTypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleH.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleH.js index 13232a10502cba..14c19d75a73d8e 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleH.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleH.js @@ -122,8 +122,12 @@ const ModuleSpecClassDeclarationTemplate = ({ return `template class JSI_EXPORT ${hasteModuleName}CxxSpec : public TurboModule { public: - jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override { - return delegate_.get(rt, propName); + jsi::Value create(jsi::Runtime &rt, const jsi::PropNameID &propName) override { + return delegate_.create(rt, propName); + } + + std::vector getPropertyNames(jsi::Runtime& runtime) override { + return delegate_.getPropertyNames(runtime); } static constexpr std::string_view kModuleName = "${moduleName}"; @@ -209,8 +213,14 @@ function translatePrimitiveJSTypeToCpp( return 'void'; case 'StringTypeAnnotation': 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': + return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -419,12 +429,17 @@ struct Bridging<${enumName}> { } };`; }; +function getMemberValueAppearance(member) { + if (member.type === 'StringLiteralTypeAnnotation') { + return `"${member.value}"`; + } else { + return member.value; + } +} function generateEnum(hasteModuleName, origEnumName, members, memberType) { const enumName = getEnumName(hasteModuleName, origEnumName); const nativeEnumMemberType = memberType === 'StringTypeAnnotation' ? 'std::string' : 'int32_t'; - const getMemberValueAppearance = value => - memberType === 'StringTypeAnnotation' ? `"${value}"` : `${value}`; const fromCases = members .map( diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleH.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleH.js.flow index 37fb00b2e54e27..76aa271ffca277 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleH.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleH.js.flow @@ -16,7 +16,7 @@ import type { import type { NativeModuleAliasMap, NativeModuleEnumMap, - NativeModuleEnumMembers, + NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleEventEmitterShape, NativeModuleFunctionTypeAnnotation, @@ -76,8 +76,12 @@ const ModuleSpecClassDeclarationTemplate = ({ return `template class JSI_EXPORT ${hasteModuleName}CxxSpec : public TurboModule { public: - jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override { - return delegate_.get(rt, propName); + jsi::Value create(jsi::Runtime &rt, const jsi::PropNameID &propName) override { + return delegate_.create(rt, propName); + } + + std::vector getPropertyNames(jsi::Runtime& runtime) override { + return delegate_.getPropertyNames(runtime); } static constexpr std::string_view kModuleName = "${moduleName}"; @@ -169,8 +173,14 @@ function translatePrimitiveJSTypeToCpp( return 'void'; case 'StringTypeAnnotation': 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': + return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': @@ -396,10 +406,18 @@ struct Bridging<${enumName}> { };`; }; +function getMemberValueAppearance(member: NativeModuleEnumMember['value']) { + if (member.type === 'StringLiteralTypeAnnotation') { + return `"${member.value}"`; + } else { + return member.value; + } +} + function generateEnum( hasteModuleName: string, origEnumName: string, - members: NativeModuleEnumMembers, + members: $ReadOnlyArray, memberType: NativeModuleEnumMemberType, ): string { const enumName = getEnumName(hasteModuleName, origEnumName); @@ -407,9 +425,6 @@ function generateEnum( const nativeEnumMemberType: NativeEnumMemberValueType = memberType === 'StringTypeAnnotation' ? 'std::string' : 'int32_t'; - const getMemberValueAppearance = (value: string | number) => - memberType === 'StringTypeAnnotation' ? `"${value}"` : `${value}`; - const fromCases = members .map( diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleJavaSpec.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleJavaSpec.js index 051e63597b4f53..dd83d8d728fd39 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleJavaSpec.js @@ -162,10 +162,16 @@ function MethodTemplate(config) { )})${methodClosing}`; } function translateEventEmitterTypeToJavaType(eventEmitter, imports) { - switch (eventEmitter.typeAnnotation.typeAnnotation.type) { + const type = eventEmitter.typeAnnotation.typeAnnotation.type; + switch (type) { case 'StringTypeAnnotation': return 'String'; + case 'StringLiteralTypeAnnotation': + return 'String'; + case 'StringLiteralUnionTypeAnnotation': + return 'String'; case 'NumberTypeAnnotation': + case 'NumberLiteralTypeAnnotation': case 'FloatTypeAnnotation': case 'DoubleTypeAnnotation': case 'Int32TypeAnnotation': @@ -180,7 +186,16 @@ function translateEventEmitterTypeToJavaType(eventEmitter, imports) { case 'ArrayTypeAnnotation': imports.add('com.facebook.react.bridge.ReadableArray'); return 'ReadableArray'; + case 'DoubleTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + case 'VoidTypeAnnotation': + // TODO: Add support for these types + throw new Error( + `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, + ); default: + type; throw new Error( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); @@ -219,8 +234,14 @@ function translateFunctionParamToJavaType( } case 'StringTypeAnnotation': return wrapOptional('String', isRequired); + case 'StringLiteralTypeAnnotation': + return wrapOptional('String', isRequired); + case 'StringLiteralUnionTypeAnnotation': + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': @@ -305,8 +326,14 @@ function translateFunctionReturnTypeToJavaType( return 'void'; case 'StringTypeAnnotation': return wrapOptional('String', isRequired); + case 'StringLiteralTypeAnnotation': + return wrapOptional('String', isRequired); + case 'StringLiteralUnionTypeAnnotation': + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': @@ -380,6 +407,8 @@ function getFalsyReturnStatementFromReturnType( return ''; case 'NumberTypeAnnotation': return nullable ? 'return null;' : 'return 0;'; + case 'NumberLiteralTypeAnnotation': + return nullable ? 'return null;' : 'return 0;'; case 'FloatTypeAnnotation': return nullable ? 'return null;' : 'return 0.0;'; case 'DoubleTypeAnnotation': @@ -412,6 +441,10 @@ function getFalsyReturnStatementFromReturnType( } 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/lib/generators/modules/GenerateModuleJavaSpec.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleJavaSpec.js.flow index 35a257b13c8699..8ff63e8c994a82 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleJavaSpec.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleJavaSpec.js.flow @@ -127,10 +127,16 @@ function translateEventEmitterTypeToJavaType( eventEmitter: NativeModuleEventEmitterShape, imports: Set, ): string { - switch (eventEmitter.typeAnnotation.typeAnnotation.type) { + const type = eventEmitter.typeAnnotation.typeAnnotation.type; + switch (type) { case 'StringTypeAnnotation': return 'String'; + case 'StringLiteralTypeAnnotation': + return 'String'; + case 'StringLiteralUnionTypeAnnotation': + return 'String'; case 'NumberTypeAnnotation': + case 'NumberLiteralTypeAnnotation': case 'FloatTypeAnnotation': case 'DoubleTypeAnnotation': case 'Int32TypeAnnotation': @@ -145,7 +151,16 @@ function translateEventEmitterTypeToJavaType( case 'ArrayTypeAnnotation': imports.add('com.facebook.react.bridge.ReadableArray'); return 'ReadableArray'; + case 'DoubleTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + case 'VoidTypeAnnotation': + // TODO: Add support for these types + throw new Error( + `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, + ); default: + (type: empty); throw new Error( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); @@ -183,8 +198,14 @@ function translateFunctionParamToJavaType( } case 'StringTypeAnnotation': return wrapOptional('String', isRequired); + case 'StringLiteralTypeAnnotation': + return wrapOptional('String', isRequired); + case 'StringLiteralUnionTypeAnnotation': + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': @@ -273,8 +294,14 @@ function translateFunctionReturnTypeToJavaType( return 'void'; case 'StringTypeAnnotation': return wrapOptional('String', isRequired); + case 'StringLiteralTypeAnnotation': + return wrapOptional('String', isRequired); + case 'StringLiteralUnionTypeAnnotation': + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': return wrapOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': @@ -351,6 +378,8 @@ function getFalsyReturnStatementFromReturnType( return ''; case 'NumberTypeAnnotation': return nullable ? 'return null;' : 'return 0;'; + case 'NumberLiteralTypeAnnotation': + return nullable ? 'return null;' : 'return 0;'; case 'FloatTypeAnnotation': return nullable ? 'return null;' : 'return 0.0;'; case 'DoubleTypeAnnotation': @@ -383,6 +412,10 @@ function getFalsyReturnStatementFromReturnType( } 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/lib/generators/modules/GenerateModuleJniCpp.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleJniCpp.js index db1fc1111bad02..9bd5a6b757fa01 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleJniCpp.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleJniCpp.js @@ -180,6 +180,10 @@ function translateReturnTypeToKind(nullableTypeAnnotation, resolveAlias) { return 'VoidKind'; case 'StringTypeAnnotation': return 'StringKind'; + case 'StringLiteralTypeAnnotation': + return 'StringKind'; + case 'StringLiteralUnionTypeAnnotation': + return 'StringKind'; case 'BooleanTypeAnnotation': return 'BooleanKind'; case 'EnumDeclaration': @@ -208,6 +212,8 @@ function translateReturnTypeToKind(nullableTypeAnnotation, resolveAlias) { } case 'NumberTypeAnnotation': return 'NumberKind'; + case 'NumberLiteralTypeAnnotation': + return 'NumberKind'; case 'DoubleTypeAnnotation': return 'NumberKind'; case 'FloatTypeAnnotation': @@ -254,6 +260,10 @@ function translateParamTypeToJniType(param, resolveAlias) { } case 'StringTypeAnnotation': 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 'EnumDeclaration': @@ -282,6 +292,8 @@ function translateParamTypeToJniType(param, resolveAlias) { } case 'NumberTypeAnnotation': return !isRequired ? 'Ljava/lang/Double;' : 'D'; + case 'NumberLiteralTypeAnnotation': + return !isRequired ? 'Ljava/lang/Double;' : 'D'; case 'DoubleTypeAnnotation': return !isRequired ? 'Ljava/lang/Double;' : 'D'; case 'FloatTypeAnnotation': @@ -327,6 +339,10 @@ function translateReturnTypeToJniType(nullableTypeAnnotation, resolveAlias) { return 'V'; case 'StringTypeAnnotation': 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 'EnumDeclaration': @@ -355,6 +371,8 @@ function translateReturnTypeToJniType(nullableTypeAnnotation, resolveAlias) { } case 'NumberTypeAnnotation': return nullable ? 'Ljava/lang/Double;' : 'D'; + case 'NumberLiteralTypeAnnotation': + return nullable ? 'Ljava/lang/Double;' : 'D'; case 'DoubleTypeAnnotation': return nullable ? 'Ljava/lang/Double;' : 'D'; case 'FloatTypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleJniCpp.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleJniCpp.js.flow index 92e642118d1b01..7d6de0a4f54e3f 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleJniCpp.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleJniCpp.js.flow @@ -165,6 +165,10 @@ function translateReturnTypeToKind( return 'VoidKind'; case 'StringTypeAnnotation': return 'StringKind'; + case 'StringLiteralTypeAnnotation': + return 'StringKind'; + case 'StringLiteralUnionTypeAnnotation': + return 'StringKind'; case 'BooleanTypeAnnotation': return 'BooleanKind'; case 'EnumDeclaration': @@ -193,6 +197,8 @@ function translateReturnTypeToKind( } case 'NumberTypeAnnotation': return 'NumberKind'; + case 'NumberLiteralTypeAnnotation': + return 'NumberKind'; case 'DoubleTypeAnnotation': return 'NumberKind'; case 'FloatTypeAnnotation': @@ -244,6 +250,10 @@ function translateParamTypeToJniType( } case 'StringTypeAnnotation': 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 'EnumDeclaration': @@ -272,6 +282,8 @@ function translateParamTypeToJniType( } case 'NumberTypeAnnotation': return !isRequired ? 'Ljava/lang/Double;' : 'D'; + case 'NumberLiteralTypeAnnotation': + return !isRequired ? 'Ljava/lang/Double;' : 'D'; case 'DoubleTypeAnnotation': return !isRequired ? 'Ljava/lang/Double;' : 'D'; case 'FloatTypeAnnotation': @@ -320,6 +332,10 @@ function translateReturnTypeToJniType( return 'V'; case 'StringTypeAnnotation': 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 'EnumDeclaration': @@ -348,6 +364,8 @@ function translateReturnTypeToJniType( } case 'NumberTypeAnnotation': return nullable ? 'Ljava/lang/Double;' : 'D'; + case 'NumberLiteralTypeAnnotation': + return nullable ? 'Ljava/lang/Double;' : 'D'; case 'DoubleTypeAnnotation': return nullable ? 'Ljava/lang/Double;' : 'D'; case 'FloatTypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/StructCollector.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/StructCollector.js index e3a57948d19604..071cd42a942cd6 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/StructCollector.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/StructCollector.js @@ -191,7 +191,28 @@ class StructCollector { case 'MixedTypeAnnotation': throw new Error('Mixed types are unsupported in structs'); case 'UnionTypeAnnotation': - throw new Error('Union types are unsupported in structs'); + switch (typeAnnotation.memberType) { + case 'StringTypeAnnotation': + return wrapNullable(nullable, { + type: 'StringTypeAnnotation', + }); + case 'NumberTypeAnnotation': + return wrapNullable(nullable, { + type: 'NumberTypeAnnotation', + }); + case 'ObjectTypeAnnotation': + // 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', + }); + default: + typeAnnotation.memberType; + throw new Error( + 'Union types are unsupported in structs' + + JSON.stringify(typeAnnotation), + ); + } default: { return wrapNullable(nullable, typeAnnotation); } diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/StructCollector.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/StructCollector.js.flow index 69a59a453897d4..db243d9f3024a2 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/StructCollector.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/StructCollector.js.flow @@ -11,20 +11,23 @@ 'use strict'; import type { - NativeModuleArrayTypeAnnotation, - NativeModuleBaseTypeAnnotation, BooleanTypeAnnotation, DoubleTypeAnnotation, - NativeModuleEnumDeclaration, FloatTypeAnnotation, - NativeModuleGenericObjectTypeAnnotation, Int32TypeAnnotation, + NativeModuleArrayTypeAnnotation, + NativeModuleBaseTypeAnnotation, + NativeModuleEnumDeclaration, + NativeModuleGenericObjectTypeAnnotation, NativeModuleNumberTypeAnnotation, NativeModuleObjectTypeAnnotation, - StringTypeAnnotation, NativeModuleTypeAliasTypeAnnotation, Nullable, + NumberLiteralTypeAnnotation, ReservedTypeAnnotation, + StringLiteralTypeAnnotation, + StringLiteralUnionTypeAnnotation, + StringTypeAnnotation, } from '../../../CodegenSchema'; import type {AliasResolver} from '../Utils'; @@ -58,7 +61,10 @@ export type StructProperty = $ReadOnly<{ export type StructTypeAnnotation = | StringTypeAnnotation + | StringLiteralTypeAnnotation + | StringLiteralUnionTypeAnnotation | NativeModuleNumberTypeAnnotation + | NumberLiteralTypeAnnotation | Int32TypeAnnotation | DoubleTypeAnnotation | FloatTypeAnnotation @@ -121,7 +127,28 @@ class StructCollector { case 'MixedTypeAnnotation': throw new Error('Mixed types are unsupported in structs'); case 'UnionTypeAnnotation': - throw new Error('Union types are unsupported in structs'); + switch (typeAnnotation.memberType) { + case 'StringTypeAnnotation': + return wrapNullable(nullable, { + type: 'StringTypeAnnotation', + }); + case 'NumberTypeAnnotation': + return wrapNullable(nullable, { + type: 'NumberTypeAnnotation', + }); + case 'ObjectTypeAnnotation': + // 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', + }); + default: + (typeAnnotation.memberType: empty); + throw new Error( + 'Union types are unsupported in structs' + + JSON.stringify(typeAnnotation), + ); + } default: { return wrapNullable(nullable, typeAnnotation); } diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js index b7a427f10bb79e..6f2b15e48b4c77 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js @@ -157,8 +157,14 @@ function toObjCType( } case 'StringTypeAnnotation': return 'NSString *'; + case 'StringLiteralTypeAnnotation': + return 'NSString *'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *'; case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'Int32TypeAnnotation': @@ -235,8 +241,14 @@ function toObjCValue( } case 'StringTypeAnnotation': return value; + case 'StringLiteralTypeAnnotation': + return value; + case 'StringLiteralUnionTypeAnnotation': + return value; case 'NumberTypeAnnotation': return wrapPrimitive('double'); + case 'NumberLiteralTypeAnnotation': + return wrapPrimitive('double'); case 'FloatTypeAnnotation': return wrapPrimitive('double'); case 'Int32TypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js.flow index 8e8054c583e1fa..9cee0665e8f396 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js.flow @@ -94,8 +94,14 @@ function toObjCType( } case 'StringTypeAnnotation': return 'NSString *'; + case 'StringLiteralTypeAnnotation': + return 'NSString *'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *'; case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'Int32TypeAnnotation': @@ -173,8 +179,14 @@ function toObjCValue( } case 'StringTypeAnnotation': return value; + case 'StringLiteralTypeAnnotation': + return value; + case 'StringLiteralUnionTypeAnnotation': + return value; case 'NumberTypeAnnotation': return wrapPrimitive('double'); + case 'NumberLiteralTypeAnnotation': + return wrapPrimitive('double'); case 'FloatTypeAnnotation': return wrapPrimitive('double'); case 'Int32TypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js index 6db4b7ba030db9..0aa2aaabccc75e 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js @@ -145,8 +145,14 @@ function toObjCType( } case 'StringTypeAnnotation': return 'NSString *'; + case 'StringLiteralTypeAnnotation': + return 'NSString *'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *'; case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'Int32TypeAnnotation': @@ -224,8 +230,14 @@ function toObjCValue( } case 'StringTypeAnnotation': return RCTBridgingTo('String'); + case 'StringLiteralTypeAnnotation': + return RCTBridgingTo('String'); + case 'StringLiteralUnionTypeAnnotation': + return RCTBridgingTo('String'); case 'NumberTypeAnnotation': return RCTBridgingTo('Double'); + case 'NumberLiteralTypeAnnotation': + return RCTBridgingTo('Double'); case 'FloatTypeAnnotation': return RCTBridgingTo('Double'); case 'Int32TypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js.flow index 3e53c456572fa5..9fca06510ad933 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js.flow @@ -85,8 +85,14 @@ function toObjCType( } case 'StringTypeAnnotation': return 'NSString *'; + case 'StringLiteralTypeAnnotation': + return 'NSString *'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *'; case 'NumberTypeAnnotation': return wrapCxxOptional('double', isRequired); + case 'NumberLiteralTypeAnnotation': + return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': return wrapCxxOptional('double', isRequired); case 'Int32TypeAnnotation': @@ -163,8 +169,14 @@ function toObjCValue( } case 'StringTypeAnnotation': return RCTBridgingTo('String'); + case 'StringLiteralTypeAnnotation': + return RCTBridgingTo('String'); + case 'StringLiteralUnionTypeAnnotation': + return RCTBridgingTo('String'); case 'NumberTypeAnnotation': return RCTBridgingTo('Double'); + case 'NumberLiteralTypeAnnotation': + return RCTBridgingTo('Double'); case 'FloatTypeAnnotation': return RCTBridgingTo('Double'); case 'Int32TypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js index 38a8f40d76ae5f..cc04f87d657ec2 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js @@ -11,10 +11,16 @@ const _require = require('../../Utils'), toPascalCase = _require.toPascalCase; function getEventEmitterTypeObjCType(eventEmitter) { - switch (eventEmitter.typeAnnotation.typeAnnotation.type) { + const type = eventEmitter.typeAnnotation.typeAnnotation.type; + switch (type) { case 'StringTypeAnnotation': return 'NSString *_Nonnull'; + case 'StringLiteralTypeAnnotation': + return 'NSString *_Nonnull'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *_Nonnull'; case 'NumberTypeAnnotation': + case 'NumberLiteralTypeAnnotation': return 'NSNumber *_Nonnull'; case 'BooleanTypeAnnotation': return 'BOOL'; @@ -24,7 +30,16 @@ function getEventEmitterTypeObjCType(eventEmitter) { return 'NSDictionary *'; case 'ArrayTypeAnnotation': return 'NSArray> *'; + case 'DoubleTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + case 'VoidTypeAnnotation': + // TODO: Add support for these types + throw new Error( + `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, + ); default: + type; throw new Error( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js.flow index b486563cc3a61c..fd1afff066fbf7 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeEventEmitter.js.flow @@ -15,10 +15,17 @@ const {toPascalCase} = require('../../Utils'); function getEventEmitterTypeObjCType( eventEmitter: NativeModuleEventEmitterShape, ): string { - switch (eventEmitter.typeAnnotation.typeAnnotation.type) { + const type = eventEmitter.typeAnnotation.typeAnnotation.type; + + switch (type) { case 'StringTypeAnnotation': return 'NSString *_Nonnull'; + case 'StringLiteralTypeAnnotation': + return 'NSString *_Nonnull'; + case 'StringLiteralUnionTypeAnnotation': + return 'NSString *_Nonnull'; case 'NumberTypeAnnotation': + case 'NumberLiteralTypeAnnotation': return 'NSNumber *_Nonnull'; case 'BooleanTypeAnnotation': return 'BOOL'; @@ -28,7 +35,16 @@ function getEventEmitterTypeObjCType( return 'NSDictionary *'; case 'ArrayTypeAnnotation': return 'NSArray> *'; + case 'DoubleTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + case 'VoidTypeAnnotation': + // TODO: Add support for these types + throw new Error( + `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, + ); default: + (type: empty); throw new Error( `Unsupported eventType for ${eventEmitter.name}. Found: ${eventEmitter.typeAnnotation.typeAnnotation.type}`, ); diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js index f4b5b040ac0c14..ad1342c945acfa 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js @@ -297,8 +297,14 @@ function getParamObjCType( } case 'StringTypeAnnotation': return notStruct(wrapOptional('NSString *', !nullable)); + case 'StringLiteralTypeAnnotation': + return notStruct(wrapOptional('NSString *', !nullable)); + case 'StringLiteralUnionTypeAnnotation': + return notStruct(wrapOptional('NSString *', !nullable)); case 'NumberTypeAnnotation': return notStruct(isRequired ? 'double' : 'NSNumber *'); + case 'NumberLiteralTypeAnnotation': + return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'FloatTypeAnnotation': return notStruct(isRequired ? 'float' : 'NSNumber *'); case 'DoubleTypeAnnotation': @@ -367,8 +373,18 @@ function getReturnObjCType(methodName, nullableTypeAnnotation) { // TODO: Can NSString * returns not be _Nullable? // In the legacy codegen, we don't surround NSSTring * with _Nullable return wrapOptional('NSString *', isRequired); + case 'StringLiteralTypeAnnotation': + // 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': + return wrapOptional('NSNumber *', isRequired); case 'FloatTypeAnnotation': return wrapOptional('NSNumber *', isRequired); case 'DoubleTypeAnnotation': @@ -431,8 +447,14 @@ function getReturnJSType(methodName, nullableTypeAnnotation) { return 'NumberKind'; case 'StringTypeAnnotation': return 'StringKind'; + case 'StringLiteralTypeAnnotation': + return 'StringKind'; + case 'StringLiteralUnionTypeAnnotation': + return 'StringKind'; case 'NumberTypeAnnotation': return 'NumberKind'; + case 'NumberLiteralTypeAnnotation': + return 'NumberKind'; case 'FloatTypeAnnotation': return 'NumberKind'; case 'DoubleTypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js.flow b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js.flow index c06354ebc3e63a..8e0a56472ff8b3 100644 --- a/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js.flow @@ -257,8 +257,14 @@ function getParamObjCType( } case 'StringTypeAnnotation': return notStruct(wrapOptional('NSString *', !nullable)); + case 'StringLiteralTypeAnnotation': + return notStruct(wrapOptional('NSString *', !nullable)); + case 'StringLiteralUnionTypeAnnotation': + return notStruct(wrapOptional('NSString *', !nullable)); case 'NumberTypeAnnotation': return notStruct(isRequired ? 'double' : 'NSNumber *'); + case 'NumberLiteralTypeAnnotation': + return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'FloatTypeAnnotation': return notStruct(isRequired ? 'float' : 'NSNumber *'); case 'DoubleTypeAnnotation': @@ -330,8 +336,18 @@ 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 'StringLiteralTypeAnnotation': + // 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': + return wrapOptional('NSNumber *', isRequired); case 'FloatTypeAnnotation': return wrapOptional('NSNumber *', isRequired); case 'DoubleTypeAnnotation': @@ -396,8 +412,14 @@ function getReturnJSType( return 'NumberKind'; case 'StringTypeAnnotation': return 'StringKind'; + case 'StringLiteralTypeAnnotation': + return 'StringKind'; + case 'StringLiteralUnionTypeAnnotation': + return 'StringKind'; case 'NumberTypeAnnotation': return 'NumberKind'; + case 'NumberLiteralTypeAnnotation': + return 'NumberKind'; case 'FloatTypeAnnotation': return 'NumberKind'; case 'DoubleTypeAnnotation': diff --git a/packages/react-native-codegen/lib/generators/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/lib/generators/modules/__test_fixtures__/fixtures.js index e655fc8ed73f02..7f78615b7818ff 100644 --- a/packages/react-native-codegen/lib/generators/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/lib/generators/modules/__test_fixtures__/fixtures.js @@ -159,11 +159,17 @@ const SIMPLE_NATIVE_MODULES = { members: [ { name: 'ONE', - value: '1', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 1, + }, }, { name: 'TWO', - value: '2', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 2, + }, }, ], }, @@ -174,15 +180,24 @@ const SIMPLE_NATIVE_MODULES = { members: [ { name: 'POINT_ZERO', - value: '0.0', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 0.0, + }, }, { name: 'POINT_ONE', - value: '0.1', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 0.1, + }, }, { name: 'POINT_TWO', - value: '0.2', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 0.2, + }, }, ], }, @@ -193,11 +208,17 @@ const SIMPLE_NATIVE_MODULES = { members: [ { name: 'HELLO', - value: 'hello', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'hello', + }, }, { name: 'GoodBye', - value: 'goodbye', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'goodbye', + }, }, ], }, @@ -448,6 +469,9 @@ const SIMPLE_NATIVE_MODULES = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -467,6 +491,9 @@ const SIMPLE_NATIVE_MODULES = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -485,7 +512,9 @@ const SIMPLE_NATIVE_MODULES = { typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { - type: 'StringTypeAnnotation', + name: 'StringEnum', + type: 'EnumDeclaration', + memberType: 'StringTypeAnnotation', }, params: [ { @@ -1356,6 +1385,9 @@ const REAL_MODULE_EXAMPLE = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -1376,6 +1408,9 @@ const REAL_MODULE_EXAMPLE = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -1402,6 +1437,9 @@ const REAL_MODULE_EXAMPLE = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -1690,6 +1728,7 @@ const REAL_MODULE_EXAMPLE = { }; const CXX_ONLY_NATIVE_MODULES = { modules: { + // $FlowFixMe[incompatible-type] NativeSampleTurboModule: { type: 'NativeModule', aliasMap: { @@ -1898,11 +1937,17 @@ const CXX_ONLY_NATIVE_MODULES = { members: [ { name: 'IA', - value: '23', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 23, + }, }, { name: 'IB', - value: '42', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 42, + }, }, ], }, @@ -1913,11 +1958,17 @@ const CXX_ONLY_NATIVE_MODULES = { members: [ { name: 'FA', - value: '1.23', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 1.23, + }, }, { name: 'FB', - value: '4.56', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 4.56, + }, }, ], }, @@ -1928,11 +1979,17 @@ const CXX_ONLY_NATIVE_MODULES = { members: [ { name: 'NA', - value: 'NA', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'NA', + }, }, { name: 'NB', - value: 'NB', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'NB', + }, }, ], }, @@ -1943,11 +2000,17 @@ const CXX_ONLY_NATIVE_MODULES = { members: [ { name: 'SA', - value: 's---a', + value: { + type: 'StringLiteralTypeAnnotation', + value: 's---a', + }, }, { name: 'SB', - value: 's---b', + value: { + type: 'StringLiteralTypeAnnotation', + value: 's---b', + }, }, ], }, @@ -2291,6 +2354,23 @@ const CXX_ONLY_NATIVE_MODULES = { memberType: 'StringTypeAnnotation', }, }, + { + name: 'y-literal', + optional: false, + typeAnnotation: { + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], + }, + }, { name: 'z', optional: false, @@ -2547,6 +2627,131 @@ const SAMPLE_WITH_UPPERCASE_NAME = { }, }, }; +const UNION_MODULE = { + modules: { + NativeSampleTurboModule: { + type: 'NativeModule', + aliasMap: {}, + enumMap: {}, + spec: { + eventEmitters: [], + methods: [ + { + name: 'getUnion', + optional: false, + typeAnnotation: { + type: 'FunctionTypeAnnotation', + returnTypeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'ObjectTypeAnnotation', + }, + params: [ + { + name: 'chooseInt', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'NumberTypeAnnotation', + }, + }, + { + name: 'chooseFloat', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'NumberTypeAnnotation', + }, + }, + { + name: 'chooseObject', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'ObjectTypeAnnotation', + }, + }, + { + name: 'chooseString', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'StringTypeAnnotation', + }, + }, + { + name: 'chooseStringLiteral', + optional: false, + typeAnnotation: { + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], + }, + }, + ], + }, + }, + ], + }, + moduleName: 'SampleTurboModule', + }, + }, +}; +const STRING_LITERALS = { + modules: { + NativeSampleTurboModule: { + type: 'NativeModule', + aliasMap: {}, + enumMap: {}, + spec: { + eventEmitters: [ + { + name: 'literalEvent', + optional: false, + typeAnnotation: { + type: 'EventEmitterTypeAnnotation', + typeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Event', + }, + }, + }, + ], + methods: [ + { + name: 'getStringLiteral', + optional: false, + typeAnnotation: { + type: 'FunctionTypeAnnotation', + returnTypeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Return', + }, + params: [ + { + name: 'literalParam', + optional: false, + typeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Param', + }, + }, + ], + }, + }, + ], + }, + moduleName: 'SampleTurboModule', + }, + }, +}; module.exports = { complex_objects: COMPLEX_OBJECTS, two_modules_different_files: TWO_MODULES_DIFFERENT_FILES, @@ -2557,4 +2762,6 @@ module.exports = { real_module_example: REAL_MODULE_EXAMPLE, cxx_only_native_modules: CXX_ONLY_NATIVE_MODULES, SampleWithUppercaseName: SAMPLE_WITH_UPPERCASE_NAME, + union_module: UNION_MODULE, + string_literals: STRING_LITERALS, }; diff --git a/packages/react-native-codegen/lib/generators/modules/__test_fixtures__/fixtures.js.flow b/packages/react-native-codegen/lib/generators/modules/__test_fixtures__/fixtures.js.flow index f7a65fe0a5621e..8428418cf9e1c6 100644 --- a/packages/react-native-codegen/lib/generators/modules/__test_fixtures__/fixtures.js.flow +++ b/packages/react-native-codegen/lib/generators/modules/__test_fixtures__/fixtures.js.flow @@ -163,11 +163,17 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { members: [ { name: 'ONE', - value: '1', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 1, + }, }, { name: 'TWO', - value: '2', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 2, + }, }, ], }, @@ -178,15 +184,24 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { members: [ { name: 'POINT_ZERO', - value: '0.0', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 0.0, + }, }, { name: 'POINT_ONE', - value: '0.1', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 0.1, + }, }, { name: 'POINT_TWO', - value: '0.2', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 0.2, + }, }, ], }, @@ -197,11 +212,17 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { members: [ { name: 'HELLO', - value: 'hello', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'hello', + }, }, { name: 'GoodBye', - value: 'goodbye', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'goodbye', + }, }, ], }, @@ -453,6 +474,9 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -472,6 +496,9 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -490,7 +517,9 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { - type: 'StringTypeAnnotation', + name: 'StringEnum', + type: 'EnumDeclaration', + memberType: 'StringTypeAnnotation', }, params: [ { @@ -1365,6 +1394,9 @@ const REAL_MODULE_EXAMPLE: SchemaType = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -1385,6 +1417,9 @@ const REAL_MODULE_EXAMPLE: SchemaType = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -1411,6 +1446,9 @@ const REAL_MODULE_EXAMPLE: SchemaType = { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }, params: [ { @@ -1700,6 +1738,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { const CXX_ONLY_NATIVE_MODULES: SchemaType = { modules: { + // $FlowFixMe[incompatible-type] NativeSampleTurboModule: { type: 'NativeModule', aliasMap: { @@ -1908,11 +1947,17 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = { members: [ { name: 'IA', - value: '23', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 23, + }, }, { name: 'IB', - value: '42', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 42, + }, }, ], }, @@ -1923,11 +1968,17 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = { members: [ { name: 'FA', - value: '1.23', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 1.23, + }, }, { name: 'FB', - value: '4.56', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 4.56, + }, }, ], }, @@ -1938,11 +1989,17 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = { members: [ { name: 'NA', - value: 'NA', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'NA', + }, }, { name: 'NB', - value: 'NB', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'NB', + }, }, ], }, @@ -1953,11 +2010,17 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = { members: [ { name: 'SA', - value: 's---a', + value: { + type: 'StringLiteralTypeAnnotation', + value: 's---a', + }, }, { name: 'SB', - value: 's---b', + value: { + type: 'StringLiteralTypeAnnotation', + value: 's---b', + }, }, ], }, @@ -2301,6 +2364,23 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = { memberType: 'StringTypeAnnotation', }, }, + { + name: 'y-literal', + optional: false, + typeAnnotation: { + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], + }, + }, { name: 'z', optional: false, @@ -2560,6 +2640,133 @@ const SAMPLE_WITH_UPPERCASE_NAME: SchemaType = { }, }; +const UNION_MODULE: SchemaType = { + modules: { + NativeSampleTurboModule: { + type: 'NativeModule', + aliasMap: {}, + enumMap: {}, + spec: { + eventEmitters: [], + methods: [ + { + name: 'getUnion', + optional: false, + typeAnnotation: { + type: 'FunctionTypeAnnotation', + returnTypeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'ObjectTypeAnnotation', + }, + params: [ + { + name: 'chooseInt', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'NumberTypeAnnotation', + }, + }, + { + name: 'chooseFloat', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'NumberTypeAnnotation', + }, + }, + { + name: 'chooseObject', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'ObjectTypeAnnotation', + }, + }, + { + name: 'chooseString', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'StringTypeAnnotation', + }, + }, + { + name: 'chooseStringLiteral', + optional: false, + typeAnnotation: { + type: 'StringLiteralUnionTypeAnnotation', + types: [ + { + type: 'StringLiteralTypeAnnotation', + value: 'foo', + }, + { + type: 'StringLiteralTypeAnnotation', + value: 'bar', + }, + ], + }, + }, + ], + }, + }, + ], + }, + moduleName: 'SampleTurboModule', + }, + }, +}; + +const STRING_LITERALS: SchemaType = { + modules: { + NativeSampleTurboModule: { + type: 'NativeModule', + aliasMap: {}, + enumMap: {}, + spec: { + eventEmitters: [ + { + name: 'literalEvent', + optional: false, + typeAnnotation: { + type: 'EventEmitterTypeAnnotation', + typeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Event', + }, + }, + }, + ], + methods: [ + { + name: 'getStringLiteral', + optional: false, + typeAnnotation: { + type: 'FunctionTypeAnnotation', + returnTypeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Return', + }, + params: [ + { + name: 'literalParam', + optional: false, + typeAnnotation: { + type: 'StringLiteralTypeAnnotation', + value: 'A String Literal Param', + }, + }, + ], + }, + }, + ], + }, + moduleName: 'SampleTurboModule', + }, + }, +}; + module.exports = { complex_objects: COMPLEX_OBJECTS, two_modules_different_files: TWO_MODULES_DIFFERENT_FILES, @@ -2570,4 +2777,6 @@ module.exports = { real_module_example: REAL_MODULE_EXAMPLE, cxx_only_native_modules: CXX_ONLY_NATIVE_MODULES, SampleWithUppercaseName: SAMPLE_WITH_UPPERCASE_NAME, + union_module: UNION_MODULE, + string_literals: STRING_LITERALS, }; diff --git a/packages/react-native-codegen/lib/parsers/errors.js b/packages/react-native-codegen/lib/parsers/errors.js index 6f588336017535..489e32c069a50c 100644 --- a/packages/react-native-codegen/lib/parsers/errors.js +++ b/packages/react-native-codegen/lib/parsers/errors.js @@ -183,6 +183,13 @@ class UnsupportedObjectPropertyTypeAnnotationParserError extends ParserError { super(nativeModuleName, propertyAST, message); } } +class UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError extends ParserError { + constructor(nativeModuleName, propertyAST) { + let message = + "'ObjectTypeAnnotation' cannot contain both an indexer and properties."; + super(nativeModuleName, propertyAST, message); + } +} class UnsupportedObjectPropertyValueTypeAnnotationParserError extends ParserError { constructor( nativeModuleName, @@ -357,6 +364,7 @@ module.exports = { UnsupportedModuleEventEmitterPropertyParserError, UnsupportedModulePropertyParserError, UnsupportedObjectPropertyTypeAnnotationParserError, + UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError, UnsupportedObjectPropertyValueTypeAnnotationParserError, UnsupportedObjectDirectRecursivePropertyParserError, UnusedModuleInterfaceParserError, diff --git a/packages/react-native-codegen/lib/parsers/errors.js.flow b/packages/react-native-codegen/lib/parsers/errors.js.flow index 91b9c827b24575..5fa37f58afba55 100644 --- a/packages/react-native-codegen/lib/parsers/errors.js.flow +++ b/packages/react-native-codegen/lib/parsers/errors.js.flow @@ -237,6 +237,15 @@ class UnsupportedObjectPropertyTypeAnnotationParserError extends ParserError { } } +class UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError extends ParserError { + constructor(nativeModuleName: string, propertyAST: $FlowFixMe) { + let message = + "'ObjectTypeAnnotation' cannot contain both an indexer and properties."; + + super(nativeModuleName, propertyAST, message); + } +} + class UnsupportedObjectPropertyValueTypeAnnotationParserError extends ParserError { constructor( nativeModuleName: string, @@ -455,6 +464,7 @@ module.exports = { UnsupportedModuleEventEmitterPropertyParserError, UnsupportedModulePropertyParserError, UnsupportedObjectPropertyTypeAnnotationParserError, + UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError, UnsupportedObjectPropertyValueTypeAnnotationParserError, UnsupportedObjectDirectRecursivePropertyParserError, UnusedModuleInterfaceParserError, diff --git a/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/failures.js b/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/failures.js index 24f2cb1e033ba5..d924bd02398bc9 100644 --- a/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/failures.js @@ -38,7 +38,7 @@ export type ModuleProps = $ReadOnly<{| |}>; export const Commands = codegenNativeCommands<{ - +hotspotUpdate: (ref: React.Ref<'RCTView'>, x: Int32, y: Int32) => void, + +hotspotUpdate: (ref: React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void, }>({ supportedCommands: ['hotspotUpdate'], }); @@ -68,7 +68,7 @@ import type {ViewProps} from 'ViewPropTypes'; import type {HostComponent} from 'react-native'; interface NativeCommands { - +hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void; + +hotspotUpdate: (viewRef: React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void; } export type ModuleProps = $ReadOnly<{| @@ -145,7 +145,7 @@ import type {ViewProps} from 'ViewPropTypes'; import type {HostComponent} from 'react-native'; interface NativeCommands { - +hotspotUpdate: (viewRef: ?React.Ref<'RCTView'>, x: Int32, y: Int32) => void; + +hotspotUpdate: (viewRef: ?React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void; } export type ModuleProps = $ReadOnly<{| @@ -182,9 +182,9 @@ import type {ViewProps} from 'ViewPropTypes'; import type {HostComponent} from 'react-native'; interface NativeCommands { - +hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void; + +hotspotUpdate: (viewRef: React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void; +scrollTo: ( - viewRef: React.Ref<'RCTView'>, + viewRef: React.RefSetter<'RCTView'>, y: Int32, animated: boolean, ) => void; @@ -224,9 +224,9 @@ import type {ViewProps} from 'ViewPropTypes'; import type {HostComponent} from 'react-native'; interface NativeCommands { - +hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void; + +hotspotUpdate: (viewRef: React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void; +scrollTo: ( - viewRef: React.Ref<'RCTView'>, + viewRef: React.RefSetter<'RCTView'>, y: Int32, animated: boolean, ) => void; diff --git a/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/failures.js.flow b/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/failures.js.flow index 1d7072636a48a8..aa1aecf3d2242c 100644 --- a/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/failures.js.flow +++ b/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/failures.js.flow @@ -38,7 +38,7 @@ export type ModuleProps = $ReadOnly<{| |}>; export const Commands = codegenNativeCommands<{ - +hotspotUpdate: (ref: React.Ref<'RCTView'>, x: Int32, y: Int32) => void, + +hotspotUpdate: (ref: React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void, }>({ supportedCommands: ['hotspotUpdate'], }); @@ -69,7 +69,7 @@ import type {ViewProps} from 'ViewPropTypes'; import type {HostComponent} from 'react-native'; interface NativeCommands { - +hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void; + +hotspotUpdate: (viewRef: React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void; } export type ModuleProps = $ReadOnly<{| @@ -148,7 +148,7 @@ import type {ViewProps} from 'ViewPropTypes'; import type {HostComponent} from 'react-native'; interface NativeCommands { - +hotspotUpdate: (viewRef: ?React.Ref<'RCTView'>, x: Int32, y: Int32) => void; + +hotspotUpdate: (viewRef: ?React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void; } export type ModuleProps = $ReadOnly<{| @@ -186,9 +186,9 @@ import type {ViewProps} from 'ViewPropTypes'; import type {HostComponent} from 'react-native'; interface NativeCommands { - +hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void; + +hotspotUpdate: (viewRef: React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void; +scrollTo: ( - viewRef: React.Ref<'RCTView'>, + viewRef: React.RefSetter<'RCTView'>, y: Int32, animated: boolean, ) => void; @@ -229,9 +229,9 @@ import type {ViewProps} from 'ViewPropTypes'; import type {HostComponent} from 'react-native'; interface NativeCommands { - +hotspotUpdate: (viewRef: React.Ref<'RCTView'>, x: Int32, y: Int32) => void; + +hotspotUpdate: (viewRef: React.RefSetter<'RCTView'>, x: Int32, y: Int32) => void; +scrollTo: ( - viewRef: React.Ref<'RCTView'>, + viewRef: React.RefSetter<'RCTView'>, y: Int32, animated: boolean, ) => void; diff --git a/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/fixtures.js b/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/fixtures.js index de29b12b6a598e..f2e9079692a688 100644 --- a/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/fixtures.js @@ -38,10 +38,10 @@ const EVENT_DEFINITION = ` int32_optional_value: ?Int32, int32_optional_both?: ?Int32, - enum_required: ('small' | 'large'), - enum_optional_key?: ('small' | 'large'), - enum_optional_value: ?('small' | 'large'), - enum_optional_both?: ?('small' | 'large'), + union_required: ('small' | 'large'), + union_optional_key?: ('small' | 'large'), + union_optional_value: ?('small' | 'large'), + union_optional_both?: ?('small' | 'large'), object_required: { boolean_required: boolean, @@ -110,10 +110,10 @@ const EVENT_DEFINITION = ` int32_array_optional_value: ?$ReadOnlyArray, int32_array_optional_both?: ?Int32[], - enum_array_required: $ReadOnlyArray<('small' | 'large')>, - enum_array_optional_key?: ('small' | 'large')[], - enum_array_optional_value: ?$ReadOnlyArray<('small' | 'large')>, - enum_array_optional_both?: ?('small' | 'large')[], + union_array_required: $ReadOnlyArray<('small' | 'large')>, + union_array_optional_key?: ('small' | 'large')[], + union_array_optional_value: ?$ReadOnlyArray<('small' | 'large')>, + union_array_optional_both?: ?('small' | 'large')[], object_array_required: $ReadOnlyArray<{ boolean_required: boolean, @@ -939,10 +939,18 @@ interface NativeCommands { z: Double, animated: boolean, ): void; + +arrayArgs: ( + viewRef: React.ElementRef, + booleanArray: $ReadOnlyArray, + stringArray: $ReadOnlyArray, + floatArray: $ReadOnlyArray, + intArray: $ReadOnlyArray, + doubleArray: $ReadOnlyArray, + ) => void; } export const Commands = codegenNativeCommands({ - supportedCommands: ['handleRootTag', 'hotspotUpdate', 'scrollTo'], + supportedCommands: ['handleRootTag', 'hotspotUpdate', 'scrollTo', 'arrayArgs'], }); export default (codegenNativeComponent( @@ -972,6 +980,10 @@ import type {HostComponent} from 'react-native'; export type Boolean = boolean; export type Int = Int32; export type Void = void; +export type Locations = { + x: number, + y: number, +} export type ModuleProps = $ReadOnly<{| ...ViewProps, @@ -993,6 +1005,7 @@ interface NativeCommands { overlayColorsReadOnly: $ReadOnlyArray, overlayColorsArray: Array, overlayColorsArrayAnnotation: string[], + overlayLocations: $ReadOnlyArray, ) => void; } diff --git a/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/fixtures.js.flow b/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/fixtures.js.flow index 7b93444feca642..67dff7280024a2 100644 --- a/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/fixtures.js.flow +++ b/packages/react-native-codegen/lib/parsers/flow/components/__test_fixtures__/fixtures.js.flow @@ -38,10 +38,10 @@ const EVENT_DEFINITION = ` int32_optional_value: ?Int32, int32_optional_both?: ?Int32, - enum_required: ('small' | 'large'), - enum_optional_key?: ('small' | 'large'), - enum_optional_value: ?('small' | 'large'), - enum_optional_both?: ?('small' | 'large'), + union_required: ('small' | 'large'), + union_optional_key?: ('small' | 'large'), + union_optional_value: ?('small' | 'large'), + union_optional_both?: ?('small' | 'large'), object_required: { boolean_required: boolean, @@ -110,10 +110,10 @@ const EVENT_DEFINITION = ` int32_array_optional_value: ?$ReadOnlyArray, int32_array_optional_both?: ?Int32[], - enum_array_required: $ReadOnlyArray<('small' | 'large')>, - enum_array_optional_key?: ('small' | 'large')[], - enum_array_optional_value: ?$ReadOnlyArray<('small' | 'large')>, - enum_array_optional_both?: ?('small' | 'large')[], + union_array_required: $ReadOnlyArray<('small' | 'large')>, + union_array_optional_key?: ('small' | 'large')[], + union_array_optional_value: ?$ReadOnlyArray<('small' | 'large')>, + union_array_optional_both?: ?('small' | 'large')[], object_array_required: $ReadOnlyArray<{ boolean_required: boolean, @@ -951,10 +951,18 @@ interface NativeCommands { z: Double, animated: boolean, ): void; + +arrayArgs: ( + viewRef: React.ElementRef, + booleanArray: $ReadOnlyArray, + stringArray: $ReadOnlyArray, + floatArray: $ReadOnlyArray, + intArray: $ReadOnlyArray, + doubleArray: $ReadOnlyArray, + ) => void; } export const Commands = codegenNativeCommands({ - supportedCommands: ['handleRootTag', 'hotspotUpdate', 'scrollTo'], + supportedCommands: ['handleRootTag', 'hotspotUpdate', 'scrollTo', 'arrayArgs'], }); export default (codegenNativeComponent( @@ -985,6 +993,10 @@ import type {HostComponent} from 'react-native'; export type Boolean = boolean; export type Int = Int32; export type Void = void; +export type Locations = { + x: number, + y: number, +} export type ModuleProps = $ReadOnly<{| ...ViewProps, @@ -1006,6 +1018,7 @@ interface NativeCommands { overlayColorsReadOnly: $ReadOnlyArray, overlayColorsArray: Array, overlayColorsArrayAnnotation: string[], + overlayLocations: $ReadOnlyArray, ) => void; } diff --git a/packages/react-native-codegen/lib/parsers/flow/components/commands.js b/packages/react-native-codegen/lib/parsers/flow/components/commands.js index 2681127cabf515..820ae44a7f2b33 100644 --- a/packages/react-native-codegen/lib/parsers/flow/components/commands.js +++ b/packages/react-native-codegen/lib/parsers/flow/components/commands.js @@ -81,19 +81,15 @@ function buildCommandSchema(property, types) { } returnType = { type: 'ArrayTypeAnnotation', - elementType: { - // TODO: T172453752 support complex type annotation for array element - type: paramValue.typeParameters.params[0].type, - }, + elementType: getCommandArrayElementTypeType( + paramValue.typeParameters.params[0], + ), }; break; case 'ArrayTypeAnnotation': returnType = { type: 'ArrayTypeAnnotation', - elementType: { - // TODO: T172453752 support complex type annotation for array element - type: paramValue.elementType.type, - }, + elementType: getCommandArrayElementTypeType(paramValue.elementType), }; break; default: @@ -120,6 +116,64 @@ function buildCommandSchema(property, types) { }, }; } +function getCommandArrayElementTypeType(inputType) { + var _inputType$id; + // TODO: T172453752 support more complex type annotation for array element + if (typeof inputType !== 'object') { + throw new Error('Expected an object'); + } + const type = + inputType === null || inputType === void 0 ? void 0 : inputType.type; + if (inputType == null || typeof type !== 'string') { + throw new Error('Command array element type must be a string'); + } + switch (type) { + case 'BooleanTypeAnnotation': + return { + type: 'BooleanTypeAnnotation', + }; + case 'StringTypeAnnotation': + return { + type: 'StringTypeAnnotation', + }; + case 'GenericTypeAnnotation': + const name = + typeof inputType.id === 'object' + ? (_inputType$id = inputType.id) === null || _inputType$id === void 0 + ? void 0 + : _inputType$id.name + : null; + if (typeof name !== 'string') { + throw new Error( + 'Expected GenericTypeAnnotation AST name to be a string', + ); + } + switch (name) { + case 'Int32': + return { + type: 'Int32TypeAnnotation', + }; + case 'Float': + return { + type: 'FloatTypeAnnotation', + }; + case 'Double': + return { + type: 'DoubleTypeAnnotation', + }; + default: + // This is not a great solution. This generally means its a type alias to another type + // like an object or union. Ideally we'd encode that in the schema so the compat-check can + // validate those deeper objects for breaking changes and the generators can do something smarter. + // As of now, the generators just create ReadableMap or (const NSArray *) which are untyped + return { + type: 'MixedTypeAnnotation', + }; + } + default: + throw new Error(`Unsupported array element type ${type}`); + } +} function getCommands(commandTypeAST, types) { return commandTypeAST .filter(property => property.type === 'ObjectTypeProperty') diff --git a/packages/react-native-codegen/lib/parsers/flow/components/commands.js.flow b/packages/react-native-codegen/lib/parsers/flow/components/commands.js.flow index c8ce0b157cbc5a..9587013661b527 100644 --- a/packages/react-native-codegen/lib/parsers/flow/components/commands.js.flow +++ b/packages/react-native-codegen/lib/parsers/flow/components/commands.js.flow @@ -13,6 +13,7 @@ import type { CommandParamTypeAnnotation, CommandTypeAnnotation, + ComponentCommandArrayTypeAnnotation, NamedShape, } from '../../../CodegenSchema.js'; import type {TypeDeclarationMap} from '../../utils'; @@ -109,19 +110,15 @@ function buildCommandSchema( } returnType = { type: 'ArrayTypeAnnotation', - elementType: { - // TODO: T172453752 support complex type annotation for array element - type: paramValue.typeParameters.params[0].type, - }, + elementType: getCommandArrayElementTypeType( + paramValue.typeParameters.params[0], + ), }; break; case 'ArrayTypeAnnotation': returnType = { type: 'ArrayTypeAnnotation', - elementType: { - // TODO: T172453752 support complex type annotation for array element - type: paramValue.elementType.type, - }, + elementType: getCommandArrayElementTypeType(paramValue.elementType), }; break; default: @@ -151,6 +148,66 @@ function buildCommandSchema( }; } +type Allowed = ComponentCommandArrayTypeAnnotation['elementType']; + +function getCommandArrayElementTypeType(inputType: mixed): Allowed { + // TODO: T172453752 support more complex type annotation for array element + if (typeof inputType !== 'object') { + throw new Error('Expected an object'); + } + + const type = inputType?.type; + + if (inputType == null || typeof type !== 'string') { + throw new Error('Command array element type must be a string'); + } + + switch (type) { + case 'BooleanTypeAnnotation': + return { + type: 'BooleanTypeAnnotation', + }; + case 'StringTypeAnnotation': + return { + type: 'StringTypeAnnotation', + }; + case 'GenericTypeAnnotation': + const name = typeof inputType.id === 'object' ? inputType.id?.name : null; + + if (typeof name !== 'string') { + throw new Error( + 'Expected GenericTypeAnnotation AST name to be a string', + ); + } + + switch (name) { + case 'Int32': + return { + type: 'Int32TypeAnnotation', + }; + case 'Float': + return { + type: 'FloatTypeAnnotation', + }; + case 'Double': + return { + type: 'DoubleTypeAnnotation', + }; + default: + // This is not a great solution. This generally means its a type alias to another type + // like an object or union. Ideally we'd encode that in the schema so the compat-check can + // validate those deeper objects for breaking changes and the generators can do something smarter. + // As of now, the generators just create ReadableMap or (const NSArray *) which are untyped + return { + type: 'MixedTypeAnnotation', + }; + } + + default: + throw new Error(`Unsupported array element type ${type}`); + } +} + function getCommands( commandTypeAST: $ReadOnlyArray, types: TypeDeclarationMap, diff --git a/packages/react-native-codegen/lib/parsers/flow/components/events.js b/packages/react-native-codegen/lib/parsers/flow/components/events.js index c0b82916220b89..6e3b854456696c 100644 --- a/packages/react-native-codegen/lib/parsers/flow/components/events.js +++ b/packages/react-native-codegen/lib/parsers/flow/components/events.js @@ -104,10 +104,11 @@ function extractArrayElementType(typeAnnotation, name, parser) { }; case 'UnionTypeAnnotation': return { - type: 'StringEnumTypeAnnotation', - options: typeAnnotation.types.map(option => - parser.getLiteralValue(option), - ), + type: 'StringLiteralUnionTypeAnnotation', + types: typeAnnotation.types.map(option => ({ + type: 'StringLiteralTypeAnnotation', + value: parser.getLiteralValue(option), + })), }; case 'UnsafeMixed': return { diff --git a/packages/react-native-codegen/lib/parsers/flow/components/events.js.flow b/packages/react-native-codegen/lib/parsers/flow/components/events.js.flow index 940706e086c3d2..ba900707aaae0e 100644 --- a/packages/react-native-codegen/lib/parsers/flow/components/events.js.flow +++ b/packages/react-native-codegen/lib/parsers/flow/components/events.js.flow @@ -115,10 +115,11 @@ function extractArrayElementType( }; case 'UnionTypeAnnotation': return { - type: 'StringEnumTypeAnnotation', - options: typeAnnotation.types.map(option => - parser.getLiteralValue(option), - ), + type: 'StringLiteralUnionTypeAnnotation', + types: typeAnnotation.types.map(option => ({ + type: 'StringLiteralTypeAnnotation', + value: parser.getLiteralValue(option), + })), }; case 'UnsafeMixed': return {type: 'MixedTypeAnnotation'}; diff --git a/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/failures.js b/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/failures.js index 59309dcc7a1e14..6b534b46305e40 100644 --- a/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/failures.js @@ -253,6 +253,61 @@ export interface Spec extends TurboModule { +getEnums: (a: SomeEnum) => string; } +export default TurboModuleRegistry.getEnforcing('MixedValuesEnumNativeModule'); +`; +const NUMERIC_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, + NEGATIVE = -1, + SUBFACTORIAL = !5, +} + +export interface Spec extends TurboModule { + +getEnums: (a: SomeEnum) => string; +} + +export default TurboModuleRegistry.getEnforcing('NumericValuesEnumNativeModule'); +`; +const MAP_WITH_EXTRA_KEYS_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'; + +type MapWithKey = { + [a: string]: ?string, + extra: string, +} + +export interface Spec extends TurboModule { + +getMap: (a: MapWithKey) => string; +} + export default TurboModuleRegistry.getEnforcing('MixedValuesEnumNativeModule'); `; module.exports = { @@ -266,4 +321,6 @@ module.exports = { TWO_NATIVE_EXTENDING_TURBO_MODULE, EMPTY_ENUM_NATIVE_MODULE, MIXED_VALUES_ENUM_NATIVE_MODULE, + NUMERIC_VALUES_ENUM_NATIVE_MODULE, + MAP_WITH_EXTRA_KEYS_NATIVE_MODULE, }; diff --git a/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/failures.js.flow b/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/failures.js.flow index 8734095f43f2d4..61d94da57e82a5 100644 --- a/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/failures.js.flow +++ b/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/failures.js.flow @@ -265,6 +265,63 @@ export interface Spec extends TurboModule { export default TurboModuleRegistry.getEnforcing('MixedValuesEnumNativeModule'); `; +const NUMERIC_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, + NEGATIVE = -1, + SUBFACTORIAL = !5, +} + +export interface Spec extends TurboModule { + +getEnums: (a: SomeEnum) => string; +} + +export default TurboModuleRegistry.getEnforcing('NumericValuesEnumNativeModule'); +`; + +const MAP_WITH_EXTRA_KEYS_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'; + +type MapWithKey = { + [a: string]: ?string, + extra: string, +} + +export interface Spec extends TurboModule { + +getMap: (a: MapWithKey) => string; +} + +export default TurboModuleRegistry.getEnforcing('MixedValuesEnumNativeModule'); +`; + module.exports = { NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT, NATIVE_MODULES_WITH_UNNAMED_PARAMS, @@ -276,4 +333,6 @@ module.exports = { TWO_NATIVE_EXTENDING_TURBO_MODULE, EMPTY_ENUM_NATIVE_MODULE, MIXED_VALUES_ENUM_NATIVE_MODULE, + NUMERIC_VALUES_ENUM_NATIVE_MODULE, + MAP_WITH_EXTRA_KEYS_NATIVE_MODULE, }; diff --git a/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/fixtures.js index 585596dc3967ee..0808473b6fe2a4 100644 --- a/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/fixtures.js @@ -123,8 +123,10 @@ import * as TurboModuleRegistry from '../TurboModuleRegistry'; export interface Spec extends TurboModule { +passBool?: (arg: boolean) => void; +passNumber: (arg: number) => void; + +passNumberLiteral: (arg: 4) => void; +passString: (arg: string) => void; +passStringish: (arg: Stringish) => void; + +passStringLiteral: (arg: 'A String Literal') => void; } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); @@ -158,6 +160,7 @@ export type ObjectAlias = {| label: string, truthy: boolean, |}; +export type PureObjectAlias = ObjectAlias; export type ReadOnlyAlias = $ReadOnly; export interface Spec extends TurboModule { @@ -167,6 +170,7 @@ export interface Spec extends TurboModule { +getArray: (a: Array) => {| a: B |}; +getStringFromAlias: (a: ObjectAlias) => string; +getStringFromNullableAlias: (a: ?ObjectAlias) => string; + +getStringFromPureAlias: (a: PureObjectAlias) => string; +getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string; +getStringFromNullableReadOnlyAlias: (a: ?ReadOnlyAlias) => string; } @@ -597,7 +601,34 @@ export interface Spec extends TurboModule { } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); +`; +const NATIVE_MODULE_WITH_UNION_RETURN_TYPES = ` +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport'; + +import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +getStringUnion: () => 'light' | 'dark'; + +setStringUnion: (strings: 'light' | 'dark') => void; + + +getNumberUnion: () => 1 | 2; + +setNumberUnion: (numbers: 1 | 2) => void; + + +getObjectUnion: () => {a: 1} | {b: 2}; + +setObjectUnion: (objects: {a: 1} | {b: 2}) => void; +} +export default (TurboModuleRegistry.get('SampleTurboModule'): ?Spec); `; const NATIVE_MODULE_WITH_EVENT_EMITTERS = ` /** @@ -726,6 +757,7 @@ export enum Quality { } export enum Resolution { + Corrupted = -1, Low = 720, High = 1080, } @@ -756,7 +788,7 @@ export type CustomDeviceEvent = { export interface Spec extends TurboModule { +getCallback: () => () => void; +getMixed: (arg: mixed) => mixed; - +getEnums: (quality: Quality, resolution?: Resolution, stringOptions: StringOptions) => string; + +getEnums: (quality: Quality, resolution?: Resolution, stringOptions: StringOptions) => Quality; +getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode; +getGraphNode: (arg: GraphNode) => GraphNode; +getMap: (arg: {[a: string]: ?number}) => {[b: string]: ?number}; @@ -826,6 +858,7 @@ module.exports = { NATIVE_MODULE_WITH_BASIC_PARAM_TYPES, NATIVE_MODULE_WITH_CALLBACK, NATIVE_MODULE_WITH_UNION, + NATIVE_MODULE_WITH_UNION_RETURN_TYPES, NATIVE_MODULE_WITH_EVENT_EMITTERS, EMPTY_NATIVE_MODULE, ANDROID_ONLY_NATIVE_MODULE, diff --git a/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/fixtures.js.flow b/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/fixtures.js.flow index a555772d09ecab..b381dcfac8775d 100644 --- a/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/fixtures.js.flow +++ b/packages/react-native-codegen/lib/parsers/flow/modules/__test_fixtures__/fixtures.js.flow @@ -126,8 +126,10 @@ import * as TurboModuleRegistry from '../TurboModuleRegistry'; export interface Spec extends TurboModule { +passBool?: (arg: boolean) => void; +passNumber: (arg: number) => void; + +passNumberLiteral: (arg: 4) => void; +passString: (arg: string) => void; +passStringish: (arg: Stringish) => void; + +passStringLiteral: (arg: 'A String Literal') => void; } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); @@ -162,6 +164,7 @@ export type ObjectAlias = {| label: string, truthy: boolean, |}; +export type PureObjectAlias = ObjectAlias; export type ReadOnlyAlias = $ReadOnly; export interface Spec extends TurboModule { @@ -171,6 +174,7 @@ export interface Spec extends TurboModule { +getArray: (a: Array) => {| a: B |}; +getStringFromAlias: (a: ObjectAlias) => string; +getStringFromNullableAlias: (a: ?ObjectAlias) => string; + +getStringFromPureAlias: (a: PureObjectAlias) => string; +getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string; +getStringFromNullableReadOnlyAlias: (a: ?ReadOnlyAlias) => string; } @@ -617,7 +621,35 @@ export interface Spec extends TurboModule { } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); +`; + +const NATIVE_MODULE_WITH_UNION_RETURN_TYPES = ` +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport'; + +import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +getStringUnion: () => 'light' | 'dark'; + +setStringUnion: (strings: 'light' | 'dark') => void; + + +getNumberUnion: () => 1 | 2; + +setNumberUnion: (numbers: 1 | 2) => void; + + +getObjectUnion: () => {a: 1} | {b: 2}; + +setObjectUnion: (objects: {a: 1} | {b: 2}) => void; +} +export default (TurboModuleRegistry.get('SampleTurboModule'): ?Spec); `; const NATIVE_MODULE_WITH_EVENT_EMITTERS = ` @@ -750,6 +782,7 @@ export enum Quality { } export enum Resolution { + Corrupted = -1, Low = 720, High = 1080, } @@ -780,7 +813,7 @@ export type CustomDeviceEvent = { export interface Spec extends TurboModule { +getCallback: () => () => void; +getMixed: (arg: mixed) => mixed; - +getEnums: (quality: Quality, resolution?: Resolution, stringOptions: StringOptions) => string; + +getEnums: (quality: Quality, resolution?: Resolution, stringOptions: StringOptions) => Quality; +getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode; +getGraphNode: (arg: GraphNode) => GraphNode; +getMap: (arg: {[a: string]: ?number}) => {[b: string]: ?number}; @@ -852,6 +885,7 @@ module.exports = { NATIVE_MODULE_WITH_BASIC_PARAM_TYPES, NATIVE_MODULE_WITH_CALLBACK, NATIVE_MODULE_WITH_UNION, + NATIVE_MODULE_WITH_UNION_RETURN_TYPES, NATIVE_MODULE_WITH_EVENT_EMITTERS, EMPTY_NATIVE_MODULE, ANDROID_ONLY_NATIVE_MODULE, diff --git a/packages/react-native-codegen/lib/parsers/flow/modules/index.js b/packages/react-native-codegen/lib/parsers/flow/modules/index.js index 32fb8ac2d9471c..137a6610efce15 100644 --- a/packages/react-native-codegen/lib/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/lib/parsers/flow/modules/index.js @@ -86,6 +86,8 @@ const _require = require('../../errors'), UnsupportedEnumDeclarationParserError = _require.UnsupportedEnumDeclarationParserError, UnsupportedGenericParserError = _require.UnsupportedGenericParserError, + UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError = + _require.UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError, UnsupportedTypeAnnotationParserError = _require.UnsupportedTypeAnnotationParserError; const _require2 = require('../../parsers-commons'), @@ -99,6 +101,7 @@ const _require3 = require('../../parsers-primitives'), emitCommonTypes = _require3.emitCommonTypes, emitDictionary = _require3.emitDictionary, emitFunction = _require3.emitFunction, + emitNumberLiteral = _require3.emitNumberLiteral, emitPromise = _require3.emitPromise, emitRootTag = _require3.emitRootTag, emitUnion = _require3.emitUnion, @@ -227,6 +230,12 @@ function translateTypeAnnotation( const indexers = typeAnnotation.indexers.filter( member => member.type === 'ObjectTypeIndexer', ); + if (indexers.length > 0 && typeAnnotation.properties.length > 0) { + throw new UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + ); + } if (indexers.length > 0) { // check the property type to prevent developers from using unsupported types // the return value from `translateTypeAnnotation` is unused @@ -292,11 +301,13 @@ function translateTypeAnnotation( case 'UnionTypeAnnotation': { return emitUnion(nullable, hasteModuleName, typeAnnotation, parser); } + case 'NumberLiteralTypeAnnotation': { + return emitNumberLiteral(nullable, typeAnnotation.value); + } case 'StringLiteralTypeAnnotation': { - // 'a' is a special case for 'a' | 'b' but the type name is different return wrapNullable(nullable, { - type: 'UnionTypeAnnotation', - memberType: 'StringTypeAnnotation', + type: 'StringLiteralTypeAnnotation', + value: typeAnnotation.value, }); } case 'EnumStringBody': diff --git a/packages/react-native-codegen/lib/parsers/flow/modules/index.js.flow b/packages/react-native-codegen/lib/parsers/flow/modules/index.js.flow index adfea71f5b1f09..501264ca3d6f87 100644 --- a/packages/react-native-codegen/lib/parsers/flow/modules/index.js.flow +++ b/packages/react-native-codegen/lib/parsers/flow/modules/index.js.flow @@ -24,6 +24,7 @@ import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils'; const { UnsupportedEnumDeclarationParserError, UnsupportedGenericParserError, + UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError, UnsupportedTypeAnnotationParserError, } = require('../../errors'); const { @@ -37,6 +38,7 @@ const { emitCommonTypes, emitDictionary, emitFunction, + emitNumberLiteral, emitPromise, emitRootTag, emitUnion, @@ -162,6 +164,14 @@ function translateTypeAnnotation( const indexers = typeAnnotation.indexers.filter( member => member.type === 'ObjectTypeIndexer', ); + + if (indexers.length > 0 && typeAnnotation.properties.length > 0) { + throw new UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + ); + } + if (indexers.length > 0) { // check the property type to prevent developers from using unsupported types // the return value from `translateTypeAnnotation` is unused @@ -234,11 +244,13 @@ function translateTypeAnnotation( case 'UnionTypeAnnotation': { return emitUnion(nullable, hasteModuleName, typeAnnotation, parser); } + case 'NumberLiteralTypeAnnotation': { + return emitNumberLiteral(nullable, typeAnnotation.value); + } case 'StringLiteralTypeAnnotation': { - // 'a' is a special case for 'a' | 'b' but the type name is different return wrapNullable(nullable, { - type: 'UnionTypeAnnotation', - memberType: 'StringTypeAnnotation', + type: 'StringLiteralTypeAnnotation', + value: typeAnnotation.value, }); } case 'EnumStringBody': diff --git a/packages/react-native-codegen/lib/parsers/flow/parser.js b/packages/react-native-codegen/lib/parsers/flow/parser.js index 5d3e0a8346074b..3f3c94ced7c697 100644 --- a/packages/react-native-codegen/lib/parsers/flow/parser.js +++ b/packages/react-native-codegen/lib/parsers/flow/parser.js @@ -124,6 +124,9 @@ class FlowParser { }; return [...new Set(membersTypes.map(remapLiteral))]; } + getStringLiteralUnionTypeAnnotationStringLiterals(membersTypes) { + return membersTypes.map(item => item.value); + } parseFile(filename) { const contents = fs.readFileSync(filename, 'utf8'); return this.parseString(contents, filename); @@ -207,16 +210,30 @@ class FlowParser { } parseEnumMembers(typeAnnotation) { return typeAnnotation.members.map(member => { - var _member$init$value, _member$init; + var _member$init, _member$init2; + const value = + typeof ((_member$init = member.init) === null || _member$init === void 0 + ? void 0 + : _member$init.value) === 'number' + ? { + type: 'NumberLiteralTypeAnnotation', + value: member.init.value, + } + : typeof ((_member$init2 = member.init) === null || + _member$init2 === void 0 + ? void 0 + : _member$init2.value) === 'string' + ? { + type: 'StringLiteralTypeAnnotation', + value: member.init.value, + } + : { + type: 'StringLiteralTypeAnnotation', + value: member.id.name, + }; return { name: member.id.name, - value: - (_member$init$value = - (_member$init = member.init) === null || _member$init === void 0 - ? void 0 - : _member$init.value) !== null && _member$init$value !== void 0 - ? _member$init$value - : member.id.name, + value: value, }; }); } diff --git a/packages/react-native-codegen/lib/parsers/flow/parser.js.flow b/packages/react-native-codegen/lib/parsers/flow/parser.js.flow index 91cac322b93b4e..07b8559c64db19 100644 --- a/packages/react-native-codegen/lib/parsers/flow/parser.js.flow +++ b/packages/react-native-codegen/lib/parsers/flow/parser.js.flow @@ -15,7 +15,7 @@ import type { NamedShape, NativeModuleAliasMap, NativeModuleEnumMap, - NativeModuleEnumMembers, + NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleParamTypeAnnotation, Nullable, @@ -117,6 +117,12 @@ 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'); @@ -221,11 +227,31 @@ class FlowParser implements Parser { }); } - parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers { - return typeAnnotation.members.map(member => ({ - name: member.id.name, - value: member.init?.value ?? member.id.name, - })); + parseEnumMembers( + typeAnnotation: $FlowFixMe, + ): $ReadOnlyArray { + return typeAnnotation.members.map(member => { + const value = + typeof member.init?.value === 'number' + ? { + type: 'NumberLiteralTypeAnnotation', + value: member.init.value, + } + : typeof member.init?.value === 'string' + ? { + type: 'StringLiteralTypeAnnotation', + value: member.init.value, + } + : { + type: 'StringLiteralTypeAnnotation', + value: member.id.name, + }; + + return { + name: member.id.name, + value: value, + }; + }); } isModuleInterface(node: $FlowFixMe): boolean { diff --git a/packages/react-native-codegen/lib/parsers/parser.js.flow b/packages/react-native-codegen/lib/parsers/parser.js.flow index 4c0d0257ed54ac..3e0b74ec6fda43 100644 --- a/packages/react-native-codegen/lib/parsers/parser.js.flow +++ b/packages/react-native-codegen/lib/parsers/parser.js.flow @@ -15,7 +15,7 @@ import type { NamedShape, NativeModuleAliasMap, NativeModuleEnumMap, - NativeModuleEnumMembers, + NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleParamTypeAnnotation, Nullable, @@ -146,6 +146,14 @@ 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[]; /** * Given the name of a file, it returns a Schema. * @parameter filename: the name of the file. @@ -232,7 +240,9 @@ export interface Parser { /** * Calculates enum's members */ - parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers; + parseEnumMembers( + typeAnnotation: $FlowFixMe, + ): $ReadOnlyArray; /** * Given a node, it returns true if it is a module interface diff --git a/packages/react-native-codegen/lib/parsers/parserMock.js b/packages/react-native-codegen/lib/parsers/parserMock.js index 648bc20eb8aefc..4781a967fd225b 100644 --- a/packages/react-native-codegen/lib/parsers/parserMock.js +++ b/packages/react-native-codegen/lib/parsers/parserMock.js @@ -115,6 +115,9 @@ export class MockedParser { remapUnionTypeAnnotationMemberNames(membersTypes) { return []; } + getStringLiteralUnionTypeAnnotationStringLiterals(membersTypes) { + return []; + } parseFile(filename) { return schemaMock; } @@ -155,21 +158,33 @@ export class MockedParser { ? [ { name: 'Hello', - value: 'hello', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'hello', + }, }, { name: 'Goodbye', - value: 'goodbye', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'goodbye', + }, }, ] : [ { name: 'On', - value: '1', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 1, + }, }, { name: 'Off', - value: '0', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 0, + }, }, ]; } diff --git a/packages/react-native-codegen/lib/parsers/parserMock.js.flow b/packages/react-native-codegen/lib/parsers/parserMock.js.flow index 7b0cc7ed53f093..c99b98272f7460 100644 --- a/packages/react-native-codegen/lib/parsers/parserMock.js.flow +++ b/packages/react-native-codegen/lib/parsers/parserMock.js.flow @@ -15,7 +15,7 @@ import type { NamedShape, NativeModuleAliasMap, NativeModuleEnumMap, - NativeModuleEnumMembers, + NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleParamTypeAnnotation, Nullable, @@ -109,6 +109,12 @@ export class MockedParser implements Parser { return []; } + getStringLiteralUnionTypeAnnotationStringLiterals( + membersTypes: $FlowFixMe[], + ): string[] { + return []; + } + parseFile(filename: string): SchemaType { return schemaMock; } @@ -162,26 +168,40 @@ export class MockedParser implements Parser { return; } - parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers { + parseEnumMembers( + typeAnnotation: $FlowFixMe, + ): $ReadOnlyArray { return typeAnnotation.type === 'StringTypeAnnotation' ? [ { name: 'Hello', - value: 'hello', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'hello', + }, }, { name: 'Goodbye', - value: 'goodbye', + value: { + type: 'StringLiteralTypeAnnotation', + value: 'goodbye', + }, }, ] : [ { name: 'On', - value: '1', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 1, + }, }, { name: 'Off', - value: '0', + value: { + type: 'NumberLiteralTypeAnnotation', + value: 0, + }, }, ]; } diff --git a/packages/react-native-codegen/lib/parsers/parsers-primitives.js b/packages/react-native-codegen/lib/parsers/parsers-primitives.js index 94a557fce1d6f8..aceb545d04942a 100644 --- a/packages/react-native-codegen/lib/parsers/parsers-primitives.js +++ b/packages/react-native-codegen/lib/parsers/parsers-primitives.js @@ -188,11 +188,23 @@ function emitMixed(nullable) { type: 'MixedTypeAnnotation', }); } +function emitNumberLiteral(nullable, value) { + return wrapNullable(nullable, { + type: 'NumberLiteralTypeAnnotation', + value, + }); +} function emitString(nullable) { return wrapNullable(nullable, { type: 'StringTypeAnnotation', }); } +function emitStringLiteral(nullable, value) { + return wrapNullable(nullable, { + type: 'StringLiteralTypeAnnotation', + value, + }); +} function emitStringProp(name, optional) { return { name, @@ -318,6 +330,9 @@ function emitPromise( ) { return wrapNullable(nullable, { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }); } else { try { @@ -337,6 +352,9 @@ function emitPromise( } catch { return wrapNullable(nullable, { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }); } } @@ -373,6 +391,11 @@ function emitFloatProp(name, optional) { }; } function emitUnion(nullable, hasteModuleName, typeAnnotation, parser) { + // 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, ); @@ -385,11 +408,38 @@ function emitUnion(nullable, hasteModuleName, typeAnnotation, parser) { 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, + hasteModuleName, + typeAnnotation, + parser, +) { + const stringLiterals = + parser.getStringLiteralUnionTypeAnnotationStringLiterals( + typeAnnotation.types, + ); + return wrapNullable(nullable, { + type: 'StringLiteralUnionTypeAnnotation', + types: stringLiterals.map(stringLiteral => ({ + type: 'StringLiteralTypeAnnotation', + value: stringLiteral, + })), + }); +} function translateArrayTypeAnnotation( hasteModuleName, types, @@ -631,10 +681,11 @@ function emitUnionProp(name, optional, parser, typeAnnotation) { name, optional, typeAnnotation: { - type: 'StringEnumTypeAnnotation', - options: typeAnnotation.types.map(option => - parser.getLiteralValue(option), - ), + type: 'StringLiteralUnionTypeAnnotation', + types: typeAnnotation.types.map(option => ({ + type: 'StringLiteralTypeAnnotation', + value: parser.getLiteralValue(option), + })), }, }; } @@ -651,6 +702,7 @@ module.exports = { emitInt32Prop, emitMixedProp, emitNumber, + emitNumberLiteral, emitGenericObject, emitDictionary, emitObject, @@ -660,6 +712,7 @@ module.exports = { emitString, emitStringish, emitStringProp, + emitStringLiteral, emitMixed, emitUnion, emitPartial, diff --git a/packages/react-native-codegen/lib/parsers/parsers-primitives.js.flow b/packages/react-native-codegen/lib/parsers/parsers-primitives.js.flow index 5fead2c4acf47a..6fa749cea97b71 100644 --- a/packages/react-native-codegen/lib/parsers/parsers-primitives.js.flow +++ b/packages/react-native-codegen/lib/parsers/parsers-primitives.js.flow @@ -14,13 +14,13 @@ import type { BooleanTypeAnnotation, DoubleTypeAnnotation, EventTypeAnnotation, + FloatTypeAnnotation, Int32TypeAnnotation, NamedShape, NativeModuleAliasMap, NativeModuleBaseTypeAnnotation, NativeModuleEnumDeclaration, NativeModuleEnumMap, - FloatTypeAnnotation, NativeModuleFunctionTypeAnnotation, NativeModuleGenericObjectTypeAnnotation, NativeModuleMixedTypeAnnotation, @@ -31,8 +31,11 @@ import type { NativeModuleTypeAnnotation, NativeModuleUnionTypeAnnotation, Nullable, + NumberLiteralTypeAnnotation, ObjectTypeAnnotation, ReservedTypeAnnotation, + StringLiteralTypeAnnotation, + StringLiteralUnionTypeAnnotation, StringTypeAnnotation, VoidTypeAnnotation, } from '../CodegenSchema'; @@ -168,12 +171,32 @@ function emitMixed( }); } +function emitNumberLiteral( + nullable: boolean, + value: number, +): Nullable { + return wrapNullable(nullable, { + type: 'NumberLiteralTypeAnnotation', + value, + }); +} + function emitString(nullable: boolean): Nullable { return wrapNullable(nullable, { type: 'StringTypeAnnotation', }); } +function emitStringLiteral( + nullable: boolean, + value: string, +): Nullable { + return wrapNullable(nullable, { + type: 'StringLiteralTypeAnnotation', + value, + }); +} + function emitStringProp( name: string, optional: boolean, @@ -316,6 +339,9 @@ function emitPromise( ) { return wrapNullable(nullable, { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }); } else { try { @@ -335,6 +361,9 @@ function emitPromise( } catch { return wrapNullable(nullable, { type: 'PromiseTypeAnnotation', + elementType: { + type: 'VoidTypeAnnotation', + }, }); } } @@ -392,7 +421,14 @@ function emitUnion( hasteModuleName: string, typeAnnotation: $FlowFixMe, parser: Parser, -): Nullable { +): 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, ); @@ -406,12 +442,42 @@ function emitUnion( ); } + 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, + })), + }); +} + function translateArrayTypeAnnotation( hasteModuleName: string, types: TypeDeclarationMap, @@ -686,10 +752,11 @@ function emitUnionProp( name, optional, typeAnnotation: { - type: 'StringEnumTypeAnnotation', - options: typeAnnotation.types.map(option => - parser.getLiteralValue(option), - ), + type: 'StringLiteralUnionTypeAnnotation', + types: typeAnnotation.types.map(option => ({ + type: 'StringLiteralTypeAnnotation', + value: parser.getLiteralValue(option), + })), }, }; } @@ -707,6 +774,7 @@ module.exports = { emitInt32Prop, emitMixedProp, emitNumber, + emitNumberLiteral, emitGenericObject, emitDictionary, emitObject, @@ -716,6 +784,7 @@ module.exports = { emitString, emitStringish, emitStringProp, + emitStringLiteral, emitMixed, emitUnion, emitPartial, diff --git a/packages/react-native-codegen/lib/parsers/typescript/components/__test_fixtures__/fixtures.js b/packages/react-native-codegen/lib/parsers/typescript/components/__test_fixtures__/fixtures.js index 042b6a58227aaf..c34bff2159b59c 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/components/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/lib/parsers/typescript/components/__test_fixtures__/fixtures.js @@ -38,10 +38,10 @@ const EVENT_DEFINITION = ` int32_optional_value: Int32 | null | undefined; int32_optional_both?: Int32 | null | undefined; - enum_required: 'small' | 'large'; - enum_optional_key?: 'small' | 'large'; - enum_optional_value: ('small' | 'large') | null | undefined; - enum_optional_both?: ('small' | 'large') | null | undefined; + union_required: 'small' | 'large'; + union_optional_key?: 'small' | 'large'; + union_optional_value: ('small' | 'large') | null | undefined; + union_optional_both?: ('small' | 'large') | null | undefined; object_required: { boolean_required: boolean; @@ -110,10 +110,10 @@ const EVENT_DEFINITION = ` int32_array_optional_value: Int32[] | null | undefined; int32_array_optional_both?: Int32[] | null | undefined; - enum_array_required: ('small' | 'large')[]; - enum_array_optional_key?: ('small' | 'large')[]; - enum_array_optional_value: ('small' | 'large')[] | null | undefined; - enum_array_optional_both?: ('small' | 'large')[] | null | undefined; + union_array_required: ('small' | 'large')[]; + union_array_optional_key?: ('small' | 'large')[]; + union_array_optional_value: ('small' | 'large')[] | null | undefined; + union_array_optional_both?: ('small' | 'large')[] | null | undefined; object_array_required: { boolean_required: boolean; @@ -1052,10 +1052,18 @@ type NativeType = HostComponent; z: Double, animated: boolean, ): void; + readonly arrayArgs: ( + viewRef: React.ElementRef, + booleanArray: ReadOnlyArray, + stringArray: ReadOnlyArray, + floatArray: ReadOnlyArray, + intArray: ReadOnlyArray, + doubleArray: ReadOnlyArray, + ) => void; } export const Commands = codegenNativeCommands({ - supportedCommands: ['handleRootTag', 'hotspotUpdate', 'scrollTo'], + supportedCommands: ['handleRootTag', 'hotspotUpdate', 'scrollTo', 'arrayArgs'], }); export default codegenNativeComponent( @@ -1082,6 +1090,10 @@ import type {HostComponent} from 'react-native'; export type Boolean = boolean; export type Int = Int32; export type Void = void; +export type Locations = { + x: number, + y: number, +} export interface ModuleProps extends ViewProps { // No props or events @@ -1099,6 +1111,7 @@ export type AddOverlays = ( overlayColorsReadOnly: ReadOnlyArray, overlayColorsArray: Array, overlayColorsArrayAnnotation: string[], + overlayLocations: ReadOnlyArray, ) => Void; interface NativeCommands { diff --git a/packages/react-native-codegen/lib/parsers/typescript/components/__test_fixtures__/fixtures.js.flow b/packages/react-native-codegen/lib/parsers/typescript/components/__test_fixtures__/fixtures.js.flow index ed237a0b6c9ef8..6084d0568ff6bd 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/components/__test_fixtures__/fixtures.js.flow +++ b/packages/react-native-codegen/lib/parsers/typescript/components/__test_fixtures__/fixtures.js.flow @@ -38,10 +38,10 @@ const EVENT_DEFINITION = ` int32_optional_value: Int32 | null | undefined; int32_optional_both?: Int32 | null | undefined; - enum_required: 'small' | 'large'; - enum_optional_key?: 'small' | 'large'; - enum_optional_value: ('small' | 'large') | null | undefined; - enum_optional_both?: ('small' | 'large') | null | undefined; + union_required: 'small' | 'large'; + union_optional_key?: 'small' | 'large'; + union_optional_value: ('small' | 'large') | null | undefined; + union_optional_both?: ('small' | 'large') | null | undefined; object_required: { boolean_required: boolean; @@ -110,10 +110,10 @@ const EVENT_DEFINITION = ` int32_array_optional_value: Int32[] | null | undefined; int32_array_optional_both?: Int32[] | null | undefined; - enum_array_required: ('small' | 'large')[]; - enum_array_optional_key?: ('small' | 'large')[]; - enum_array_optional_value: ('small' | 'large')[] | null | undefined; - enum_array_optional_both?: ('small' | 'large')[] | null | undefined; + union_array_required: ('small' | 'large')[]; + union_array_optional_key?: ('small' | 'large')[]; + union_array_optional_value: ('small' | 'large')[] | null | undefined; + union_array_optional_both?: ('small' | 'large')[] | null | undefined; object_array_required: { boolean_required: boolean; @@ -1065,10 +1065,18 @@ type NativeType = HostComponent; z: Double, animated: boolean, ): void; + readonly arrayArgs: ( + viewRef: React.ElementRef, + booleanArray: ReadOnlyArray, + stringArray: ReadOnlyArray, + floatArray: ReadOnlyArray, + intArray: ReadOnlyArray, + doubleArray: ReadOnlyArray, + ) => void; } export const Commands = codegenNativeCommands({ - supportedCommands: ['handleRootTag', 'hotspotUpdate', 'scrollTo'], + supportedCommands: ['handleRootTag', 'hotspotUpdate', 'scrollTo', 'arrayArgs'], }); export default codegenNativeComponent( @@ -1096,6 +1104,10 @@ import type {HostComponent} from 'react-native'; export type Boolean = boolean; export type Int = Int32; export type Void = void; +export type Locations = { + x: number, + y: number, +} export interface ModuleProps extends ViewProps { // No props or events @@ -1113,6 +1125,7 @@ export type AddOverlays = ( overlayColorsReadOnly: ReadOnlyArray, overlayColorsArray: Array, overlayColorsArrayAnnotation: string[], + overlayLocations: ReadOnlyArray, ) => Void; interface NativeCommands { diff --git a/packages/react-native-codegen/lib/parsers/typescript/components/commands.js b/packages/react-native-codegen/lib/parsers/typescript/components/commands.js index 9c76cd65316e41..531bd564138d25 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/components/commands.js +++ b/packages/react-native-codegen/lib/parsers/typescript/components/commands.js @@ -72,19 +72,15 @@ function buildCommandSchemaInternal(name, optional, parameters, types) { } returnType = { type: 'ArrayTypeAnnotation', - elementType: getPrimitiveTypeAnnotation( - // TODO: T172453752 support complex type annotation for array element - paramValue.typeParameters.params[0].type, + elementType: getCommandArrayElementTypeType( + paramValue.typeParameters.params[0], ), }; break; case 'TSArrayType': returnType = { type: 'ArrayTypeAnnotation', - elementType: { - // TODO: T172453752 support complex type annotation for array element - type: getPrimitiveTypeAnnotation(paramValue.elementType.type).type, - }, + elementType: getCommandArrayElementTypeType(paramValue.elementType), }; break; default: @@ -111,6 +107,43 @@ function buildCommandSchemaInternal(name, optional, parameters, types) { }, }; } +function getCommandArrayElementTypeType(inputType) { + // TODO: T172453752 support more complex type annotation for array element + + if (inputType == null || typeof inputType !== 'object') { + throw new Error(`Expected an object, received ${typeof inputType}`); + } + const type = inputType.type; + if (typeof type !== 'string') { + throw new Error('Command array element type must be a string'); + } + + // This is not a great solution. This generally means its a type alias to another type + // like an object or union. Ideally we'd encode that in the schema so the compat-check can + // validate those deeper objects for breaking changes and the generators can do something smarter. + // As of now, the generators just create ReadableMap or (const NSArray *) which are untyped + if (type === 'TSTypeReference') { + var _inputType$typeName; + const name = + typeof inputType.typeName === 'object' + ? (_inputType$typeName = inputType.typeName) === null || + _inputType$typeName === void 0 + ? void 0 + : _inputType$typeName.name + : null; + if (typeof name !== 'string') { + throw new Error('Expected TSTypeReference AST name to be a string'); + } + try { + return getPrimitiveTypeAnnotation(name); + } catch (e) { + return { + type: 'MixedTypeAnnotation', + }; + } + } + return getPrimitiveTypeAnnotation(type); +} function buildCommandSchema(property, types) { if (property.type === 'TSPropertySignature') { const topLevelType = parseTopLevelType( diff --git a/packages/react-native-codegen/lib/parsers/typescript/components/commands.js.flow b/packages/react-native-codegen/lib/parsers/typescript/components/commands.js.flow index 882f7002d9a4b6..184b754554b76b 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/components/commands.js.flow +++ b/packages/react-native-codegen/lib/parsers/typescript/components/commands.js.flow @@ -11,7 +11,9 @@ 'use strict'; import type { + CommandParamTypeAnnotation, CommandTypeAnnotation, + ComponentCommandArrayTypeAnnotation, NamedShape, } from '../../../CodegenSchema.js'; import type {TypeDeclarationMap} from '../../utils'; @@ -53,7 +55,7 @@ function buildCommandSchemaInternal( paramValue.type === 'TSTypeReference' ? paramValue.typeName.name : paramValue.type; - let returnType; + let returnType: CommandParamTypeAnnotation; switch (type) { case 'RootTag': @@ -78,19 +80,15 @@ function buildCommandSchemaInternal( } returnType = { type: 'ArrayTypeAnnotation', - elementType: getPrimitiveTypeAnnotation( - // TODO: T172453752 support complex type annotation for array element - paramValue.typeParameters.params[0].type, + elementType: getCommandArrayElementTypeType( + paramValue.typeParameters.params[0], ), }; break; case 'TSArrayType': returnType = { type: 'ArrayTypeAnnotation', - elementType: { - // TODO: T172453752 support complex type annotation for array element - type: getPrimitiveTypeAnnotation(paramValue.elementType.type).type, - }, + elementType: getCommandArrayElementTypeType(paramValue.elementType), }; break; default: @@ -120,6 +118,44 @@ function buildCommandSchemaInternal( }; } +function getCommandArrayElementTypeType( + inputType: mixed, +): ComponentCommandArrayTypeAnnotation['elementType'] { + // TODO: T172453752 support more complex type annotation for array element + + if (inputType == null || typeof inputType !== 'object') { + throw new Error(`Expected an object, received ${typeof inputType}`); + } + + const type = inputType.type; + if (typeof type !== 'string') { + throw new Error('Command array element type must be a string'); + } + + // This is not a great solution. This generally means its a type alias to another type + // like an object or union. Ideally we'd encode that in the schema so the compat-check can + // validate those deeper objects for breaking changes and the generators can do something smarter. + // As of now, the generators just create ReadableMap or (const NSArray *) which are untyped + if (type === 'TSTypeReference') { + const name = + typeof inputType.typeName === 'object' ? inputType.typeName?.name : null; + + if (typeof name !== 'string') { + throw new Error('Expected TSTypeReference AST name to be a string'); + } + + try { + return getPrimitiveTypeAnnotation(name); + } catch (e) { + return { + type: 'MixedTypeAnnotation', + }; + } + } + + return getPrimitiveTypeAnnotation(type); +} + function buildCommandSchema( property: EventTypeAST, types: TypeDeclarationMap, diff --git a/packages/react-native-codegen/lib/parsers/typescript/components/events.js b/packages/react-native-codegen/lib/parsers/typescript/components/events.js index 216752c45bd8d5..fff827ab037729 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/components/events.js +++ b/packages/react-native-codegen/lib/parsers/typescript/components/events.js @@ -114,10 +114,11 @@ function extractArrayElementType(typeAnnotation, name, parser) { }; case 'TSUnionType': return { - type: 'StringEnumTypeAnnotation', - options: typeAnnotation.types.map(option => - parser.getLiteralValue(option), - ), + type: 'StringLiteralUnionTypeAnnotation', + types: typeAnnotation.types.map(option => ({ + type: 'StringLiteralTypeAnnotation', + value: parser.getLiteralValue(option), + })), }; case 'TSTypeLiteral': return { diff --git a/packages/react-native-codegen/lib/parsers/typescript/components/events.js.flow b/packages/react-native-codegen/lib/parsers/typescript/components/events.js.flow index a0672ac3570448..b4510a02b4849f 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/components/events.js.flow +++ b/packages/react-native-codegen/lib/parsers/typescript/components/events.js.flow @@ -126,10 +126,11 @@ function extractArrayElementType( }; case 'TSUnionType': return { - type: 'StringEnumTypeAnnotation', - options: typeAnnotation.types.map(option => - parser.getLiteralValue(option), - ), + type: 'StringLiteralUnionTypeAnnotation', + types: typeAnnotation.types.map(option => ({ + type: 'StringLiteralTypeAnnotation', + value: parser.getLiteralValue(option), + })), }; case 'TSTypeLiteral': return { diff --git a/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/failures.js b/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/failures.js index 59f52acef62c05..cf76a42234eeb1 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/failures.js @@ -199,6 +199,57 @@ export default TurboModuleRegistry.getEnforcing( 'MixedValuesEnumNativeModule', ); `; +const NUMERIC_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, + NEGATIVE = -1, + SUBFACTORIAL = !5, +} + +export interface Spec extends TurboModule { + readonly getEnums: (a: SomeEnum) => string; +} + +export default TurboModuleRegistry.getEnforcing( + 'NumericValuesEnumNativeModule', +); +`; +const MAP_WITH_EXTRA_KEYS_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'; + +type MapWithKey = { + [a: string]: string | null, + extra: string, +} + +export interface Spec extends TurboModule { + readonly getMap: (a: MapWithKey) => string; +} + +export default TurboModuleRegistry.getEnforcing('MapWithExtraKeysNativeModule'); +`; module.exports = { NATIVE_MODULES_WITH_UNNAMED_PARAMS, NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE, @@ -209,4 +260,6 @@ module.exports = { TWO_NATIVE_EXTENDING_TURBO_MODULE, EMPTY_ENUM_NATIVE_MODULE, MIXED_VALUES_ENUM_NATIVE_MODULE, + NUMERIC_VALUES_ENUM_NATIVE_MODULE, + MAP_WITH_EXTRA_KEYS_NATIVE_MODULE, }; diff --git a/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/failures.js.flow b/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/failures.js.flow index a8d08e8395ff9a..8e42067ec161eb 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/failures.js.flow +++ b/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/failures.js.flow @@ -208,6 +208,59 @@ export default TurboModuleRegistry.getEnforcing( ); `; +const NUMERIC_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, + NEGATIVE = -1, + SUBFACTORIAL = !5, +} + +export interface Spec extends TurboModule { + readonly getEnums: (a: SomeEnum) => string; +} + +export default TurboModuleRegistry.getEnforcing( + 'NumericValuesEnumNativeModule', +); +`; + +const MAP_WITH_EXTRA_KEYS_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'; + +type MapWithKey = { + [a: string]: string | null, + extra: string, +} + +export interface Spec extends TurboModule { + readonly getMap: (a: MapWithKey) => string; +} + +export default TurboModuleRegistry.getEnforcing('MapWithExtraKeysNativeModule'); +`; + module.exports = { NATIVE_MODULES_WITH_UNNAMED_PARAMS, NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE, @@ -218,4 +271,6 @@ module.exports = { TWO_NATIVE_EXTENDING_TURBO_MODULE, EMPTY_ENUM_NATIVE_MODULE, MIXED_VALUES_ENUM_NATIVE_MODULE, + NUMERIC_VALUES_ENUM_NATIVE_MODULE, + MAP_WITH_EXTRA_KEYS_NATIVE_MODULE, }; diff --git a/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/fixtures.js index 19c4440e2119e5..ffab9918f8378c 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/fixtures.js @@ -110,8 +110,10 @@ import * as TurboModuleRegistry from '../TurboModuleRegistry'; export interface Spec extends TurboModule { readonly passBool?: (arg: boolean) => void; readonly passNumber: (arg: number) => void; + readonly passNumberLiteral: (arg: 4) => void; readonly passString: (arg: string) => void; readonly passStringish: (arg: Stringish) => void; + readonly passStringLiteral: (arg: 'A String Literal') => void; } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); @@ -142,6 +144,7 @@ export type ObjectAlias = { label: string; truthy: boolean; }; +export type PureObjectAlias = ObjectAlias; export type ReadOnlyAlias = Readonly; export interface Spec extends TurboModule { @@ -151,6 +154,7 @@ export interface Spec extends TurboModule { readonly getArray: (a: Array) => {a: B}; readonly getStringFromAlias: (a: ObjectAlias) => string; readonly getStringFromNullableAlias: (a: ObjectAlias | null) => string; + readonly getStringFromPureAlias: (a: PureObjectAlias) => string; readonly getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string; readonly getStringFromNullableReadOnlyAlias: (a: ReadOnlyAlias | null) => string; } @@ -695,6 +699,32 @@ export interface Spec extends TurboModule { export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); +`; +const NATIVE_MODULE_WITH_UNION_RETURN_TYPES = ` +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; +import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; + +export interface Spec extends TurboModule { + readonly getStringUnion: () => 'light' | 'dark'; + readonly setStringUnion: (strings: 'light' | 'dark') => void; + + readonly getNumberUnion: () => 1 | 2; + readonly setNumberUnion: (numbers: 1 | 2) => void; + + readonly getObjectUnion: () => {a: 1} | {b: 2}; + readonly setObjectUnion: (objects: {a: 1} | {b: 2}) => void; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; const NATIVE_MODULE_WITH_EVENT_EMITTERS = ` /** @@ -805,6 +835,7 @@ export enum Quality { } export enum Resolution { + Corrupted = -1, Low = 720, High = 1080, } @@ -840,7 +871,7 @@ export type CustomDeviceEvent = { export interface Spec extends TurboModule { readonly getCallback: () => () => void; readonly getMixed: (arg: unknown) => unknown; - readonly getEnums: (quality: Quality, resolution?: Resolution, stringOptions: StringOptions) => string; + readonly getEnums: (quality: Quality, resolution?: Resolution, stringOptions: StringOptions) => Quality; readonly getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode; readonly getGraphNode: (arg: GraphNode) => GraphNode; readonly getMap: (arg: {[a: string]: number | null;}) => {[b: string]: number | null;}; @@ -879,6 +910,7 @@ module.exports = { NATIVE_MODULE_WITH_BASIC_PARAM_TYPES, NATIVE_MODULE_WITH_CALLBACK, NATIVE_MODULE_WITH_UNION, + NATIVE_MODULE_WITH_UNION_RETURN_TYPES, NATIVE_MODULE_WITH_EVENT_EMITTERS, EMPTY_NATIVE_MODULE, ANDROID_ONLY_NATIVE_MODULE, diff --git a/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/fixtures.js.flow b/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/fixtures.js.flow index 7ebbe9846ad25a..221713ccd29abf 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/fixtures.js.flow +++ b/packages/react-native-codegen/lib/parsers/typescript/modules/__test_fixtures__/fixtures.js.flow @@ -113,8 +113,10 @@ import * as TurboModuleRegistry from '../TurboModuleRegistry'; export interface Spec extends TurboModule { readonly passBool?: (arg: boolean) => void; readonly passNumber: (arg: number) => void; + readonly passNumberLiteral: (arg: 4) => void; readonly passString: (arg: string) => void; readonly passStringish: (arg: Stringish) => void; + readonly passStringLiteral: (arg: 'A String Literal') => void; } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); @@ -146,6 +148,7 @@ export type ObjectAlias = { label: string; truthy: boolean; }; +export type PureObjectAlias = ObjectAlias; export type ReadOnlyAlias = Readonly; export interface Spec extends TurboModule { @@ -155,6 +158,7 @@ export interface Spec extends TurboModule { readonly getArray: (a: Array) => {a: B}; readonly getStringFromAlias: (a: ObjectAlias) => string; readonly getStringFromNullableAlias: (a: ObjectAlias | null) => string; + readonly getStringFromPureAlias: (a: PureObjectAlias) => string; readonly getStringFromReadOnlyAlias: (a: ReadOnlyAlias) => string; readonly getStringFromNullableReadOnlyAlias: (a: ReadOnlyAlias | null) => string; } @@ -723,6 +727,33 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; +const NATIVE_MODULE_WITH_UNION_RETURN_TYPES = ` +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; +import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; + +export interface Spec extends TurboModule { + readonly getStringUnion: () => 'light' | 'dark'; + readonly setStringUnion: (strings: 'light' | 'dark') => void; + + readonly getNumberUnion: () => 1 | 2; + readonly setNumberUnion: (numbers: 1 | 2) => void; + + readonly getObjectUnion: () => {a: 1} | {b: 2}; + readonly setObjectUnion: (objects: {a: 1} | {b: 2}) => void; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); +`; + const NATIVE_MODULE_WITH_EVENT_EMITTERS = ` /** * Copyright (c) Meta Platforms, Inc. and affiliates. @@ -835,6 +866,7 @@ export enum Quality { } export enum Resolution { + Corrupted = -1, Low = 720, High = 1080, } @@ -870,7 +902,7 @@ export type CustomDeviceEvent = { export interface Spec extends TurboModule { readonly getCallback: () => () => void; readonly getMixed: (arg: unknown) => unknown; - readonly getEnums: (quality: Quality, resolution?: Resolution, stringOptions: StringOptions) => string; + readonly getEnums: (quality: Quality, resolution?: Resolution, stringOptions: StringOptions) => Quality; readonly getBinaryTreeNode: (arg: BinaryTreeNode) => BinaryTreeNode; readonly getGraphNode: (arg: GraphNode) => GraphNode; readonly getMap: (arg: {[a: string]: number | null;}) => {[b: string]: number | null;}; @@ -910,6 +942,7 @@ module.exports = { NATIVE_MODULE_WITH_BASIC_PARAM_TYPES, NATIVE_MODULE_WITH_CALLBACK, NATIVE_MODULE_WITH_UNION, + NATIVE_MODULE_WITH_UNION_RETURN_TYPES, NATIVE_MODULE_WITH_EVENT_EMITTERS, EMPTY_NATIVE_MODULE, ANDROID_ONLY_NATIVE_MODULE, diff --git a/packages/react-native-codegen/lib/parsers/typescript/modules/index.js b/packages/react-native-codegen/lib/parsers/typescript/modules/index.js index 6986108968b6d0..56697bd088915e 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/lib/parsers/typescript/modules/index.js @@ -14,6 +14,8 @@ const _require = require('../../errors'), UnsupportedEnumDeclarationParserError = _require.UnsupportedEnumDeclarationParserError, UnsupportedGenericParserError = _require.UnsupportedGenericParserError, + UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError = + _require.UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError, UnsupportedTypeAnnotationParserError = _require.UnsupportedTypeAnnotationParserError; const _require2 = require('../../parsers-commons'), @@ -23,8 +25,10 @@ const _require3 = require('../../parsers-primitives'), emitCommonTypes = _require3.emitCommonTypes, emitDictionary = _require3.emitDictionary, emitFunction = _require3.emitFunction, + emitNumberLiteral = _require3.emitNumberLiteral, emitPromise = _require3.emitPromise, emitRootTag = _require3.emitRootTag, + emitStringLiteral = _require3.emitStringLiteral, emitUnion = _require3.emitUnion, translateArrayTypeAnnotation = _require3.translateArrayTypeAnnotation, typeAliasResolution = _require3.typeAliasResolution, @@ -302,6 +306,15 @@ function translateTypeAnnotation( const indexSignatures = typeAnnotation.members.filter( member => member.type === 'TSIndexSignature', ); + const properties = typeAnnotation.members.filter( + member => member.type === 'TSPropertySignature', + ); + if (indexSignatures.length > 0 && properties.length > 0) { + throw new UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + ); + } if (indexSignatures.length > 0) { // check the property type to prevent developers from using unsupported types // the return value from `translateTypeAnnotation` is unused @@ -376,6 +389,24 @@ function translateTypeAnnotation( case 'TSUnionType': { return emitUnion(nullable, hasteModuleName, typeAnnotation, parser); } + case 'TSLiteralType': { + const literal = typeAnnotation.literal; + switch (literal.type) { + case 'StringLiteral': { + return emitStringLiteral(nullable, literal.value); + } + case 'NumericLiteral': { + return emitNumberLiteral(nullable, literal.value); + } + default: { + throw new UnsupportedTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + parser.language(), + ); + } + } + } default: { const commonType = emitCommonTypes( hasteModuleName, diff --git a/packages/react-native-codegen/lib/parsers/typescript/modules/index.js.flow b/packages/react-native-codegen/lib/parsers/typescript/modules/index.js.flow index ef87bd26ce309c..aaab3ddeeae859 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/modules/index.js.flow +++ b/packages/react-native-codegen/lib/parsers/typescript/modules/index.js.flow @@ -28,6 +28,7 @@ import type { const { UnsupportedEnumDeclarationParserError, UnsupportedGenericParserError, + UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError, UnsupportedTypeAnnotationParserError, } = require('../../errors'); const {parseObjectProperty} = require('../../parsers-commons'); @@ -36,8 +37,10 @@ const { emitCommonTypes, emitDictionary, emitFunction, + emitNumberLiteral, emitPromise, emitRootTag, + emitStringLiteral, emitUnion, translateArrayTypeAnnotation, typeAliasResolution, @@ -308,6 +311,18 @@ function translateTypeAnnotation( const indexSignatures = typeAnnotation.members.filter( member => member.type === 'TSIndexSignature', ); + + const properties = typeAnnotation.members.filter( + member => member.type === 'TSPropertySignature', + ); + + if (indexSignatures.length > 0 && properties.length > 0) { + throw new UnsupportedObjectPropertyWithIndexerTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + ); + } + if (indexSignatures.length > 0) { // check the property type to prevent developers from using unsupported types // the return value from `translateTypeAnnotation` is unused @@ -383,6 +398,24 @@ function translateTypeAnnotation( case 'TSUnionType': { return emitUnion(nullable, hasteModuleName, typeAnnotation, parser); } + case 'TSLiteralType': { + const literal = typeAnnotation.literal; + switch (literal.type) { + case 'StringLiteral': { + return emitStringLiteral(nullable, literal.value); + } + case 'NumericLiteral': { + return emitNumberLiteral(nullable, literal.value); + } + default: { + throw new UnsupportedTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + parser.language(), + ); + } + } + } default: { const commonType = emitCommonTypes( hasteModuleName, diff --git a/packages/react-native-codegen/lib/parsers/typescript/parser.js b/packages/react-native-codegen/lib/parsers/typescript/parser.js index da4afe30e2ab36..c3517665d04a1f 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/parser.js +++ b/packages/react-native-codegen/lib/parsers/typescript/parser.js @@ -126,6 +126,9 @@ class TypeScriptParser { }; return [...new Set(membersTypes.map(remapLiteral))]; } + getStringLiteralUnionTypeAnnotationStringLiterals(membersTypes) { + return membersTypes.map(item => item.literal.value); + } parseFile(filename) { const contents = fs.readFileSync(filename, 'utf8'); return this.parseString(contents, filename); @@ -174,12 +177,29 @@ class TypeScriptParser { _typeAnnotation$membe === void 0 ? void 0 : _typeAnnotation$membe.initializer; - const enumMembersType = - !enumInitializer || enumInitializer.type === 'StringLiteral' - ? 'StringTypeAnnotation' - : enumInitializer.type === 'NumericLiteral' - ? 'NumberTypeAnnotation' - : null; + const enumInitializerType = + enumInitializer === null || enumInitializer === void 0 + ? void 0 + : enumInitializer.type; + let enumMembersType = null; + if (!enumInitializerType) { + return 'StringTypeAnnotation'; + } + switch (enumInitializerType) { + case 'StringLiteral': + enumMembersType = 'StringTypeAnnotation'; + break; + case 'NumericLiteral': + enumMembersType = 'NumberTypeAnnotation'; + break; + case 'UnaryExpression': + if (enumInitializer.operator === '-') { + enumMembersType = 'NumberTypeAnnotation'; + } + break; + default: + enumMembersType = null; + } if (!enumMembersType) { throw new Error( 'Enum values must be either blank, number, or string values.', @@ -198,15 +218,33 @@ class TypeScriptParser { ? 'NumericLiteral' : null; typeAnnotation.members.forEach(member => { - var _member$initializer$t, _member$initializer; + var _member$initializer, + _member$initializer2, + _member$initializer3, + _member$initializer4; + const isNegative = + ((_member$initializer = member.initializer) === null || + _member$initializer === void 0 + ? void 0 + : _member$initializer.type) === 'UnaryExpression' && + ((_member$initializer2 = member.initializer) === null || + _member$initializer2 === void 0 + ? void 0 + : _member$initializer2.operator) === '-'; + const initializerType = isNegative + ? (_member$initializer3 = member.initializer) === null || + _member$initializer3 === void 0 || + (_member$initializer3 = _member$initializer3.argument) === null || + _member$initializer3 === void 0 + ? void 0 + : _member$initializer3.type + : (_member$initializer4 = member.initializer) === null || + _member$initializer4 === void 0 + ? void 0 + : _member$initializer4.type; if ( - ((_member$initializer$t = - (_member$initializer = member.initializer) === null || - _member$initializer === void 0 - ? void 0 - : _member$initializer.type) !== null && - _member$initializer$t !== void 0 - ? _member$initializer$t + (initializerType !== null && initializerType !== void 0 + ? initializerType : 'StringLiteral') !== enumInitializerType ) { throw new Error( @@ -217,18 +255,60 @@ class TypeScriptParser { } parseEnumMembers(typeAnnotation) { return typeAnnotation.members.map(member => { - var _member$initializer$v, _member$initializer2; + var _member$initializer5, + _member$initializer6, + _member$initializer7, + _member$initializer8, + _member$initializer9, + _member$initializer10; + const value = + ((_member$initializer5 = member.initializer) === null || + _member$initializer5 === void 0 + ? void 0 + : _member$initializer5.operator) === '-' + ? { + type: 'NumberLiteralTypeAnnotation', + value: + -1 * + ((_member$initializer6 = member.initializer) === null || + _member$initializer6 === void 0 || + (_member$initializer6 = _member$initializer6.argument) === + null || + _member$initializer6 === void 0 + ? void 0 + : _member$initializer6.value), + } + : typeof ((_member$initializer7 = member.initializer) === null || + _member$initializer7 === void 0 + ? void 0 + : _member$initializer7.value) === 'number' + ? { + type: 'NumberLiteralTypeAnnotation', + value: + (_member$initializer8 = member.initializer) === null || + _member$initializer8 === void 0 + ? void 0 + : _member$initializer8.value, + } + : typeof ((_member$initializer9 = member.initializer) === null || + _member$initializer9 === void 0 + ? void 0 + : _member$initializer9.value) === 'string' + ? { + type: 'StringLiteralTypeAnnotation', + value: + (_member$initializer10 = member.initializer) === null || + _member$initializer10 === void 0 + ? void 0 + : _member$initializer10.value, + } + : { + type: 'StringLiteralTypeAnnotation', + value: member.id.name, + }; return { name: member.id.name, - value: - (_member$initializer$v = - (_member$initializer2 = member.initializer) === null || - _member$initializer2 === void 0 - ? void 0 - : _member$initializer2.value) !== null && - _member$initializer$v !== void 0 - ? _member$initializer$v - : member.id.name, + value, }; }); } diff --git a/packages/react-native-codegen/lib/parsers/typescript/parser.js.flow b/packages/react-native-codegen/lib/parsers/typescript/parser.js.flow index 04f8c1d69e6688..70055831887ebd 100644 --- a/packages/react-native-codegen/lib/parsers/typescript/parser.js.flow +++ b/packages/react-native-codegen/lib/parsers/typescript/parser.js.flow @@ -15,7 +15,7 @@ import type { NamedShape, NativeModuleAliasMap, NativeModuleEnumMap, - NativeModuleEnumMembers, + NativeModuleEnumMember, NativeModuleEnumMemberType, NativeModuleParamTypeAnnotation, Nullable, @@ -117,6 +117,12 @@ class TypeScriptParser implements Parser { return [...new Set(membersTypes.map(remapLiteral))]; } + getStringLiteralUnionTypeAnnotationStringLiterals( + membersTypes: $FlowFixMe[], + ): string[] { + return membersTypes.map((item: $FlowFixMe) => item.literal.value); + } + parseFile(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8'); @@ -177,12 +183,30 @@ class TypeScriptParser implements Parser { parseEnumMembersType(typeAnnotation: $FlowFixMe): NativeModuleEnumMemberType { const enumInitializer = typeAnnotation.members[0]?.initializer; - const enumMembersType: ?NativeModuleEnumMemberType = - !enumInitializer || enumInitializer.type === 'StringLiteral' - ? 'StringTypeAnnotation' - : enumInitializer.type === 'NumericLiteral' - ? 'NumberTypeAnnotation' - : null; + const enumInitializerType = enumInitializer?.type; + + let enumMembersType: ?NativeModuleEnumMemberType = null; + + if (!enumInitializerType) { + return 'StringTypeAnnotation'; + } + + switch (enumInitializerType) { + case 'StringLiteral': + enumMembersType = 'StringTypeAnnotation'; + break; + case 'NumericLiteral': + enumMembersType = 'NumberTypeAnnotation'; + break; + case 'UnaryExpression': + if (enumInitializer.operator === '-') { + enumMembersType = 'NumberTypeAnnotation'; + } + break; + default: + enumMembersType = null; + } + if (!enumMembersType) { throw new Error( 'Enum values must be either blank, number, or string values.', @@ -207,9 +231,14 @@ class TypeScriptParser implements Parser { : null; typeAnnotation.members.forEach(member => { - if ( - (member.initializer?.type ?? 'StringLiteral') !== enumInitializerType - ) { + const isNegative = + member.initializer?.type === 'UnaryExpression' && + member.initializer?.operator === '-'; + const initializerType = isNegative + ? member.initializer?.argument?.type + : member.initializer?.type; + + if ((initializerType ?? 'StringLiteral') !== enumInitializerType) { throw new Error( 'Enum values can not be mixed. They all must be either blank, number, or string values.', ); @@ -217,11 +246,36 @@ class TypeScriptParser implements Parser { }); } - parseEnumMembers(typeAnnotation: $FlowFixMe): NativeModuleEnumMembers { - return typeAnnotation.members.map(member => ({ - name: member.id.name, - value: member.initializer?.value ?? member.id.name, - })); + parseEnumMembers( + typeAnnotation: $FlowFixMe, + ): $ReadOnlyArray { + return typeAnnotation.members.map(member => { + const value = + member.initializer?.operator === '-' + ? { + type: 'NumberLiteralTypeAnnotation', + value: -1 * member.initializer?.argument?.value, + } + : typeof member.initializer?.value === 'number' + ? { + type: 'NumberLiteralTypeAnnotation', + value: member.initializer?.value, + } + : typeof member.initializer?.value === 'string' + ? { + type: 'StringLiteralTypeAnnotation', + value: member.initializer?.value, + } + : { + type: 'StringLiteralTypeAnnotation', + value: member.id.name, + }; + + return { + name: member.id.name, + value, + }; + }); } isModuleInterface(node: $FlowFixMe): boolean {