From 2f25261b8886363855f3653be840332e2c9ff9e0 Mon Sep 17 00:00:00 2001 From: Pranav Yadav Date: Mon, 20 Mar 2023 07:32:14 -0700 Subject: [PATCH] Merge TS & Flow parsers' logic for `Partial` case to `emitPartial` fn & `commonTypes` cases into `emitCommonTypes` fn (#36450) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: > [Codegen 78 - Assigned to Pranav-yadav] It depends on [Codegen 75][Codegen 76][Codegen 77] Extract the logic that emits Partial values in an emitPartial function, which takes the Parsers as parameter. >[Codegen 79 - Assigned to Pranav-yadav] It depends on [Codegen 78] Extract the basic cases logics (case Stringish, case Int32, case Double, ..., case Partial. `Flow` lines and `TypeScript` lines into a function emitCommonTypes in `parsers-primitives.js`. Make sure that the default case returns `null`. Call this function in the default: case `Flow`, `TypeScript` of the `index.js` file: if the function return something, return that from the default case; otherwise if the `emitCommonTypes` returns `null`, keep the current default implementation (throw the error). ### Changes - merged TS & Flow parsers' logic for `Partial` case to `emitPatial` fn - merged TS & Flow parsers' logic for below cases: - `Stringish` - `Int32` - `Double` - `Float` - `UnsafeObject` - `Object` - `Partial` - into an `emitCommonTypes` fn into `parsers-primitives.js` - add **_tests_** for `emitPartial` and `emitCommonTypes` fn's - add `getAnnotatedElementProperties` fn to parser & impl to both TS & Flow parsers ## Changelog: [INTERNAL] [CHANGED] - Merge TS & Flow parsers' logic for `Partial` case to `emitPatial` fn & `commonTypes` cases into `emitCommonTypes` fn Pull Request resolved: https://github.com/facebook/react-native/pull/36450 Test Plan: - `yarn lint && yarn run flow && yarn jest react-native` ⇒ � Reviewed By: rshest Differential Revision: D44132308 Pulled By: cipolleschi fbshipit-source-id: f965e85ecc5d94e57ad85334ce565a55c512fde4 --- .../__tests__/parsers-primitives-test.js | 242 ++++++++++++++++++ .../src/parsers/flow/modules/index.js | 65 +---- .../src/parsers/flow/parser.js | 4 + .../src/parsers/parser.js | 7 + .../src/parsers/parserMock.js | 4 + .../src/parsers/parsers-primitives.js | 90 +++++++ .../src/parsers/typescript/modules/index.js | 62 ++--- .../src/parsers/typescript/parser.js | 4 + 8 files changed, 380 insertions(+), 98 deletions(-) diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js index 6c09b098daaf84..2574005f71e6b9 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-primitives-test.js @@ -28,6 +28,8 @@ const { emitString, emitStringish, emitMixed, + emitPartial, + emitCommonTypes, typeAliasResolution, typeEnumResolution, Visitor, @@ -1253,3 +1255,243 @@ describe('Visitor', () => { }); }); }); + +describe('emitPartial', () => { + const hasteModuleName = 'SampleTurboModule'; + function emitPartialForUnitTest( + typeAnnotation: $FlowFixMe, + nullable: boolean, + ): $FlowFixMe { + return emitPartial( + hasteModuleName, + typeAnnotation, + /* types: TypeDeclarationMap */ + {}, + /* aliasMap: {...NativeModuleAliasMap} */ + {}, + /* enumMap: {...NativeModuleEnumMap} */ + {}, + /* tryParse: ParserErrorCapturer */ + // $FlowFixMe[missing-local-annot] + function (_: () => T) { + return null; + }, + /* cxxOnly: boolean */ + false, + nullable, + parser, + ); + } + + describe("when 'typeAnnotation' doesn't have exactly 'one' typeParameter", () => { + const nullable = false; + const typeAnnotation = { + typeParameters: { + params: [1, 2], + type: 'TypeParameterInstantiation', + }, + id: { + name: 'typeAnnotationName', + }, + }; + + it('throws an error', () => { + expect(() => emitPartialForUnitTest(typeAnnotation, nullable)).toThrow( + 'Partials only support annotating exactly one parameter.', + ); + }); + }); + + describe('when Partial Not annotating type parameter', () => { + const nullable = false; + const typeAnnotation = { + typeParameters: { + params: [ + { + id: { + name: 'TypeDeclaration', + }, + }, + ], + }, + id: { + name: 'typeAnnotationName', + }, + }; + + it('throws an error', () => { + expect(() => emitPartialForUnitTest(typeAnnotation, nullable)).toThrow( + 'Partials only support annotating a type parameter.', + ); + }); + }); +}); + +describe('emitCommonTypes', () => { + const hasteModuleName = 'SampleTurboModule'; + + function emitCommonTypesForUnitTest( + typeAnnotation: $FlowFixMe, + nullable: boolean, + ): $FlowFixMe { + return emitCommonTypes( + hasteModuleName, + /* types: TypeDeclarationMap */ + {}, + typeAnnotation, + /* aliasMap: {...NativeModuleAliasMap} */ + {}, + /* enumMap: {...NativeModuleEnumMap} */ + {}, + /* tryParse: ParserErrorCapturer */ + // $FlowFixMe[missing-local-annot] + function (_: () => T) { + return null; + }, + /* cxxOnly: boolean */ + false, + nullable, + parser, + ); + } + + describe("when 'typeAnnotation.id.name' is 'Stringish'", () => { + const typeAnnotation = { + typeParameters: { + params: [1, 2], + type: 'StringTypeAnnotation', + }, + id: { + name: 'Stringish', + }, + }; + const expected = { + type: 'StringTypeAnnotation', + }; + const result = emitCommonTypesForUnitTest(typeAnnotation, false); + + it("returns 'StringTypeAnnotation'", () => { + expect(result).toEqual(expected); + }); + }); + + describe("when 'typeAnnotation.id.name' is 'Int32'", () => { + const typeAnnotation = { + typeParameters: { + params: [1, 2], + type: 'Int32TypeAnnotation', + }, + id: { + name: 'Int32', + }, + }; + const expected = { + type: 'Int32TypeAnnotation', + }; + const result = emitCommonTypesForUnitTest(typeAnnotation, false); + + it("returns 'Int32TypeAnnotation'", () => { + expect(result).toEqual(expected); + }); + }); + + describe("when 'typeAnnotation.id.name' is 'Double'", () => { + const typeAnnotation = { + typeParameters: { + params: [1, 2], + type: 'DoubleTypeAnnotation', + }, + id: { + name: 'Double', + }, + }; + const expected = { + type: 'DoubleTypeAnnotation', + }; + const result = emitCommonTypesForUnitTest(typeAnnotation, false); + + it("returns 'DoubleTypeAnnotation'", () => { + expect(result).toEqual(expected); + }); + }); + + describe("when 'typeAnnotation.id.name' is 'Float'", () => { + const typeAnnotation = { + typeParameters: { + params: [1, 2], + type: 'FloatTypeAnnotation', + }, + id: { + name: 'Float', + }, + }; + const expected = { + type: 'FloatTypeAnnotation', + }; + const result = emitCommonTypesForUnitTest(typeAnnotation, false); + + it("returns 'FloatTypeAnnotation'", () => { + expect(result).toEqual(expected); + }); + }); + + describe("when 'typeAnnotation.id.name' is 'UnsafeObject'", () => { + const typeAnnotation = { + typeParameters: { + params: [1, 2], + type: 'GenericObjectTypeAnnotation', + }, + id: { + name: 'UnsafeObject', + }, + }; + const expected = { + type: 'GenericObjectTypeAnnotation', + }; + const result = emitCommonTypesForUnitTest(typeAnnotation, false); + + it("returns 'GenericObjectTypeAnnotation'", () => { + expect(result).toEqual(expected); + }); + }); + + describe("when 'typeAnnotation.id.name' is 'Object'", () => { + const typeAnnotation = { + typeParameters: { + params: [1, 2], + type: 'GenericObjectTypeAnnotation', + }, + id: { + name: 'Object', + }, + }; + const expected = { + type: 'GenericObjectTypeAnnotation', + }; + const result = emitCommonTypesForUnitTest(typeAnnotation, false); + + it("returns 'GenericObjectTypeAnnotation'", () => { + expect(result).toEqual(expected); + }); + }); + + describe("when 'typeAnnotation.id.name' is '$Partial' i.e. Object", () => { + const typeAnnotation = { + typeParameters: { + params: [1], + type: 'GenericObjectTypeAnnotation', + }, + id: { + name: 'Object', + }, + }; + const expected = { + type: 'GenericObjectTypeAnnotation', + }; + const result = emitCommonTypesForUnitTest(typeAnnotation, false); + + it("returns 'GenericObjectTypeAnnotation'", () => { + expect(result).toEqual(expected); + }); + }); +}); diff --git a/packages/react-native-codegen/src/parsers/flow/modules/index.js b/packages/react-native-codegen/src/parsers/flow/modules/index.js index 1ab013270c8b90..13c3fc91ca7f2f 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -32,20 +32,16 @@ const { const { emitArrayType, emitBoolean, - emitDouble, - emitFloat, emitFunction, emitNumber, - emitInt32, emitGenericObject, - emitObject, emitPromise, emitRootTag, emitVoid, emitString, - emitStringish, emitMixed, emitUnion, + emitCommonTypes, typeAliasResolution, typeEnumResolution, } = require('../../parsers-primitives'); @@ -55,11 +51,6 @@ const { UnsupportedGenericParserError, } = require('../../errors'); -const { - throwIfPartialNotAnnotatingTypeParameter, - throwIfPartialWithMoreParameter, -} = require('../../error-utils'); - function translateTypeAnnotation( hasteModuleName: string, /** @@ -132,55 +123,27 @@ function translateTypeAnnotation( return wrapNullable(nullable || isParamNullable, paramType); } - case 'Stringish': { - return emitStringish(nullable); - } - case 'Int32': { - return emitInt32(nullable); - } - case 'Double': { - return emitDouble(nullable); - } - case 'Float': { - return emitFloat(nullable); - } - case 'UnsafeObject': - case 'Object': { - return emitGenericObject(nullable); - } - case 'Partial': - case '$Partial': { - throwIfPartialWithMoreParameter(typeAnnotation); - - const annotatedElement = parser.extractAnnotatedElement( - typeAnnotation, - types, - ); - - throwIfPartialNotAnnotatingTypeParameter( - typeAnnotation, - types, - parser, - ); - - const properties = parser.computePartialProperties( - annotatedElement.right.properties, + default: { + const commonType = emitCommonTypes( hasteModuleName, types, + typeAnnotation, aliasMap, enumMap, tryParse, cxxOnly, - ); - - return emitObject(nullable, properties); - } - default: { - throw new UnsupportedGenericParserError( - hasteModuleName, - typeAnnotation, + nullable, parser, ); + + if (!commonType) { + throw new UnsupportedGenericParserError( + hasteModuleName, + typeAnnotation, + parser, + ); + } + return commonType; } } } diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index 46e3a882923b5f..f82a52dc18d5c9 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -313,6 +313,10 @@ class FlowParser implements Parser { componentName: funcArgumentParams[0].value, }; } + + getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe { + return annotatedElement.right.properties; + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index 6195e640e86447..fc1777bda932c1 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -229,4 +229,11 @@ export interface Parser { typeArgumentParams: $FlowFixMe, funcArgumentParams: $FlowFixMe, ): {[string]: string}; + + /** + * Given a annotatedElement, it returns the properties of annotated element. + * @parameter annotatedElement: the annotated element. + * @returns: the properties of annotated element. + */ + getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe; } diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 9e85fae12526ab..81c88c7e6cbcd3 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -227,4 +227,8 @@ export class MockedParser implements Parser { componentName: funcArgumentParams[0].value, }; } + + getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe { + return annotatedElement.right.properties; + } } diff --git a/packages/react-native-codegen/src/parsers/parsers-primitives.js b/packages/react-native-codegen/src/parsers/parsers-primitives.js index 13fcb2ce198fdc..ef2a3b605cd314 100644 --- a/packages/react-native-codegen/src/parsers/parsers-primitives.js +++ b/packages/react-native-codegen/src/parsers/parsers-primitives.js @@ -49,6 +49,8 @@ const { const { throwIfArrayElementTypeAnnotationIsUnsupported, + throwIfPartialNotAnnotatingTypeParameter, + throwIfPartialWithMoreParameter, } = require('./error-utils'); const {nullGuard} = require('./parsers-utils'); const { @@ -473,6 +475,92 @@ function Visitor(infoMap: {isComponent: boolean, isModule: boolean}): { }; } +function emitPartial( + hasteModuleName: string, + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, + tryParse: ParserErrorCapturer, + cxxOnly: boolean, + nullable: boolean, + parser: Parser, +): Nullable { + throwIfPartialWithMoreParameter(typeAnnotation); + + throwIfPartialNotAnnotatingTypeParameter(typeAnnotation, types, parser); + + const annotatedElement = parser.extractAnnotatedElement( + typeAnnotation, + types, + ); + const annotatedElementProperties = + parser.getAnnotatedElementProperties(annotatedElement); + + const partialProperties = parser.computePartialProperties( + annotatedElementProperties, + hasteModuleName, + types, + aliasMap, + enumMap, + tryParse, + cxxOnly, + ); + + return emitObject(nullable, partialProperties); +} + +function emitCommonTypes( + hasteModuleName: string, + types: TypeDeclarationMap, + typeAnnotation: $FlowFixMe, + aliasMap: {...NativeModuleAliasMap}, + enumMap: {...NativeModuleEnumMap}, + tryParse: ParserErrorCapturer, + cxxOnly: boolean, + nullable: boolean, + parser: Parser, +): $FlowFixMe { + const genericTypeAnnotationName = + parser.nameForGenericTypeAnnotation(typeAnnotation); + + switch (genericTypeAnnotationName) { + case 'Stringish': { + return emitStringish(nullable); + } + case 'Int32': { + return emitInt32(nullable); + } + case 'Double': { + return emitDouble(nullable); + } + case 'Float': { + return emitFloat(nullable); + } + case 'UnsafeObject': + case 'Object': { + return emitGenericObject(nullable); + } + case '$Partial': + case 'Partial': { + return emitPartial( + hasteModuleName, + typeAnnotation, + types, + aliasMap, + enumMap, + tryParse, + cxxOnly, + nullable, + parser, + ); + } + default: { + return null; + } + } +} + module.exports = { emitArrayType, emitBoolean, @@ -490,6 +578,8 @@ module.exports = { emitStringish, emitMixed, emitUnion, + emitPartial, + emitCommonTypes, typeAliasResolution, typeEnumResolution, translateArrayTypeAnnotation, diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/index.js b/packages/react-native-codegen/src/parsers/typescript/modules/index.js index 056e5dfd496c47..130cfea9419a15 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -31,26 +31,22 @@ const {flattenProperties} = require('../components/componentsUtils'); const {resolveTypeAnnotation} = require('../utils'); const {parseObjectProperty} = require('../../parsers-commons'); -const {typeEnumResolution} = require('../../parsers-primitives'); const { emitArrayType, emitBoolean, - emitDouble, - emitFloat, emitFunction, emitNumber, - emitInt32, emitGenericObject, - emitObject, emitPromise, emitRootTag, emitVoid, emitString, - emitStringish, emitMixed, emitUnion, + emitCommonTypes, typeAliasResolution, + typeEnumResolution, translateArrayTypeAnnotation, } = require('../../parsers-primitives'); @@ -59,11 +55,6 @@ const { UnsupportedTypeAnnotationParserError, } = require('../../errors'); -const { - throwIfPartialNotAnnotatingTypeParameter, - throwIfPartialWithMoreParameter, -} = require('../../error-utils'); - function translateObjectTypeAnnotation( hasteModuleName: string, /** @@ -166,50 +157,27 @@ function translateTypeReferenceAnnotation( translateTypeAnnotation, ); } - case 'Stringish': { - return emitStringish(nullable); - } - case 'Int32': { - return emitInt32(nullable); - } - case 'Double': { - return emitDouble(nullable); - } - case 'Float': { - return emitFloat(nullable); - } - case 'UnsafeObject': - case 'Object': { - return emitGenericObject(nullable); - } - case 'Partial': { - throwIfPartialWithMoreParameter(typeAnnotation); - - const annotatedElement = parser.extractAnnotatedElement( - typeAnnotation, - types, - ); - - throwIfPartialNotAnnotatingTypeParameter(typeAnnotation, types, parser); - - const properties = parser.computePartialProperties( - annotatedElement.typeAnnotation.members, + default: { + const commonType = emitCommonTypes( hasteModuleName, types, + typeAnnotation, aliasMap, enumMap, tryParse, cxxOnly, - ); - - return emitObject(nullable, properties); - } - default: { - throw new UnsupportedGenericParserError( - hasteModuleName, - typeAnnotation, + nullable, parser, ); + + if (!commonType) { + throw new UnsupportedGenericParserError( + hasteModuleName, + typeAnnotation, + parser, + ); + } + return commonType; } } } diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index 464f2ef5fe8b5d..e5dd58f80a4571 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -299,6 +299,10 @@ class TypeScriptParser implements Parser { componentName: funcArgumentParams[0].value, }; } + + getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe { + return annotatedElement.typeAnnotation.members; + } } module.exports = {