From d7b50113a14e2ada88fef4ae632d13801a22df88 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Dall'Agnol Date: Thu, 10 Nov 2022 09:04:48 -0800 Subject: [PATCH] chore: Add getKeyName function to codegen Parser class (#35202) Summary: This PR adds a `getKeyName` function to the codegen Parser class and implements it in the Flow and TypeScript parsers as requested on https://github.com/facebook/react-native/issues/34872. ## Changelog [Internal] [Added] - Add `getKeyName` function to codegen Parser class Pull Request resolved: https://github.com/facebook/react-native/pull/35202 Test Plan: Run `yarn jest react-native-codegen` and ensure CI is green ![image](https://user-images.githubusercontent.com/11707729/200028600-87e9c1d7-d56d-4cf7-bdbc-18bdf1b03fc5.png) Reviewed By: cipolleschi Differential Revision: D41081711 Pulled By: jacdebug fbshipit-source-id: 7ad2953a0e2f90f04d03270bda40d669d4d0d50a --- .../parsers/__tests__/parsers-commons-test.js | 8 +- .../src/parsers/__tests__/parsers-test.js | 129 ++++++++++++++++++ .../src/parsers/flow/modules/index.js | 2 +- .../src/parsers/flow/parser.js | 21 +++ .../src/parsers/parser.js | 8 ++ .../src/parsers/parserMock.js | 21 +++ .../src/parsers/parsers-commons.js | 32 +---- .../src/parsers/typescript/modules/index.js | 2 +- .../src/parsers/typescript/parser.js | 20 +++ 9 files changed, 210 insertions(+), 33 deletions(-) create mode 100644 packages/react-native-codegen/src/parsers/__tests__/parsers-test.js diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js index 1cb8a15126f562..7ddf2611d9c93d 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js @@ -28,8 +28,10 @@ const { } = require('../errors'); import {MockedParser} from '../parserMock'; +import {TypeScriptParser} from '../typescript/parser'; const parser = new MockedParser(); +const typeScriptParser = new TypeScriptParser(); const flowTranslateTypeAnnotation = require('../flow/modules/index'); const typeScriptTranslateTypeAnnotation = require('../typescript/modules/index'); @@ -316,9 +318,9 @@ describe('parseObjectProperty', () => { aliasMap, tryParse, cxxOnly, - language, nullable, flowTranslateTypeAnnotation, + parser, ), ).toThrow(expected); }); @@ -349,9 +351,9 @@ describe('parseObjectProperty', () => { aliasMap, tryParse, cxxOnly, - language, nullable, typeScriptTranslateTypeAnnotation, + parser, ), ).toThrow(expected); }); @@ -377,9 +379,9 @@ describe('parseObjectProperty', () => { aliasMap, tryParse, cxxOnly, - language, nullable, typeScriptTranslateTypeAnnotation, + typeScriptParser, ); const expected = { name: 'testName', diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-test.js new file mode 100644 index 00000000000000..f3cf49b4f70f66 --- /dev/null +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-test.js @@ -0,0 +1,129 @@ +/** + * 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 + * @oncall react_native + */ + +'use-strict'; + +const { + UnsupportedObjectPropertyTypeAnnotationParserError, +} = require('../errors'); + +import {TypeScriptParser} from '../typescript/parser'; +import {FlowParser} from '../flow/parser'; + +const hasteModuleName = 'moduleName'; +describe('TypeScriptParser', () => { + const parser = new FlowParser(); + describe('getKeyName', () => { + describe('when propertyOrIndex is ObjectTypeProperty', () => { + it('returns property name', () => { + const property = { + type: 'ObjectTypeProperty', + key: { + name: 'propertyName', + }, + }; + + const expected = 'propertyName'; + + expect(parser.getKeyName(property, hasteModuleName)).toEqual(expected); + }); + }); + + describe('when propertyOrIndex is ObjectTypeIndexer', () => { + it('returns indexer name', () => { + const indexer = { + type: 'ObjectTypeIndexer', + id: { + name: 'indexerName', + }, + }; + + const expected = 'indexerName'; + + expect(parser.getKeyName(indexer, hasteModuleName)).toEqual(expected); + }); + + it('returns `key` if indexer has no name', () => { + const indexer = { + type: 'ObjectTypeIndexer', + id: {}, + }; + + const expected = 'key'; + + expect(parser.getKeyName(indexer, hasteModuleName)).toEqual(expected); + }); + }); + + describe('when propertyOrIndex is not ObjectTypeProperty or ObjectTypeIndexer', () => { + it('throw UnsupportedObjectPropertyTypeAnnotationParserError', () => { + const indexer = { + type: 'EnumDeclaration', + memberType: 'NumberTypeAnnotation', + }; + + expect(() => parser.getKeyName(indexer, hasteModuleName)).toThrowError( + UnsupportedObjectPropertyTypeAnnotationParserError, + ); + }); + }); + }); +}); + +describe('FlowParser', () => { + const parser = new TypeScriptParser(); + describe('getKeyName', () => { + describe('when propertyOrIndex is TSPropertySignature', () => { + it('returns property name', () => { + const property = { + type: 'TSPropertySignature', + key: { + name: 'propertyName', + }, + }; + + const expected = 'propertyName'; + + expect(parser.getKeyName(property, hasteModuleName)).toEqual(expected); + }); + }); + + describe('when propertyOrIndex is TSIndexSignature', () => { + it('returns indexer name', () => { + const indexer = { + type: 'TSIndexSignature', + parameters: [ + { + name: 'indexerName', + }, + ], + }; + + const expected = 'indexerName'; + + expect(parser.getKeyName(indexer, hasteModuleName)).toEqual(expected); + }); + }); + + describe('when propertyOrIndex is not TSPropertySignature or TSIndexSignature', () => { + it('throw UnsupportedObjectPropertyTypeAnnotationParserError', () => { + const indexer = { + type: 'TSEnumDeclaration', + memberType: 'NumberTypeAnnotation', + }; + + expect(() => parser.getKeyName(indexer, hasteModuleName)).toThrowError( + UnsupportedObjectPropertyTypeAnnotationParserError, + ); + }); + }); + }); +}); 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 14a26b31392017..c6ba77ea7a68fd 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -266,9 +266,9 @@ function translateTypeAnnotation( aliasMap, tryParse, cxxOnly, - language, nullable, translateTypeAnnotation, + parser, ); }); }, diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index 44f2c122b1e76f..eb6e1b9b711ada 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -13,9 +13,30 @@ import type {ParserType} from '../errors'; import type {Parser} from '../parser'; +const { + UnsupportedObjectPropertyTypeAnnotationParserError, +} = require('../errors'); + class FlowParser implements Parser { typeParameterInstantiation: string = 'TypeParameterInstantiation'; + getKeyName(propertyOrIndex: $FlowFixMe, hasteModuleName: string): string { + switch (propertyOrIndex.type) { + case 'ObjectTypeProperty': + return propertyOrIndex.key.name; + case 'ObjectTypeIndexer': + // flow index name is optional + return propertyOrIndex.id?.name ?? 'key'; + default: + throw new UnsupportedObjectPropertyTypeAnnotationParserError( + hasteModuleName, + propertyOrIndex, + propertyOrIndex.type, + this.language(), + ); + } + } + getMaybeEnumMemberType(maybeEnumDeclaration: $FlowFixMe): string { return maybeEnumDeclaration.body.type .replace('EnumNumberBody', 'NumberTypeAnnotation') diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index 40ba98b7cd1405..ef61b1f364c498 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -22,6 +22,14 @@ export interface Parser { */ typeParameterInstantiation: string; + /** + * Given a property or an index declaration, it returns the key name. + * @parameter propertyOrIndex: an object containing a property or an index declaration. + * @parameter hasteModuleName: a string with the native module name. + * @returns: the key name. + * @throws if propertyOrIndex does not contain a property or an index declaration. + */ + getKeyName(propertyOrIndex: $FlowFixMe, hasteModuleName: string): string; /** * Given a type declaration, it possibly returns the name of the Enum type. * @parameter maybeEnumDeclaration: an object possibly containing an Enum declaration. diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 75242b21f93795..78a36f98a21f58 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -13,9 +13,30 @@ import type {Parser} from './parser'; import type {ParserType} from './errors'; +const { + UnsupportedObjectPropertyTypeAnnotationParserError, +} = require('./errors'); + export class MockedParser implements Parser { typeParameterInstantiation: string = 'TypeParameterInstantiation'; + getKeyName(propertyOrIndex: $FlowFixMe, hasteModuleName: string): string { + switch (propertyOrIndex.type) { + case 'ObjectTypeProperty': + return propertyOrIndex.key.name; + case 'ObjectTypeIndexer': + // flow index name is optional + return propertyOrIndex.id?.name ?? 'key'; + default: + throw new UnsupportedObjectPropertyTypeAnnotationParserError( + hasteModuleName, + propertyOrIndex, + propertyOrIndex.type, + this.language(), + ); + } + } + getMaybeEnumMemberType(maybeEnumDeclaration: $FlowFixMe): string { return maybeEnumDeclaration.body.type .replace('EnumNumberBody', 'NumberTypeAnnotation') diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index 4c14a5df546392..8ceaa58d17dc33 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -129,10 +129,12 @@ function parseObjectProperty( aliasMap: {...NativeModuleAliasMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, - language: ParserType, nullable: boolean, translateTypeAnnotation: $FlowFixMe, + parser: Parser, ): NamedShape> { + const language = parser.language(); + if (!isObjectProperty(property, language)) { throw new UnsupportedObjectPropertyTypeAnnotationParserError( hasteModuleName, @@ -143,7 +145,7 @@ function parseObjectProperty( } const {optional = false} = property; - const name = getKeyName(property, hasteModuleName, language); + const name = parser.getKeyName(property, hasteModuleName); const languageTypeAnnotation = language === 'TypeScript' ? property.typeAnnotation.typeAnnotation @@ -282,31 +284,6 @@ function translateDefault( ); } -function getKeyName( - propertyOrIndex: $FlowFixMe, - hasteModuleName: string, - language: ParserType, -): string { - switch (propertyOrIndex.type) { - case 'ObjectTypeProperty': - case 'TSPropertySignature': - return propertyOrIndex.key.name; - case 'ObjectTypeIndexer': - // flow index name is optional - return propertyOrIndex.id?.name ?? 'key'; - case 'TSIndexSignature': - // TypeScript index name is mandatory - return propertyOrIndex.parameters[0].name; - default: - throw new UnsupportedObjectPropertyTypeAnnotationParserError( - hasteModuleName, - propertyOrIndex, - propertyOrIndex.type, - language, - ); - } -} - module.exports = { wrapModuleSchema, unwrapNullable, @@ -316,5 +293,4 @@ module.exports = { parseObjectProperty, emitUnionTypeAnnotation, translateDefault, - getKeyName, }; 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 32ea1769420881..07f72dd7acc1f0 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -272,9 +272,9 @@ function translateTypeAnnotation( aliasMap, tryParse, cxxOnly, - language, nullable, translateTypeAnnotation, + parser, ); }); }, diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index 5f0be54410dc66..2afbc75863fc6b 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -13,9 +13,29 @@ import type {ParserType} from '../errors'; import type {Parser} from '../parser'; +const { + UnsupportedObjectPropertyTypeAnnotationParserError, +} = require('../errors'); + class TypeScriptParser implements Parser { typeParameterInstantiation: string = 'TSTypeParameterInstantiation'; + getKeyName(propertyOrIndex: $FlowFixMe, hasteModuleName: string): string { + switch (propertyOrIndex.type) { + case 'TSPropertySignature': + return propertyOrIndex.key.name; + case 'TSIndexSignature': + return propertyOrIndex.parameters[0].name; + default: + throw new UnsupportedObjectPropertyTypeAnnotationParserError( + hasteModuleName, + propertyOrIndex, + propertyOrIndex.type, + this.language(), + ); + } + } + getMaybeEnumMemberType(maybeEnumDeclaration: $FlowFixMe): string { if (maybeEnumDeclaration.members[0].initializer) { return maybeEnumDeclaration.members[0].initializer.type