diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-test.js index 9418c0539e8..9339a4529b4 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-test.js @@ -270,6 +270,41 @@ describe('FlowParser', () => { ).toEqual(expected); }); }); + + describe('isOptionalProperty', () => { + it('when property is optional', () => { + const property = { + value: { + type: 'TypeAnnotation', + }, + optional: true, + }; + + expect(parser.isOptionalProperty(property)).toEqual(true); + }); + + it('when property is not optional', () => { + const property = { + value: { + type: 'TypeAnnotation', + }, + optional: false, + }; + + expect(parser.isOptionalProperty(property)).toEqual(false); + }); + + it('when property value type is NullableTypeAnnotation', () => { + const property = { + value: { + type: 'NullableTypeAnnotation', + }, + optional: false, + }; + + expect(parser.isOptionalProperty(property)).toEqual(true); + }); + }); }); describe('TypeScriptParser', () => { @@ -531,4 +566,20 @@ describe('TypeScriptParser', () => { ).toEqual(expected); }); }); + + describe('isOptionalProperty', () => { + it('when property is optional', () => { + const property = { + optional: true, + }; + expect(parser.isOptionalProperty(property)).toEqual(true); + }); + + it('when property is undefined or not optional', () => { + const property = { + optional: false, + }; + expect(parser.isOptionalProperty(property)).toEqual(false); + }); + }); }); diff --git a/packages/react-native-codegen/src/parsers/flow/components/events.js b/packages/react-native-codegen/src/parsers/flow/components/events.js index 8ffbdbfae6d..9c7f0312c0e 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/events.js +++ b/packages/react-native-codegen/src/parsers/flow/components/events.js @@ -28,6 +28,7 @@ function getPropertyType( name, optional: boolean, typeAnnotation: $FlowFixMe, + parser: Parser, ): NamedShape { const type = extractTypeFromTypeAnnotation(typeAnnotation); @@ -77,6 +78,7 @@ function getPropertyType( name, optional, typeAnnotation.typeParameters.params[0], + parser, ); case 'ObjectTypeAnnotation': return { @@ -84,7 +86,9 @@ function getPropertyType( optional, typeAnnotation: { type: 'ObjectTypeAnnotation', - properties: typeAnnotation.properties.map(buildPropertiesForEvent), + properties: typeAnnotation.properties.map(member => + buildPropertiesForEvent(member, parser), + ), }, }; case 'UnionTypeAnnotation': @@ -109,7 +113,7 @@ function getPropertyType( return { name, optional, - typeAnnotation: extractArrayElementType(typeAnnotation, name), + typeAnnotation: extractArrayElementType(typeAnnotation, name, parser), }; default: throw new Error(`Unable to determine event type for "${name}": ${type}`); @@ -119,6 +123,7 @@ function getPropertyType( function extractArrayElementType( typeAnnotation: $FlowFixMe, name: string, + parser: Parser, ): EventTypeAnnotation { const type = extractTypeFromTypeAnnotation(typeAnnotation); @@ -146,12 +151,18 @@ function extractArrayElementType( case 'ObjectTypeAnnotation': return { type: 'ObjectTypeAnnotation', - properties: typeAnnotation.properties.map(buildPropertiesForEvent), + properties: typeAnnotation.properties.map(member => + buildPropertiesForEvent(member, parser), + ), }; case 'ArrayTypeAnnotation': return { type: 'ArrayTypeAnnotation', - elementType: extractArrayElementType(typeAnnotation.elementType, name), + elementType: extractArrayElementType( + typeAnnotation.elementType, + name, + parser, + ), }; case '$ReadOnlyArray': const genericParams = typeAnnotation.typeParameters.params; @@ -164,7 +175,7 @@ function extractArrayElementType( } return { type: 'ArrayTypeAnnotation', - elementType: extractArrayElementType(genericParams[0], name), + elementType: extractArrayElementType(genericParams[0], name, parser), }; default: throw new Error( @@ -244,18 +255,20 @@ function findEventArgumentsAndType( } } -/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's - * LTI update could not be added via codemod */ -function buildPropertiesForEvent(property): NamedShape { +function buildPropertiesForEvent( + /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's + * LTI update could not be added via codemod */ + property, + parser: Parser, +): NamedShape { const name = property.key.name; - const optional = - property.value.type === 'NullableTypeAnnotation' || property.optional; + const optional = parser.isOptionalProperty(property); let typeAnnotation = property.value.type === 'NullableTypeAnnotation' ? property.value.typeAnnotation : property.value; - return getPropertyType(name, optional, typeAnnotation); + return getPropertyType(name, optional, typeAnnotation, parser); } function buildEventSchema( @@ -299,7 +312,11 @@ function buildEventSchema( paperTopLevelNameDeprecated, typeAnnotation: { type: 'EventTypeAnnotation', - argument: getEventArgument(argumentProps, buildPropertiesForEvent), + argument: getEventArgument( + argumentProps, + buildPropertiesForEvent, + parser, + ), }, }; } @@ -316,7 +333,11 @@ function buildEventSchema( bubblingType, typeAnnotation: { type: 'EventTypeAnnotation', - argument: getEventArgument(argumentProps, buildPropertiesForEvent), + argument: getEventArgument( + argumentProps, + buildPropertiesForEvent, + parser, + ), }, }; } diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index 626346ab28c..fdec2c5cb72 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -337,6 +337,12 @@ class FlowParser implements Parser { nameForArgument(prop: PropAST): $FlowFixMe { return prop.argument.id.name; } + + isOptionalProperty(property: $FlowFixMe): boolean { + return ( + property.value.type === 'NullableTypeAnnotation' || property.optional + ); + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index 066ad13293e..ea0325aff51 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -269,4 +269,11 @@ export interface Parser { * @returns: name property */ nameForArgument(prop: PropAST): $FlowFixMe; + + /** + * Given a property return if it is optional. + * @parameter property + * @returns: a boolean specifying if the Property is optional + */ + isOptionalProperty(property: $FlowFixMe): boolean; } diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 1263b469de5..d9c78e7efd1 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -251,4 +251,8 @@ export class MockedParser implements Parser { nameForArgument(prop: PropAST): $FlowFixMe { return prop.expression.name; } + + isOptionalProperty(property: $FlowFixMe): boolean { + return property.optional || false; + } } diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index 2c9e08751e6..b03b2bd9190 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -860,11 +860,15 @@ function getEventArgument( argumentProps: PropAST, buildPropertiesForEvent: ( property: PropAST, + parser: Parser, ) => NamedShape, + parser: Parser, ): ObjectTypeAnnotation { return { type: 'ObjectTypeAnnotation', - properties: argumentProps.map(buildPropertiesForEvent), + properties: argumentProps.map(member => + buildPropertiesForEvent(member, parser), + ), }; } diff --git a/packages/react-native-codegen/src/parsers/typescript/components/events.js b/packages/react-native-codegen/src/parsers/typescript/components/events.js index bb0cfac3830..d1e9dadde2c 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/events.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/events.js @@ -32,6 +32,7 @@ function getPropertyType( /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ annotation, + parser: Parser, ): NamedShape { const topLevelType = parseTopLevelType(annotation); const typeAnnotation = topLevelType.type; @@ -88,7 +89,9 @@ function getPropertyType( optional, typeAnnotation: { type: 'ObjectTypeAnnotation', - properties: typeAnnotation.members.map(buildPropertiesForEvent), + properties: typeAnnotation.members.map(member => + buildPropertiesForEvent(member, parser), + ), }, }; case 'TSUnionType': @@ -112,7 +115,7 @@ function getPropertyType( return { name, optional, - typeAnnotation: extractArrayElementType(typeAnnotation, name), + typeAnnotation: extractArrayElementType(typeAnnotation, name, parser), }; default: (type: empty); @@ -123,12 +126,17 @@ function getPropertyType( function extractArrayElementType( typeAnnotation: $FlowFixMe, name: string, + parser: Parser, ): EventTypeAnnotation { const type = extractTypeFromTypeAnnotation(typeAnnotation); switch (type) { case 'TSParenthesizedType': - return extractArrayElementType(typeAnnotation.typeAnnotation, name); + return extractArrayElementType( + typeAnnotation.typeAnnotation, + name, + parser, + ); case 'TSBooleanKeyword': return {type: 'BooleanTypeAnnotation'}; case 'TSStringKeyword': @@ -154,12 +162,18 @@ function extractArrayElementType( case 'TSTypeLiteral': return { type: 'ObjectTypeAnnotation', - properties: typeAnnotation.members.map(buildPropertiesForEvent), + properties: typeAnnotation.members.map(member => + buildPropertiesForEvent(member, parser), + ), }; case 'TSArrayType': return { type: 'ArrayTypeAnnotation', - elementType: extractArrayElementType(typeAnnotation.elementType, name), + elementType: extractArrayElementType( + typeAnnotation.elementType, + name, + parser, + ), }; default: throw new Error( @@ -260,14 +274,17 @@ function findEventArgumentsAndType( } } -/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's - * LTI update could not be added via codemod */ -function buildPropertiesForEvent(property): NamedShape { +function buildPropertiesForEvent( + /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's + * LTI update could not be added via codemod */ + property, + parser: Parser, +): NamedShape { const name = property.key.name; - const optional = property.optional || false; + const optional = parser.isOptionalProperty(property); let typeAnnotation = property.typeAnnotation.typeAnnotation; - return getPropertyType(name, optional, typeAnnotation); + return getPropertyType(name, optional, typeAnnotation, parser); } // $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser @@ -303,7 +320,11 @@ function buildEventSchema( paperTopLevelNameDeprecated, typeAnnotation: { type: 'EventTypeAnnotation', - argument: getEventArgument(argumentProps, buildPropertiesForEvent), + argument: getEventArgument( + argumentProps, + buildPropertiesForEvent, + parser, + ), }, }; } @@ -314,7 +335,11 @@ function buildEventSchema( bubblingType, typeAnnotation: { type: 'EventTypeAnnotation', - argument: getEventArgument(argumentProps, buildPropertiesForEvent), + argument: getEventArgument( + argumentProps, + buildPropertiesForEvent, + parser, + ), }, }; } diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index 86534b5cbdd..f583f594834 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -336,6 +336,10 @@ class TypeScriptParser implements Parser { nameForArgument(prop: PropAST): $FlowFixMe { return prop.expression.name; } + + isOptionalProperty(property: $FlowFixMe): boolean { + return property.optional || false; + } } module.exports = {