From 0212179293c4475d02ae14d37732d11b471983c5 Mon Sep 17 00:00:00 2001 From: Ken Tominaga Date: Thu, 11 May 2023 05:36:45 -0700 Subject: [PATCH] Use `buildPropSchema` from parser-commons (#37043) Summary: > The `buildPropSchema` function in `parsers/typescript/components/props.js` and `parsers/flow/components/props.js` is the same. move it to `parser-commons` and use it in the original files. part of https://github.com/facebook/react-native/issues/34872 - [x] Make the getTypeAnnotation signature from the Flow package to be equal to the typescript one. Specifically, the Typescript one needs an additional parameter withNullDefault that we can safely ignore in the implementation. - [x] buildPropSchema signature can be updated to accept those two functions in input as callbacks. Then, the getProps function can feed the right functions based on the language to the shared build prop schema. ref: https://github.com/facebook/react-native/issues/34872#issuecomment-1517519254 bypass-github-export-checks ## Changelog: [Internal] [Changed] - Use `buildPropSchema` from parser-commons Pull Request resolved: https://github.com/facebook/react-native/pull/37043 Test Plan: - [ ] `yarn jest react-native-codegen` pass Reviewed By: rshest Differential Revision: D45209982 Pulled By: cipolleschi fbshipit-source-id: c241bc0542ba662c965d70d1dc283f48541e14ea --- .../flow/components/componentsUtils.js | 17 ++++--- .../src/parsers/flow/components/props.js | 33 ++------------ .../src/parsers/flow/parser.js | 15 ++++++- .../src/parsers/flow/utils.js | 5 +-- .../src/parsers/parser.js | 44 ++++++++++++++++++- .../src/parsers/parserMock.js | 20 ++++++++- .../src/parsers/parsers-commons.js | 31 +++++++++++++ .../typescript/components/componentsUtils.js | 42 +++++++++++++----- .../parsers/typescript/components/props.js | 27 ++---------- .../src/parsers/typescript/parser.js | 14 +++++- .../src/parsers/typescript/utils.js | 3 -- .../react-native-codegen/src/parsers/utils.js | 3 ++ 12 files changed, 172 insertions(+), 82 deletions(-) diff --git a/packages/react-native-codegen/src/parsers/flow/components/componentsUtils.js b/packages/react-native-codegen/src/parsers/flow/components/componentsUtils.js index 0ae12c7cbdc45c..7497c702579868 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/componentsUtils.js +++ b/packages/react-native-codegen/src/parsers/flow/components/componentsUtils.js @@ -10,10 +10,10 @@ 'use strict'; -import type {ASTNode} from '../utils'; import type {NamedShape} from '../../../CodegenSchema.js'; const {getValueFromTypes} = require('../utils.js'); -import type {TypeDeclarationMap, PropAST} from '../../utils'; +import type {TypeDeclarationMap, PropAST, ASTNode} from '../../utils'; +import type {BuildSchemaFN, Parser} from '../../parser'; function getProperties( typeName: string, @@ -34,7 +34,8 @@ function getTypeAnnotationForArray<+T>( typeAnnotation: $FlowFixMe, defaultValue: $FlowFixMe | null, types: TypeDeclarationMap, - buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape, + parser: Parser, + buildSchema: BuildSchemaFN, ): $FlowFixMe { const extractedTypeAnnotation = getValueFromTypes(typeAnnotation, types); if (extractedTypeAnnotation.type === 'NullableTypeAnnotation') { @@ -63,7 +64,7 @@ function getTypeAnnotationForArray<+T>( objectType.typeParameters.params[0].properties, types, ) - .map(prop => buildSchema(prop, types)) + .map(prop => buildSchema(prop, types, parser)) .filter(Boolean), }; } @@ -84,7 +85,7 @@ function getTypeAnnotationForArray<+T>( nestedObjectType.typeParameters.params[0].properties, types, ) - .map(prop => buildSchema(prop, types)) + .map(prop => buildSchema(prop, types, parser)) .filter(Boolean), }, }; @@ -233,7 +234,8 @@ function getTypeAnnotation<+T>( defaultValue: $FlowFixMe | null, withNullDefault: boolean, types: TypeDeclarationMap, - buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape, + parser: Parser, + buildSchema: BuildSchemaFN, ): $FlowFixMe { const typeAnnotation = getValueFromTypes(annotation, types); @@ -248,6 +250,7 @@ function getTypeAnnotation<+T>( typeAnnotation.typeParameters.params[0], defaultValue, types, + parser, buildSchema, ), }; @@ -263,7 +266,7 @@ function getTypeAnnotation<+T>( typeAnnotation.typeParameters.params[0].properties, types, ) - .map(prop => buildSchema(prop, types)) + .map(prop => buildSchema(prop, types, parser)) .filter(Boolean), }; } diff --git a/packages/react-native-codegen/src/parsers/flow/components/props.js b/packages/react-native-codegen/src/parsers/flow/components/props.js index a55b5a43025223..1542e8f4e8b554 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/props.js +++ b/packages/react-native-codegen/src/parsers/flow/components/props.js @@ -18,41 +18,14 @@ import type { import type {TypeDeclarationMap, PropAST} from '../../utils'; import type {Parser} from '../../parser'; -const { - flattenProperties, - getSchemaInfo, - getTypeAnnotation, -} = require('./componentsUtils.js'); +const {flattenProperties} = require('./componentsUtils.js'); +const {buildPropSchema} = require('../../parsers-commons'); type ExtendsForProp = null | { type: 'ReactNativeBuiltInType', knownTypeName: 'ReactNativeCoreViewProps', }; -function buildPropSchema( - property: PropAST, - types: TypeDeclarationMap, -): ?NamedShape { - const info = getSchemaInfo(property, types); - if (info == null) { - return null; - } - const {name, optional, typeAnnotation, defaultValue, withNullDefault} = info; - - return { - name, - optional, - typeAnnotation: getTypeAnnotation( - name, - typeAnnotation, - defaultValue, - withNullDefault, - types, - buildPropSchema, - ), - }; -} - function extendsForProp( prop: PropAST, types: TypeDeclarationMap, @@ -117,7 +90,7 @@ function getProps( } { const nonExtendsProps = removeKnownExtends(typeDefinition, types, parser); const props = flattenProperties(nonExtendsProps, types) - .map(property => buildPropSchema(property, types)) + .map(property => buildPropSchema(property, types, parser)) .filter(Boolean); return { diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index fdec2c5cb729c2..5758bfb404643a 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -22,9 +22,14 @@ import type { NativeModuleEnumMap, } from '../../CodegenSchema'; import type {ParserType} from '../errors'; -import type {Parser} from '../parser'; +import type {GetSchemaInfoFN, GetTypeAnnotationFN, Parser} from '../parser'; import type {ParserErrorCapturer, TypeDeclarationMap, PropAST} from '../utils'; +const { + getSchemaInfo, + getTypeAnnotation, +} = require('./components/componentsUtils'); + const {flowTranslateTypeAnnotation} = require('./modules'); // $FlowFixMe[untyped-import] there's no flowtype flow-parser @@ -343,6 +348,14 @@ class FlowParser implements Parser { property.value.type === 'NullableTypeAnnotation' || property.optional ); } + + getGetSchemaInfoFN(): GetSchemaInfoFN { + return getSchemaInfo; + } + + getGetTypeAnnotationFN(): GetTypeAnnotationFN { + return getTypeAnnotation; + } } 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 3692cf16f89b7c..258c9258512fc0 100644 --- a/packages/react-native-codegen/src/parsers/flow/utils.js +++ b/packages/react-native-codegen/src/parsers/flow/utils.js @@ -10,10 +10,7 @@ 'use strict'; -import type {TypeResolutionStatus, TypeDeclarationMap} from '../utils'; - -// $FlowFixMe[unclear-type] there's no flowtype for ASTs -export type ASTNode = Object; +import type {TypeResolutionStatus, TypeDeclarationMap, ASTNode} from '../utils'; const invariant = require('invariant'); diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index ea0325aff51f2f..b95cc3d5ad2d01 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -22,7 +22,45 @@ import type { NativeModuleEnumMap, } from '../CodegenSchema'; import type {ParserType} from './errors'; -import type {ParserErrorCapturer, TypeDeclarationMap, PropAST} from './utils'; +import type { + ParserErrorCapturer, + TypeDeclarationMap, + PropAST, + ASTNode, +} from './utils'; + +export type GetTypeAnnotationFN = ( + name: string, + annotation: $FlowFixMe | ASTNode, + defaultValue: $FlowFixMe | void, + withNullDefault: boolean, + types: TypeDeclarationMap, + parser: Parser, + buildSchema: ( + property: PropAST, + types: TypeDeclarationMap, + parser: Parser, + ) => $FlowFixMe, +) => $FlowFixMe; + +export type SchemaInfo = { + name: string, + optional: boolean, + typeAnnotation: $FlowFixMe, + defaultValue: $FlowFixMe, + withNullDefault: boolean, +}; + +export type GetSchemaInfoFN = ( + property: PropAST, + types: TypeDeclarationMap, +) => ?SchemaInfo; + +export type BuildSchemaFN = ( + property: PropAST, + types: TypeDeclarationMap, + parser: Parser, +) => ?NamedShape; /** * This is the main interface for Parsers of various languages. @@ -276,4 +314,8 @@ export interface Parser { * @returns: a boolean specifying if the Property is optional */ isOptionalProperty(property: $FlowFixMe): boolean; + + getGetTypeAnnotationFN(): GetTypeAnnotationFN; + + getGetSchemaInfoFN(): GetSchemaInfoFN; } diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index d9c78e7efd135d..4dbb5246c24102 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -10,7 +10,7 @@ 'use strict'; -import type {Parser} from './parser'; +import type {GetSchemaInfoFN, GetTypeAnnotationFN, Parser} from './parser'; import type {ParserType} from './errors'; import type { UnionTypeAnnotationMemberType, @@ -255,4 +255,22 @@ export class MockedParser implements Parser { isOptionalProperty(property: $FlowFixMe): boolean { return property.optional || false; } + + getGetTypeAnnotationFN(): GetTypeAnnotationFN { + return () => { + return {}; + }; + } + + getGetSchemaInfoFN(): GetSchemaInfoFN { + return () => { + return { + name: 'MockedSchema', + optional: false, + typeAnnotation: 'BooleanTypeAnnotation', + defaultValue: false, + withNullDefault: false, + }; + }; + } } diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index b03b2bd9190c44..0b9f06c36cb060 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -23,6 +23,7 @@ import type { SchemaType, NativeModuleEnumMap, OptionsShape, + PropTypeAnnotation, EventTypeAnnotation, ObjectTypeAnnotation, } from '../CodegenSchema.js'; @@ -854,6 +855,35 @@ function extendsForProp( } } +function buildPropSchema( + property: PropAST, + types: TypeDeclarationMap, + parser: Parser, +): ?NamedShape { + const getSchemaInfoFN = parser.getGetSchemaInfoFN(); + const info = getSchemaInfoFN(property, types); + if (info == null) { + return null; + } + const {name, optional, typeAnnotation, defaultValue, withNullDefault} = info; + + const getTypeAnnotationFN = parser.getGetTypeAnnotationFN(); + + return { + name, + optional, + typeAnnotation: getTypeAnnotationFN( + name, + typeAnnotation, + defaultValue, + withNullDefault, + types, + parser, + buildPropSchema, + ), + }; +} + /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ function getEventArgument( @@ -892,5 +922,6 @@ module.exports = { getOptions, getCommandTypeNameAndOptionsExpression, extendsForProp, + buildPropSchema, getEventArgument, }; diff --git a/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js b/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js index 763b85b874ce8b..26b9bddaceac19 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js @@ -9,13 +9,12 @@ */ 'use strict'; -import type {ASTNode} from '../utils'; -import type {NamedShape} from '../../../CodegenSchema.js'; const { parseTopLevelType, flattenIntersectionType, } = require('../parseTopLevelType'); -import type {TypeDeclarationMap, PropAST} from '../../utils'; +import type {TypeDeclarationMap, PropAST, ASTNode} from '../../utils'; +import type {BuildSchemaFN, Parser} from '../../parser'; function getProperties( typeName: string, @@ -111,7 +110,8 @@ function detectArrayType( typeAnnotation: $FlowFixMe | ASTNode, defaultValue: $FlowFixMe | void, types: TypeDeclarationMap, - buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape, + parser: Parser, + buildSchema: BuildSchemaFN, ): $FlowFixMe { // Covers: readonly T[] if ( @@ -126,6 +126,7 @@ function detectArrayType( typeAnnotation.typeAnnotation.elementType, defaultValue, types, + parser, buildSchema, ), }; @@ -140,6 +141,7 @@ function detectArrayType( typeAnnotation.elementType, defaultValue, types, + parser, buildSchema, ), }; @@ -158,6 +160,7 @@ function detectArrayType( typeAnnotation.typeParameters.params[0], defaultValue, types, + parser, buildSchema, ), }; @@ -169,11 +172,12 @@ function detectArrayType( function buildObjectType( rawProperties: Array<$FlowFixMe>, types: TypeDeclarationMap, - buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape, + parser: Parser, + buildSchema: BuildSchemaFN, ): $FlowFixMe { const flattenedProperties = flattenProperties(rawProperties, types); const properties = flattenedProperties - .map(prop => buildSchema(prop, types)) + .map(prop => buildSchema(prop, types, parser)) .filter(Boolean); return { @@ -189,17 +193,24 @@ function getCommonTypeAnnotation( typeAnnotation: $FlowFixMe, defaultValue: $FlowFixMe | void, types: TypeDeclarationMap, - buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape, + parser: Parser, + buildSchema: BuildSchemaFN, ): $FlowFixMe { switch (type) { case 'TSTypeLiteral': - return buildObjectType(typeAnnotation.members, types, buildSchema); + return buildObjectType( + typeAnnotation.members, + types, + parser, + buildSchema, + ); case 'TSInterfaceDeclaration': - return buildObjectType([typeAnnotation], types, buildSchema); + return buildObjectType([typeAnnotation], types, parser, buildSchema); case 'TSIntersectionType': return buildObjectType( flattenIntersectionType(typeAnnotation, types), types, + parser, buildSchema, ); case 'ImageSource': @@ -276,7 +287,8 @@ function getTypeAnnotationForArray( typeAnnotation: $FlowFixMe, defaultValue: $FlowFixMe | void, types: TypeDeclarationMap, - buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape, + parser: Parser, + buildSchema: BuildSchemaFN, ): $FlowFixMe { // unpack WithDefault, (T) or T|U const topLevelType = parseTopLevelType(typeAnnotation, types); @@ -297,6 +309,7 @@ function getTypeAnnotationForArray( extractedTypeAnnotation, defaultValue, types, + parser, buildSchema, ); if (arrayType) { @@ -322,6 +335,7 @@ function getTypeAnnotationForArray( extractedTypeAnnotation, defaultValue, types, + parser, buildSchema, ); if (common) { @@ -370,8 +384,10 @@ function getTypeAnnotation( name: string, annotation: $FlowFixMe | ASTNode, defaultValue: $FlowFixMe | void, + withNullDefault: boolean, // Just to make `getTypeAnnotation` signature match with the one from Flow types: TypeDeclarationMap, - buildSchema: (property: PropAST, types: TypeDeclarationMap) => ?NamedShape, + parser: Parser, + buildSchema: BuildSchemaFN, ): $FlowFixMe { // unpack WithDefault, (T) or T|U const topLevelType = parseTopLevelType(annotation, types); @@ -381,6 +397,7 @@ function getTypeAnnotation( typeAnnotation, defaultValue, types, + parser, buildSchema, ); if (arrayType) { @@ -400,6 +417,7 @@ function getTypeAnnotation( typeAnnotation, defaultValue, types, + parser, buildSchema, ); if (common) { @@ -435,6 +453,7 @@ type SchemaInfo = { optional: boolean, typeAnnotation: $FlowFixMe, defaultValue: $FlowFixMe, + withNullDefault: boolean, // Just to make `getTypeAnnotation` signature match with the one from Flow }; function getSchemaInfo( @@ -460,6 +479,7 @@ function getSchemaInfo( optional: property.optional || topLevelType.optional, typeAnnotation: topLevelType.type, defaultValue: topLevelType.defaultValue, + withNullDefault: false, // Just to make `getTypeAnnotation` signature match with the one from Flow }; } diff --git a/packages/react-native-codegen/src/parsers/typescript/components/props.js b/packages/react-native-codegen/src/parsers/typescript/components/props.js index ab989186cc4d65..25d24698f5c24b 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/props.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/props.js @@ -10,8 +10,6 @@ 'use strict'; -const {getSchemaInfo, getTypeAnnotation} = require('./componentsUtils.js'); - import type {NamedShape, PropTypeAnnotation} from '../../../CodegenSchema.js'; import type {TypeDeclarationMap, PropAST} from '../../utils'; import type {ExtendsPropsShape} from '../../../CodegenSchema.js'; @@ -19,26 +17,7 @@ import type {Parser} from '../../parser'; const {flattenProperties} = require('./componentsUtils.js'); const {parseTopLevelType} = require('../parseTopLevelType'); -const {extendsForProp} = require('../../parsers-commons'); - -function buildPropSchema( - property: PropAST, - types: TypeDeclarationMap, -): NamedShape { - const info = getSchemaInfo(property, types); - const {name, optional, typeAnnotation, defaultValue} = info; - return { - name, - optional, - typeAnnotation: getTypeAnnotation( - name, - typeAnnotation, - defaultValue, - types, - buildPropSchema, - ), - }; -} +const {buildPropSchema, extendsForProp} = require('../../parsers-commons'); function isEvent(typeAnnotation: $FlowFixMe): boolean { if (typeAnnotation.type !== 'TSTypeReference') { @@ -101,7 +80,9 @@ function getProps( } return { - props: componentPropAsts.map(property => buildPropSchema(property, types)), + props: componentPropAsts + .map(property => buildPropSchema(property, types, parser)) + .filter(Boolean), extendsProps, }; } diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index f583f594834eaa..a467dc919005df 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -22,7 +22,7 @@ import type { NativeModuleEnumMap, } from '../../CodegenSchema'; import type {ParserType} from '../errors'; -import type {Parser} from '../parser'; +import type {GetSchemaInfoFN, GetTypeAnnotationFN, Parser} from '../parser'; import type {ParserErrorCapturer, TypeDeclarationMap, PropAST} from '../utils'; const {typeScriptTranslateTypeAnnotation} = require('./modules'); @@ -36,6 +36,10 @@ const {buildComponentSchema} = require('./components'); const {wrapComponentSchema} = require('../schema.js'); const {buildModuleSchema} = require('../parsers-commons.js'); const {resolveTypeAnnotation} = require('./utils'); +const { + getSchemaInfo, + getTypeAnnotation, +} = require('./components/componentsUtils'); const fs = require('fs'); @@ -340,6 +344,14 @@ class TypeScriptParser implements Parser { isOptionalProperty(property: $FlowFixMe): boolean { return property.optional || false; } + + getGetSchemaInfoFN(): GetSchemaInfoFN { + return getSchemaInfo; + } + + getGetTypeAnnotationFN(): GetTypeAnnotationFN { + return getTypeAnnotation; + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/typescript/utils.js b/packages/react-native-codegen/src/parsers/typescript/utils.js index ffb7145d620852..e555ff5fe4e2c4 100644 --- a/packages/react-native-codegen/src/parsers/typescript/utils.js +++ b/packages/react-native-codegen/src/parsers/typescript/utils.js @@ -14,9 +14,6 @@ import type {TypeResolutionStatus, TypeDeclarationMap} from '../utils'; const {parseTopLevelType} = require('./parseTopLevelType'); -// $FlowFixMe[unclear-type] Use flow-types for @babel/parser -export type ASTNode = Object; - const invariant = require('invariant'); function resolveTypeAnnotation( diff --git a/packages/react-native-codegen/src/parsers/utils.js b/packages/react-native-codegen/src/parsers/utils.js index 56d93491273252..5c904c6c80fecf 100644 --- a/packages/react-native-codegen/src/parsers/utils.js +++ b/packages/react-native-codegen/src/parsers/utils.js @@ -37,6 +37,9 @@ export type ParserErrorCapturer = (fn: () => T) => ?T; // $FlowFixMe[unclear-type] there's no flowtype for ASTs export type PropAST = Object; +// $FlowFixMe[unclear-type] there's no flowtype for ASTs +export type ASTNode = Object; + function createParserErrorCapturer(): [ Array, ParserErrorCapturer,