From 969a8d07992aeac89fd064ec14cb86e26193caf0 Mon Sep 17 00:00:00 2001 From: Kyaw Thura <44088165+kyawthura-gg@users.noreply.github.com> Date: Wed, 29 Mar 2023 09:28:00 -0700 Subject: [PATCH] Enrich getCommandProperties with the parser object (#36500) Summary: > [Codegen 101] The code of getCommandProperties is almost identical in [Flow](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/flow/components/index.js#L128) and [TS](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/typescript/components/index.js#L129). There are small differences between flow/ts, so we need for it to accept a Parser object. Enrich the parser object with the required methods if necessary. ## Changelog: [Internal] [Changed] - Enrich getCommandProperties with the parser object Pull Request resolved: https://github.com/facebook/react-native/pull/36500 Test Plan: `yarn test react-native-codegen` Reviewed By: cortinico Differential Revision: D44415265 Pulled By: cipolleschi fbshipit-source-id: ed13b553a6f782beb0f1aec79bd17d865a96fac9 --- .../parsers/__tests__/parsers-commons-test.js | 20 ++++++++ .../src/parsers/flow/components/index.js | 47 +++++++------------ .../src/parsers/flow/parser.js | 4 ++ .../src/parsers/parser.js | 7 +++ .../src/parsers/parserMock.js | 4 ++ .../src/parsers/parsers-commons.js | 10 +++- .../parsers/typescript/components/index.js | 45 +++++++----------- .../src/parsers/typescript/parser.js | 4 ++ 8 files changed, 82 insertions(+), 59 deletions(-) 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 dc16fad3e1d..4704e445f75 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 @@ -21,6 +21,7 @@ import { buildSchema, parseModuleName, createComponentConfig, + propertyNames, getCommandOptions, getOptions, getCommandTypeNameAndOptionsExpression, @@ -1536,4 +1537,23 @@ describe('getCommandTypeNameAndOptionsExpression', () => { commandOptionsExpression, }); }); + + describe('propertyNames', () => { + it('returns propertyNames with valid properties', () => { + const properties = [ + {key: {name: 'testName'}}, + {key: {name: 'testName2'}}, + ]; + const expected = ['testName', 'testName2']; + expect(propertyNames(properties)).toEqual(expected); + }); + + it('returns empty propertyNames with incorrect properties', () => { + const properties = [ + {key: {invalid: 'testName'}}, + {key: {invalid: 'testName2'}}, + ]; + expect(propertyNames(properties)).toEqual([]); + }); + }); }); diff --git a/packages/react-native-codegen/src/parsers/flow/components/index.js b/packages/react-native-codegen/src/parsers/flow/components/index.js index b5d55e2702c..30c613e8962 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/index.js +++ b/packages/react-native-codegen/src/parsers/flow/components/index.js @@ -10,8 +10,6 @@ 'use strict'; import type {Parser} from '../../parser'; -import type {TypeDeclarationMap} from '../../utils'; -import type {CommandOptions} from '../../parsers-commons'; import type {ComponentSchemaBuilderConfig} from '../../schema.js'; const {getCommands} = require('./commands'); @@ -23,6 +21,7 @@ const {throwIfMoreThanOneCodegenNativecommands} = require('../../error-utils'); const { createComponentConfig, findNativeComponentType, + propertyNames, getCommandOptions, getOptions, getCommandTypeNameAndOptionsExpression, @@ -62,16 +61,16 @@ function findComponentConfig(ast: $FlowFixMe, parser: Parser) { return createComponentConfig(foundConfig, commandsTypeNames); } -function getCommandProperties( - /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's - * LTI update could not be added via codemod */ - commandTypeName, - types: TypeDeclarationMap, - commandOptions: ?CommandOptions, -) { +function getCommandProperties(ast: $FlowFixMe, parser: Parser) { + const {commandTypeName, commandOptionsExpression} = findComponentConfig( + ast, + parser, + ); + if (commandTypeName == null) { return []; } + const types = parser.getTypes(ast); const typeAlias = types[commandTypeName]; @@ -81,18 +80,16 @@ function getCommandProperties( ); } - let properties; - try { - properties = typeAlias.body.properties; - } catch (e) { + const properties = parser.bodyProperties(typeAlias); + if (!properties) { throw new Error( `Failed to find type definition for "${commandTypeName}", please check that you have a valid codegen flow file`, ); } - const flowPropertyNames = properties - .map(property => property && property.key && property.key.name) - .filter(Boolean); + const flowPropertyNames = propertyNames(properties); + + const commandOptions = getCommandOptions(commandOptionsExpression); if (commandOptions == null || commandOptions.supportedCommands == null) { throw new Error( @@ -121,24 +118,16 @@ function buildComponentSchema( ast: $FlowFixMe, parser: Parser, ): ComponentSchemaBuilderConfig { - const { - componentName, - propsTypeName, - commandTypeName, - commandOptionsExpression, - optionsExpression, - } = findComponentConfig(ast, parser); + const {componentName, propsTypeName, optionsExpression} = findComponentConfig( + ast, + parser, + ); const types = parser.getTypes(ast); const propProperties = getProperties(propsTypeName, types); - const commandOptions = getCommandOptions(commandOptionsExpression); - const commandProperties = getCommandProperties( - commandTypeName, - types, - commandOptions, - ); + const commandProperties = getCommandProperties(ast, parser); const extendsProps = getExtendsProps(propProperties, types); const options = getOptions(optionsExpression); diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index 2e5da42e134..700a521eb76 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -321,6 +321,10 @@ class FlowParser implements Parser { getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe { return annotatedElement.right.properties; } + + bodyProperties(typeAlias: $FlowFixMe): $ReadOnlyArray<$FlowFixMe> { + return typeAlias.body.properties; + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index 78d10ba141a..d4e5ab86a3e 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -241,4 +241,11 @@ export interface Parser { * @returns: the properties of annotated element. */ getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe; + + /** + * Given a typeAlias, it returns an array of properties. + * @parameter typeAlias: the type alias. + * @returns: an array of properties. + */ + bodyProperties(typeAlias: $FlowFixMe): $ReadOnlyArray<$FlowFixMe>; } diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 2db705cdba5..c2b04e61401 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -235,4 +235,8 @@ export class MockedParser implements Parser { getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe { return annotatedElement.right.properties; } + + bodyProperties(typeAlias: $FlowFixMe): $ReadOnlyArray<$FlowFixMe> { + return typeAlias.body.properties; + } } diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index 32361f7c40e..6fbb0baf8e8 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -765,7 +765,6 @@ function getOptions(optionsExpression: OptionsAST): ?OptionsShape { 'Failed to parse codegen options, cannot use both paperComponentName and paperComponentNameDeprecated', ); } - return foundOptions; } @@ -810,6 +809,14 @@ function getCommandTypeNameAndOptionsExpression( }; } +function propertyNames( + properties: $ReadOnlyArray<$FlowFixMe>, +): $ReadOnlyArray<$FlowFixMe> { + return properties + .map(property => property && property.key && property.key.name) + .filter(Boolean); +} + module.exports = { wrapModuleSchema, unwrapNullable, @@ -825,6 +832,7 @@ module.exports = { parseModuleName, buildModuleSchema, findNativeComponentType, + propertyNames, getCommandOptions, getOptions, getCommandTypeNameAndOptionsExpression, diff --git a/packages/react-native-codegen/src/parsers/typescript/components/index.js b/packages/react-native-codegen/src/parsers/typescript/components/index.js index 63270f66fd9..20c24bc0a79 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/index.js @@ -11,8 +11,6 @@ 'use strict'; import type {ExtendsPropsShape} from '../../../CodegenSchema.js'; import type {Parser} from '../../parser'; -import type {TypeDeclarationMap} from '../../utils'; -import type {CommandOptions} from '../../parsers-commons'; import type {ComponentSchemaBuilderConfig} from '../../schema.js'; const {getCommands} = require('./commands'); @@ -24,6 +22,7 @@ const {throwIfMoreThanOneCodegenNativecommands} = require('../../error-utils'); const { createComponentConfig, findNativeComponentType, + propertyNames, getCommandOptions, getOptions, getCommandTypeNameAndOptionsExpression, @@ -63,17 +62,16 @@ function findComponentConfig(ast: $FlowFixMe, parser: Parser) { return createComponentConfig(foundConfig, commandsTypeNames); } -function getCommandProperties( - /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's - * LTI update could not be added via codemod */ - commandTypeName, - types: TypeDeclarationMap, - commandOptions: ?CommandOptions, -) { +function getCommandProperties(ast: $FlowFixMe, parser: Parser) { + const {commandTypeName, commandOptionsExpression} = findComponentConfig( + ast, + parser, + ); if (commandTypeName == null) { return []; } + const types = parser.getTypes(ast); const typeAlias = types[commandTypeName]; if (typeAlias.type !== 'TSInterfaceDeclaration') { @@ -82,19 +80,16 @@ function getCommandProperties( ); } - let properties; - try { - properties = typeAlias.body.body; - } catch (e) { + const properties = parser.bodyProperties(typeAlias); + if (!properties) { throw new Error( `Failed to find type definition for "${commandTypeName}", please check that you have a valid codegen typescript file`, ); } - const typeScriptPropertyNames = properties - .map(property => property && property.key && property.key.name) - .filter(Boolean); + const typeScriptPropertyNames = propertyNames(properties); + const commandOptions = getCommandOptions(commandOptionsExpression); if (commandOptions == null || commandOptions.supportedCommands == null) { throw new Error( 'codegenNativeCommands must be given an options object with supportedCommands array', @@ -126,24 +121,16 @@ function buildComponentSchema( ast: $FlowFixMe, parser: Parser, ): ComponentSchemaBuilderConfig { - const { - componentName, - propsTypeName, - commandTypeName, - commandOptionsExpression, - optionsExpression, - } = findComponentConfig(ast, parser); + const {componentName, propsTypeName, optionsExpression} = findComponentConfig( + ast, + parser, + ); const types = parser.getTypes(ast); const propProperties = getProperties(propsTypeName, types); - const commandOptions = getCommandOptions(commandOptionsExpression); - const commandProperties = getCommandProperties( - commandTypeName, - types, - commandOptions, - ); + const commandProperties = getCommandProperties(ast, parser); const options = getOptions(optionsExpression); diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index 1e22da0f1ab..e81fb6e4d15 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -307,6 +307,10 @@ class TypeScriptParser implements Parser { getAnnotatedElementProperties(annotatedElement: $FlowFixMe): $FlowFixMe { return annotatedElement.typeAnnotation.members; } + + bodyProperties(typeAlias: TypeDeclarationMap): $ReadOnlyArray<$FlowFixMe> { + return typeAlias.body.body; + } } module.exports = {