From 690bcdf45e41ea3af7ee2c54e691d6f50ffc26c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Osadnik?= Date: Mon, 8 Jul 2019 04:50:25 -0700 Subject: [PATCH] Add suport for array as a param and returning value Summary: Now `Array` is supported as a returning values or a param of function's definition. Also, Array of Array is allowed. Reviewed By: TheSavior Differential Revision: D16121246 fbshipit-source-id: 59c484120c4025a152e3ba8044eecf11dbbea1f7 --- .../react-native-codegen/src/CodegenSchema.js | 38 +++-- .../flow/__test_fixtures__/failures.js | 50 +++++++ .../flow/__test_fixtures__/fixtures.js | 77 ++++++++++ .../__snapshots__/parser-test.js.snap | 136 ++++++++++++++++++ .../src/parsers/flow/methods.js | 103 ++++++++++++- 5 files changed, 385 insertions(+), 19 deletions(-) diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index 57c11d31888..c33b3068153 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -130,20 +130,34 @@ export type PropTypeShape = $ReadOnly<{| typeAnnotation: PropTypeTypeAnnotation, |}>; -export type FunctionTypeAnnotationParamTypeAnnotation = $ReadOnly<{| - type: - | 'StringTypeAnnotation' - | 'NumberTypeAnnotation' - | 'BooleanTypeAnnotation', +export type PrimitiveTypeAnnotationType = + | 'StringTypeAnnotation' + | 'NumberTypeAnnotation' + | 'BooleanTypeAnnotation'; + +export type PrimitiveTypeAnnotation = $ReadOnly<{| + type: PrimitiveTypeAnnotationType, |}>; -export type FunctionTypeAnnotationReturn = $ReadOnly<{| - type: - | 'StringTypeAnnotation' - | 'NumberTypeAnnotation' - | 'BooleanTypeAnnotation' - | 'VoidTypeAnnotation', -|}>; +export type FunctionTypeAnnotationParamTypeAnnotation = + | $ReadOnly<{| + type: 'AnyTypeAnnotation' | PrimitiveTypeAnnotationType, + |}> + | $ReadOnly<{| + type: 'ArrayTypeAnnotation', + elementType: FunctionTypeAnnotationParamTypeAnnotation, + |}>; + +export type FunctionTypeAnnotationReturnArrayElementType = FunctionTypeAnnotationParamTypeAnnotation; + +export type FunctionTypeAnnotationReturn = + | $ReadOnly<{| + type: PrimitiveTypeAnnotationType | 'VoidTypeAnnotation', + |}> + | $ReadOnly<{| + type: 'ArrayTypeAnnotation', + elementType: FunctionTypeAnnotationReturnArrayElementType, + |}>; export type FunctionTypeAnnotationParam = $ReadOnly<{| name: string, diff --git a/packages/react-native-codegen/src/parsers/flow/__test_fixtures__/failures.js b/packages/react-native-codegen/src/parsers/flow/__test_fixtures__/failures.js index f55709cbdd7..6a508333d2b 100644 --- a/packages/react-native-codegen/src/parsers/flow/__test_fixtures__/failures.js +++ b/packages/react-native-codegen/src/parsers/flow/__test_fixtures__/failures.js @@ -34,6 +34,54 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; +const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT = ` +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from '../RCTExport'; +import * as TurboModuleRegistry from '../TurboModuleRegistry'; + +export interface Spec extends TurboModule { + getString: (arg: string) => Array; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + +const NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM = ` +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from '../RCTExport'; +import * as TurboModuleRegistry from '../TurboModuleRegistry'; + +export interface Spec extends TurboModule { + getString: (arg : Array) => string; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + const NATIVE_MODULES_WITH_NOT_EXISTING_TYPE_AS_RETURN = ` /** * Copyright (c) Facebook, Inc. and its affiliates. @@ -276,6 +324,8 @@ export default codegenNativeComponent('Module'); `; module.exports = { + NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM, + NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT, TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT, NATIVE_MODULES_WITH_NOT_EXISTING_TYPE_AS_PARAM, NATIVE_MODULES_WITH_NOT_EXISTING_TYPE_AS_RETURN, diff --git a/packages/react-native-codegen/src/parsers/flow/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/parsers/flow/__test_fixtures__/fixtures.js index ecd7ea341bf..3377a560130 100644 --- a/packages/react-native-codegen/src/parsers/flow/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/parsers/flow/__test_fixtures__/fixtures.js @@ -91,6 +91,80 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; +const NATIVE_MODULE_WITH_BASIC_ARRAY = ` +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from '../RCTExport'; +import * as TurboModuleRegistry from '../TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +getArray: (arg: Array) => Array; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + +const NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS = ` +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from '../RCTExport'; +import * as TurboModuleRegistry from '../TurboModuleRegistry'; + +export type SomeString = string; + +export interface Spec extends TurboModule { + +getArray: (arg: Array) => Array; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + +const NATIVE_MODULE_WITH_COMPLEX_ARRAY = ` +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from '../RCTExport'; +import * as TurboModuleRegistry from '../TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +getArray: (arg: Array>>>>) => Array>>; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + const EVENT_DEFINITION = ` boolean_required: boolean, boolean_optional_key?: boolean, @@ -632,6 +706,9 @@ export default codegenNativeComponent('Module'); module.exports = { NATIVE_MODULE_WITH_WITH_ALIASES, + NATIVE_MODULE_WITH_BASIC_ARRAY, + NATIVE_MODULE_WITH_COMPLEX_ARRAY, + NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS, NATIVE_MODULE_WITH_BASIC_PARAM_TYPES, EMPTY_NATIVE_MODULE, ALL_PROP_TYPES_NO_EVENTS, diff --git a/packages/react-native-codegen/src/parsers/flow/__tests__/__snapshots__/parser-test.js.snap b/packages/react-native-codegen/src/parsers/flow/__tests__/__snapshots__/parser-test.js.snap index 9efe51c22a5..5dbf7af5bdd 100644 --- a/packages/react-native-codegen/src/parsers/flow/__tests__/__snapshots__/parser-test.js.snap +++ b/packages/react-native-codegen/src/parsers/flow/__tests__/__snapshots__/parser-test.js.snap @@ -8,6 +8,10 @@ exports[`RN Codegen Flow Parser Fails with error message COMMANDS_DEFINED_WITHOU exports[`RN Codegen Flow Parser Fails with error message INCORRECT_NATIVE_MODULES 1`] = `"Interface properties for \\"SpecWithTypo has been specified incorrectly.\\""`; +exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT 1`] = `"Unsupported return type for getString: expected to find annotation for type of array contents"`; + +exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM 1`] = `"Unsupported type for getString, param: \\"arg\\": expected to find annotation for type of array contents"`; + exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_NOT_EXISTING_TYPE_AS_PARAM 1`] = `"Unsupported param type for method \\"getString\\", param \\"arg\\". Found GenericTypeAnnotation"`; exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_NOT_EXISTING_TYPE_AS_RETURN 1`] = `"Unsupported param type for method \\"getString\\", param \\"arg\\". Found GenericTypeAnnotation"`; @@ -3320,6 +3324,82 @@ Object { } `; +exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS 1`] = ` +Object { + "modules": Object { + "SampleTurboModule": Object { + "nativeModules": Object { + "SampleTurboModule": Object { + "properties": Array [ + Object { + "name": "getArray", + "typeAnnotation": Object { + "params": Array [ + Object { + "name": "arg", + "typeAnnotation": Object { + "elementType": Object { + "type": "StringTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + }, + ], + "returnTypeAnnotation": Object { + "elementType": Object { + "type": "StringTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "FunctionTypeAnnotation", + }, + }, + ], + }, + }, + }, + }, +} +`; + +exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_ARRAY 1`] = ` +Object { + "modules": Object { + "SampleTurboModule": Object { + "nativeModules": Object { + "SampleTurboModule": Object { + "properties": Array [ + Object { + "name": "getArray", + "typeAnnotation": Object { + "params": Array [ + Object { + "name": "arg", + "typeAnnotation": Object { + "elementType": Object { + "type": "StringTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + }, + ], + "returnTypeAnnotation": Object { + "elementType": Object { + "type": "StringTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "FunctionTypeAnnotation", + }, + }, + ], + }, + }, + }, + }, +} +`; + exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_PARAM_TYPES 1`] = ` Object { "modules": Object { @@ -3386,6 +3466,62 @@ Object { } `; +exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_ARRAY 1`] = ` +Object { + "modules": Object { + "SampleTurboModule": Object { + "nativeModules": Object { + "SampleTurboModule": Object { + "properties": Array [ + Object { + "name": "getArray", + "typeAnnotation": Object { + "params": Array [ + Object { + "name": "arg", + "typeAnnotation": Object { + "elementType": Object { + "elementType": Object { + "elementType": Object { + "elementType": Object { + "elementType": Object { + "type": "AnyTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + }, + ], + "returnTypeAnnotation": Object { + "elementType": Object { + "elementType": Object { + "elementType": Object { + "type": "StringTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "ArrayTypeAnnotation", + }, + "type": "FunctionTypeAnnotation", + }, + }, + ], + }, + }, + }, + }, +} +`; + exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_WITH_ALIASES 1`] = ` Object { "modules": Object { diff --git a/packages/react-native-codegen/src/parsers/flow/methods.js b/packages/react-native-codegen/src/parsers/flow/methods.js index d7055d90ce1..2577ade85ed 100644 --- a/packages/react-native-codegen/src/parsers/flow/methods.js +++ b/packages/react-native-codegen/src/parsers/flow/methods.js @@ -13,8 +13,9 @@ import type { MethodTypeShape, FunctionTypeAnnotationParam, - FunctionTypeAnnotationParamTypeAnnotation, FunctionTypeAnnotationReturn, + PrimitiveTypeAnnotation, + FunctionTypeAnnotationParamTypeAnnotation, } from '../../CodegenSchema.js'; function getValueFromTypes(value, types) { @@ -31,7 +32,7 @@ function wrapPrimitiveIntoTypeAnnotation( | 'NumberTypeAnnotation' | 'StringTypeAnnotation', paramName: string, -): FunctionTypeAnnotationParamTypeAnnotation { +): PrimitiveTypeAnnotation { switch (type) { case 'BooleanTypeAnnotation': case 'NumberTypeAnnotation': @@ -47,23 +48,110 @@ function wrapPrimitiveIntoTypeAnnotation( } } +function getElementTypeForArray( + name, + arrayParam, + paramName, + types: $ReadOnlyArray, +): FunctionTypeAnnotationParamTypeAnnotation { + const typeAnnotation = getValueFromTypes(arrayParam, types); + if ( + typeAnnotation.type === 'GenericTypeAnnotation' && + typeAnnotation.id.name === 'Array' + ) { + if ( + typeAnnotation.typeParameters && + typeAnnotation.typeParameters.params[0] + ) { + return { + type: 'ArrayTypeAnnotation', + elementType: getElementTypeForArray( + name, + typeAnnotation.typeParameters.params[0], + 'returning value', + types, + ), + }; + } else { + throw new Error( + `Unsupported type for ${name}, param: "${paramName}": expected to find annotation for type of nested array contents`, + ); + } + } + const type = typeAnnotation.type; + if (type === 'AnyTypeAnnotation') { + return { + type, + }; + } + return wrapPrimitiveIntoTypeAnnotation(name, type, paramName); +} + function getTypeAnnotationForParam( name: string, param, types: $ReadOnlyArray, ): FunctionTypeAnnotationParam { - const type = getValueFromTypes(param.typeAnnotation, types).type; + const typeAnnotation = getValueFromTypes(param.typeAnnotation, types); const paramName = param.name.name; - const typeAnnotation = wrapPrimitiveIntoTypeAnnotation(name, type, paramName); + if ( + typeAnnotation.type === 'GenericTypeAnnotation' && + typeAnnotation.id.name === 'Array' + ) { + if ( + typeAnnotation.typeParameters && + typeAnnotation.typeParameters.params[0] + ) { + return { + name: paramName, + typeAnnotation: { + type: 'ArrayTypeAnnotation', + elementType: getElementTypeForArray( + name, + typeAnnotation.typeParameters.params[0], + paramName, + types, + ), + }, + }; + } else { + throw new Error( + `Unsupported type for ${name}, param: "${paramName}": expected to find annotation for type of array contents`, + ); + } + } + const type = typeAnnotation.type; return { name: paramName, - typeAnnotation, + typeAnnotation: wrapPrimitiveIntoTypeAnnotation(name, type, paramName), }; } function getReturnTypeAnnotation( methodName: string, - type, + returnType, + types: $ReadOnlyArray, ): FunctionTypeAnnotationReturn { + if ( + returnType.type === 'GenericTypeAnnotation' && + returnType.id.name === 'Array' + ) { + if (returnType.typeParameters && returnType.typeParameters.params[0]) { + return { + type: 'ArrayTypeAnnotation', + elementType: getElementTypeForArray( + methodName, + returnType.typeParameters.params[0], + 'returning value', + types, + ), + }; + } else { + throw new Error( + `Unsupported return type for ${methodName}: expected to find annotation for type of array contents`, + ); + } + } + const type = returnType.type; switch (type) { case 'BooleanTypeAnnotation': case 'NumberTypeAnnotation': @@ -99,7 +187,8 @@ function buildMethodSchema( const returnTypeAnnotation = getReturnTypeAnnotation( name, - getValueFromTypes(value.returnType, types).type, + getValueFromTypes(value.returnType, types), + types, ); return { name,