From f23f7f4c0f0640adf12ce99e78cf928ed9427962 Mon Sep 17 00:00:00 2001 From: Antoine Doubovetzky Date: Wed, 1 Mar 2023 06:13:57 -0800 Subject: [PATCH] Replace getTypes functions with parser specific methods (#36225) Summary: This PR is task 74 from https://github.com/facebook/react-native/issues/34872: > Move getTypes functions from utils.js to specific Parsers. Right now we have two Parser classes that takes care of the language specific details and two utils files that contains similar logic. We would like to move everything under the Parsers classes for better OOP architecture and to encourage code-reuse. ## Changelog [Internal] [Changed] - Replace getTypes functions with parser specific methods Pull Request resolved: https://github.com/facebook/react-native/pull/36225 Test Plan: I tested using Jest and Flow commands. Reviewed By: rshest Differential Revision: D43453454 Pulled By: cipolleschi fbshipit-source-id: 0eebcb55e1af3319e2c35bb462980046329a2c09 --- .../parsers/__tests__/parsers-commons-test.js | 13 ++++-- .../src/parsers/flow/components/index.js | 11 ++--- .../src/parsers/flow/modules/index.js | 4 +- .../src/parsers/flow/parser.js | 40 +++++++++++++++++++ .../src/parsers/flow/utils.js | 38 ------------------ .../src/parsers/parser.js | 5 +++ .../src/parsers/parserMock.js | 5 ++- .../src/parsers/parsers-commons.js | 12 ++++-- .../parsers/typescript/components/index.js | 11 ++--- .../src/parsers/typescript/modules/index.js | 4 +- .../src/parsers/typescript/parser.js | 30 ++++++++++++++ .../src/parsers/typescript/utils.js | 31 -------------- 12 files changed, 113 insertions(+), 91 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 2d8d96175e8..1117c1819ce 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 @@ -33,9 +33,12 @@ const { } = require('../errors'); import {MockedParser} from '../parserMock'; +import {FlowParser} from '../flow/parser'; const parser = new MockedParser(); +const flowParser = new FlowParser(); + const flowTranslateTypeAnnotation = require('../flow/modules/index'); const typeScriptTranslateTypeAnnotation = require('../typescript/modules/index'); @@ -384,7 +387,9 @@ describe('buildSchemaFromConfigType', () => { }; const wrapComponentSchemaMock = jest.fn(); - const buildComponentSchemaMock = jest.fn(_ => componentSchemaMock); + const buildComponentSchemaMock = jest.fn( + (_ast, _parser) => componentSchemaMock, + ); const buildModuleSchemaMock = jest.fn((_0, _1, _2, _3) => moduleSchemaMock); const buildSchemaFromConfigTypeHelper = ( @@ -414,7 +419,7 @@ describe('buildSchemaFromConfigType', () => { buildSchemaFromConfigTypeHelper('component'); expect(buildComponentSchemaMock).toHaveBeenCalledTimes(1); - expect(buildComponentSchemaMock).toHaveBeenCalledWith(astMock); + expect(buildComponentSchemaMock).toHaveBeenCalledWith(astMock, parser); expect(wrapComponentSchemaMock).toHaveBeenCalledTimes(1); expect(wrapComponentSchemaMock).toHaveBeenCalledWith(componentSchemaMock); @@ -681,7 +686,7 @@ describe('buildSchema', () => { buildComponentSchema, buildModuleSchema, Visitor, - parser, + flowParser, ); expect(getConfigTypeSpy).toHaveBeenCalledTimes(1); @@ -734,7 +739,7 @@ describe('buildSchema', () => { buildComponentSchema, buildModuleSchema, Visitor, - parser, + flowParser, ); expect(getConfigTypeSpy).toHaveBeenCalledTimes(1); 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 35106bcab38..e209ce4671f 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/index.js +++ b/packages/react-native-codegen/src/parsers/flow/components/index.js @@ -9,11 +9,11 @@ */ 'use strict'; +import type {Parser} from '../../parser'; import type {TypeDeclarationMap} from '../../utils'; import type {CommandOptions} from './options'; import type {ComponentSchemaBuilderConfig} from '../../schema.js'; -const {getTypes} = require('../utils'); const {getCommands} = require('./commands'); const {getEvents} = require('./events'); const {getExtendsProps, removeKnownExtends} = require('./extends'); @@ -180,9 +180,10 @@ function getCommandProperties( } // $FlowFixMe[signature-verification-failure] there's no flowtype for AST -/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's - * LTI update could not be added via codemod */ -function buildComponentSchema(ast): ComponentSchemaBuilderConfig { +function buildComponentSchema( + ast: $FlowFixMe, + parser: Parser, +): ComponentSchemaBuilderConfig { const { componentName, propsTypeName, @@ -191,7 +192,7 @@ function buildComponentSchema(ast): ComponentSchemaBuilderConfig { optionsExpression, } = findComponentConfig(ast); - const types = getTypes(ast); + const types = parser.getTypes(ast); const propProperties = getProperties(propsTypeName, types); const commandOptions = getCommandOptions(commandOptionsExpression); 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 29c825ee633..b6d6c53028e 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -25,7 +25,7 @@ import type {Parser} from '../../parser'; import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils'; const {visit, isModuleRegistryCall, verifyPlatforms} = require('../../utils'); -const {resolveTypeAnnotation, getTypes} = require('../utils'); +const {resolveTypeAnnotation} = require('../utils'); const { unwrapNullable, wrapNullable, @@ -339,7 +339,7 @@ function buildModuleSchema( tryParse: ParserErrorCapturer, parser: Parser, ): NativeModuleSchema { - const types = getTypes(ast); + const types = parser.getTypes(ast); const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter( t => parser.isModuleInterface(t), ); diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index 4d6e84836a3..5ae36dbd134 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -213,6 +213,46 @@ class FlowParser implements Parser { ): $FlowFixMe { return types[typeAnnotation.typeParameters.params[0].id.name]; } + + /** + * This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias + * declaration type. Unfortunately, we don't have those types, because flow-parser + * generates them, and flow-parser is not type-safe. In the future, we should find + * a way to get these types from our flow parser library. + * + * TODO(T71778680): Flow type AST Nodes + */ + + getTypes(ast: $FlowFixMe): TypeDeclarationMap { + return ast.body.reduce((types, node) => { + if ( + node.type === 'ExportNamedDeclaration' && + node.exportKind === 'type' + ) { + if ( + node.declaration != null && + (node.declaration.type === 'TypeAlias' || + node.declaration.type === 'InterfaceDeclaration') + ) { + types[node.declaration.id.name] = node.declaration; + } + } else if ( + node.type === 'ExportNamedDeclaration' && + node.exportKind === 'value' && + node.declaration && + node.declaration.type === 'EnumDeclaration' + ) { + types[node.declaration.id.name] = node.declaration; + } else if ( + node.type === 'TypeAlias' || + node.type === 'InterfaceDeclaration' || + node.type === 'EnumDeclaration' + ) { + types[node.id.name] = node; + } + return types; + }, {}); + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/flow/utils.js b/packages/react-native-codegen/src/parsers/flow/utils.js index d8939913389..3692cf16f89 100644 --- a/packages/react-native-codegen/src/parsers/flow/utils.js +++ b/packages/react-native-codegen/src/parsers/flow/utils.js @@ -12,43 +12,6 @@ import type {TypeResolutionStatus, TypeDeclarationMap} from '../utils'; -/** - * This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias - * declaration type. Unfortunately, we don't have those types, because flow-parser - * generates them, and flow-parser is not type-safe. In the future, we should find - * a way to get these types from our flow parser library. - * - * TODO(T71778680): Flow type AST Nodes - */ - -function getTypes(ast: $FlowFixMe): TypeDeclarationMap { - return ast.body.reduce((types, node) => { - if (node.type === 'ExportNamedDeclaration' && node.exportKind === 'type') { - if ( - node.declaration != null && - (node.declaration.type === 'TypeAlias' || - node.declaration.type === 'InterfaceDeclaration') - ) { - types[node.declaration.id.name] = node.declaration; - } - } else if ( - node.type === 'ExportNamedDeclaration' && - node.exportKind === 'value' && - node.declaration && - node.declaration.type === 'EnumDeclaration' - ) { - types[node.declaration.id.name] = node.declaration; - } else if ( - node.type === 'TypeAlias' || - node.type === 'InterfaceDeclaration' || - node.type === 'EnumDeclaration' - ) { - types[node.id.name] = node; - } - return types; - }, {}); -} - // $FlowFixMe[unclear-type] there's no flowtype for ASTs export type ASTNode = Object; @@ -134,5 +97,4 @@ function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode { module.exports = { getValueFromTypes, resolveTypeAnnotation, - getTypes, }; diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index 54e9605dafd..4126b1ca5ee 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -169,4 +169,9 @@ export interface Parser { typeAnnotation: $FlowFixMe, types: TypeDeclarationMap, ): $FlowFixMe; + + /** + * Given the AST, returns the TypeDeclarationMap + */ + getTypes(ast: $FlowFixMe): TypeDeclarationMap; } diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 75874a0b85e..b0e6545b2a0 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -21,7 +21,6 @@ import type { NativeModuleEnumMemberType, NativeModuleEnumMembers, } from '../CodegenSchema'; - import type {TypeDeclarationMap} from './utils'; // $FlowFixMe[untyped-import] there's no flowtype flow-parser @@ -177,4 +176,8 @@ export class MockedParser implements Parser { ): $FlowFixMe { return types[typeAnnotation.typeParameters.params[0].id.name]; } + + getTypes(ast: $FlowFixMe): TypeDeclarationMap { + return {}; + } } diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index 0a674eb85f4..4c633eb07b9 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -345,7 +345,10 @@ function buildSchemaFromConfigType( filename: ?string, ast: $FlowFixMe, wrapComponentSchema: (config: ComponentSchemaBuilderConfig) => SchemaType, - buildComponentSchema: (ast: $FlowFixMe) => ComponentSchemaBuilderConfig, + buildComponentSchema: ( + ast: $FlowFixMe, + parser: Parser, + ) => ComponentSchemaBuilderConfig, buildModuleSchema: ( hasteModuleName: string, ast: $FlowFixMe, @@ -356,7 +359,7 @@ function buildSchemaFromConfigType( ): SchemaType { switch (configType) { case 'component': { - return wrapComponentSchema(buildComponentSchema(ast)); + return wrapComponentSchema(buildComponentSchema(ast, parser)); } case 'module': { if (filename === undefined || filename === null) { @@ -398,7 +401,10 @@ function buildSchema( contents: string, filename: ?string, wrapComponentSchema: (config: ComponentSchemaBuilderConfig) => SchemaType, - buildComponentSchema: (ast: $FlowFixMe) => ComponentSchemaBuilderConfig, + buildComponentSchema: ( + ast: $FlowFixMe, + parser: Parser, + ) => ComponentSchemaBuilderConfig, buildModuleSchema: ( hasteModuleName: string, ast: $FlowFixMe, 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 41b5c0e41c2..2adaddeceeb 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/index.js @@ -10,11 +10,11 @@ 'use strict'; import type {ExtendsPropsShape} from '../../../CodegenSchema.js'; +import type {Parser} from '../../parser'; import type {TypeDeclarationMap} from '../../utils'; import type {CommandOptions} from './options'; import type {ComponentSchemaBuilderConfig} from '../../schema.js'; -const {getTypes} = require('../utils'); const {getCommands} = require('./commands'); const {getEvents} = require('./events'); const {categorizeProps} = require('./extends'); @@ -185,9 +185,10 @@ function getCommandProperties( type PropsAST = Object; // $FlowFixMe[signature-verification-failure] TODO(T108222691): Use flow-types for @babel/parser -/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's - * LTI update could not be added via codemod */ -function buildComponentSchema(ast): ComponentSchemaBuilderConfig { +function buildComponentSchema( + ast: $FlowFixMe, + parser: Parser, +): ComponentSchemaBuilderConfig { const { componentName, propsTypeName, @@ -196,7 +197,7 @@ function buildComponentSchema(ast): ComponentSchemaBuilderConfig { optionsExpression, } = findComponentConfig(ast); - const types = getTypes(ast); + const types = parser.getTypes(ast); const propProperties = getProperties(propsTypeName, types); const commandOptions = getCommandOptions(commandOptionsExpression); 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 5a511b51a31..a37dd4cbbca 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -31,7 +31,7 @@ const {flattenIntersectionType} = require('../parseTopLevelType'); const {flattenProperties} = require('../components/componentsUtils'); const {visit, isModuleRegistryCall, verifyPlatforms} = require('../../utils'); -const {resolveTypeAnnotation, getTypes} = require('../utils'); +const {resolveTypeAnnotation} = require('../utils'); const { parseObjectProperty, @@ -445,7 +445,7 @@ function buildModuleSchema( tryParse: ParserErrorCapturer, parser: Parser, ): NativeModuleSchema { - const types = getTypes(ast); + const types = parser.getTypes(ast); const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter( t => parser.isModuleInterface(t), ); diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index b050c9a87ac..3a6cca42664 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -209,6 +209,36 @@ class TypeScriptParser implements Parser { ): $FlowFixMe { return types[typeAnnotation.typeParameters.params[0].typeName.name]; } + + /** + * TODO(T108222691): Use flow-types for @babel/parser + */ + getTypes(ast: $FlowFixMe): TypeDeclarationMap { + return ast.body.reduce((types, node) => { + switch (node.type) { + case 'ExportNamedDeclaration': { + if (node.declaration) { + switch (node.declaration.type) { + case 'TSTypeAliasDeclaration': + case 'TSInterfaceDeclaration': + case 'TSEnumDeclaration': { + types[node.declaration.id.name] = node.declaration; + break; + } + } + } + break; + } + case 'TSTypeAliasDeclaration': + case 'TSInterfaceDeclaration': + case 'TSEnumDeclaration': { + types[node.id.name] = node; + break; + } + } + return types; + }, {}); + } } module.exports = { TypeScriptParser, diff --git a/packages/react-native-codegen/src/parsers/typescript/utils.js b/packages/react-native-codegen/src/parsers/typescript/utils.js index ae72b030f64..ffb7145d620 100644 --- a/packages/react-native-codegen/src/parsers/typescript/utils.js +++ b/packages/react-native-codegen/src/parsers/typescript/utils.js @@ -14,36 +14,6 @@ import type {TypeResolutionStatus, TypeDeclarationMap} from '../utils'; const {parseTopLevelType} = require('./parseTopLevelType'); -/** - * TODO(T108222691): Use flow-types for @babel/parser - */ -function getTypes(ast: $FlowFixMe): TypeDeclarationMap { - return ast.body.reduce((types, node) => { - switch (node.type) { - case 'ExportNamedDeclaration': { - if (node.declaration) { - switch (node.declaration.type) { - case 'TSTypeAliasDeclaration': - case 'TSInterfaceDeclaration': - case 'TSEnumDeclaration': { - types[node.declaration.id.name] = node.declaration; - break; - } - } - } - break; - } - case 'TSTypeAliasDeclaration': - case 'TSInterfaceDeclaration': - case 'TSEnumDeclaration': { - types[node.id.name] = node; - break; - } - } - return types; - }, {}); -} - // $FlowFixMe[unclear-type] Use flow-types for @babel/parser export type ASTNode = Object; @@ -131,5 +101,4 @@ function resolveTypeAnnotation( module.exports = { resolveTypeAnnotation, - getTypes, };