From a7664dbaf13e5ff486beec6c73a9421be63445ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Osadnik?= Date: Mon, 8 Jul 2019 09:28:02 -0700 Subject: [PATCH] Add support for nullable params Summary: I'm not very confident with this part, but actually in existing codegen we wrap param into another object marked as `NullableTypeAnnotion`. It makes logic a little more complicated. Also it allows for multiple `?` before type which is useless in code generation as well as nullable types inside arrays which also does not have impact on a final code generation. I suggest adding `nullable` field into param which covers all existing cases (and probably all cases needed). Reviewed By: TheSavior Differential Revision: D16121609 fbshipit-source-id: 6e086d4d26bbd0aab3015ec7ecae106ebbaa5a2c --- .../react-native-codegen/src/CodegenSchema.js | 1 + .../flow/__test_fixtures__/failures.js | 50 +++++++++++++++++++ .../flow/__test_fixtures__/fixtures.js | 26 ++++++++++ .../__snapshots__/parser-test.js.snap | 49 ++++++++++++++++++ .../src/parsers/flow/methods.js | 24 ++++++++- 5 files changed, 148 insertions(+), 2 deletions(-) diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index e7849009c1c..3bc12a11615 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -178,6 +178,7 @@ export type FunctionTypeAnnotationReturn = |}>; export type FunctionTypeAnnotationParam = $ReadOnly<{| + nullable: boolean, name: string, typeAnnotation: FunctionTypeAnnotationParamTypeAnnotation, |}>; 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 dbcc6e82953..cbadbc2e9cd 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 @@ -183,6 +183,54 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule `; +const NATIVE_MODULE_NULLABLE_BOOLEAN = ` +/** + * 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 { + +getSth(a : ?boolean) => void +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + +const NATIVE_MODULE_NULLABLE_NUMBER = ` +/** + * 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 { + +getSth(a : ?number) => void +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + const TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT = ` /** * Copyright (c) Facebook, Inc. and its affiliates. @@ -352,6 +400,8 @@ module.exports = { NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE, NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM, NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT, + NATIVE_MODULE_NULLABLE_BOOLEAN, + NATIVE_MODULE_NULLABLE_NUMBER, 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 bb9c107151a..936eefaea7c 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 @@ -147,6 +147,31 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; +const NATIVE_MODULE_WITH_NULLABLE_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 { + // Exported methods. + +voidFunc: (arg: ?string) => void; +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + const NATIVE_MODULE_WITH_BASIC_ARRAY = ` /** * Copyright (c) Facebook, Inc. and its affiliates. @@ -790,6 +815,7 @@ module.exports = { NATIVE_MODULE_WITH_PROMISE, NATIVE_MODULE_WITH_COMPLEX_OBJECTS, NATIVE_MODULE_WITH_SIMPLE_OBJECT, + NATIVE_MODULE_WITH_NULLABLE_PARAM, NATIVE_MODULE_WITH_BASIC_ARRAY, NATIVE_MODULE_WITH_COMPLEX_ARRAY, NATIVE_MODULE_WITH_ARRAY_WITH_ALIAS, 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 348d4608a19..fe6decfdabd 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_MODULE_NULLABLE_BOOLEAN 1`] = `"Booleans and numbers cannot be nullable for param \\"a in method \\"getSth\\"."`; + +exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULE_NULLABLE_NUMBER 1`] = `"Booleans and numbers cannot be nullable for param \\"a in method \\"getSth\\"."`; + 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"`; @@ -3340,6 +3344,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "elementType": Object { "type": "StringTypeAnnotation", @@ -3379,6 +3384,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "elementType": Object { "type": "StringTypeAnnotation", @@ -3418,6 +3424,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "type": "BooleanTypeAnnotation", }, @@ -3436,6 +3443,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "type": "NumberTypeAnnotation", }, @@ -3454,6 +3462,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "type": "StringTypeAnnotation", }, @@ -3487,6 +3496,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "elementType": Object { "elementType": Object { @@ -3544,6 +3554,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "properties": Array [ Object { @@ -3594,6 +3605,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "properties": Array [ Object { @@ -3620,6 +3632,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "properties": Array [ Object { @@ -3674,6 +3687,40 @@ Object { } `; +exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_NULLABLE_PARAM 1`] = ` +Object { + "modules": Object { + "SampleTurboModule": Object { + "nativeModules": Object { + "SampleTurboModule": Object { + "properties": Array [ + Object { + "name": "voidFunc", + "typeAnnotation": Object { + "optional": false, + "params": Array [ + Object { + "name": "arg", + "nullable": true, + "typeAnnotation": Object { + "type": "StringTypeAnnotation", + }, + }, + ], + "returnTypeAnnotation": Object { + "type": "VoidTypeAnnotation", + }, + "type": "FunctionTypeAnnotation", + }, + }, + ], + }, + }, + }, + }, +} +`; + exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PROMISE 1`] = ` Object { "modules": Object { @@ -3731,6 +3778,7 @@ Object { "params": Array [ Object { "name": "o", + "nullable": false, "typeAnnotation": Object { "type": "ObjectWithoutPropertiesTypeAnnotation", }, @@ -3764,6 +3812,7 @@ Object { "params": Array [ Object { "name": "arg", + "nullable": false, "typeAnnotation": Object { "type": "NumberTypeAnnotation", }, diff --git a/packages/react-native-codegen/src/parsers/flow/methods.js b/packages/react-native-codegen/src/parsers/flow/methods.js index d7a52c6eeb8..5191cd5970a 100644 --- a/packages/react-native-codegen/src/parsers/flow/methods.js +++ b/packages/react-native-codegen/src/parsers/flow/methods.js @@ -123,16 +123,24 @@ function getElementTypeForArrayOrObject( function getTypeAnnotationForParam( name: string, - param, + paramAnnotation, types: $ReadOnlyArray, ): FunctionTypeAnnotationParam { + let param = paramAnnotation; + let paramName = param.name.name; + let nullable = false; + if (param.typeAnnotation.type === 'NullableTypeAnnotation') { + nullable = true; + param = paramAnnotation.typeAnnotation; + } + const typeAnnotation = getValueFromTypes(param.typeAnnotation, types); - const paramName = param.name.name; if ( param.typeAnnotation.type === 'GenericTypeAnnotation' && param.typeAnnotation.id.name === 'Object' ) { return { + nullable, name: paramName, typeAnnotation: { type: 'ObjectWithoutPropertiesTypeAnnotation', @@ -149,6 +157,7 @@ function getTypeAnnotationForParam( ) { return { name: paramName, + nullable, typeAnnotation: { type: 'ArrayTypeAnnotation', elementType: getElementTypeForArrayOrObject( @@ -167,6 +176,7 @@ function getTypeAnnotationForParam( } if (param.typeAnnotation.type === 'ObjectTypeAnnotation') { return { + nullable, name: paramName, typeAnnotation: { type: 'ObjectTypeAnnotation', @@ -180,7 +190,17 @@ function getTypeAnnotationForParam( }; } const type = typeAnnotation.type; + + if ( + nullable && + (type === 'NumberTypeAnnotation' || type === 'BooleanTypeAnnotation') + ) { + throw new Error( + `Booleans and numbers cannot be nullable for param "${paramName} in method "${name}".`, + ); + } return { + nullable, name: paramName, typeAnnotation: wrapPrimitiveIntoTypeAnnotation(name, type, paramName), };