diff --git a/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js b/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js index 4806672ad7c8ae..5e3b7fa2ecc6bf 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js @@ -33,6 +33,7 @@ const { throwIfPartialNotAnnotatingTypeParameter, throwIfPartialWithMoreParameter, throwIfMoreThanOneCodegenNativecommands, + throwIfEventHasNoName, } = require('../error-utils'); const { UnsupportedModulePropertyParserError, @@ -905,3 +906,47 @@ describe('throwIfMoreThanOneConfig', () => { }).not.toThrow(); }); }); + +describe('throwIfEventHasNoName', () => { + const flowParser = new FlowParser(); + const typescriptParser = new TypeScriptParser(); + + it('throws an error if typeAnnotation of event have no name in Flow', () => { + const typeAnnotation = {}; + expect(() => { + throwIfEventHasNoName(typeAnnotation, flowParser); + }).toThrowError(`typeAnnotation of event doesn't have a name`); + }); + + it('does not throw an error if typeAnnotation of event have a name in Flow', () => { + const typeAnnotation = { + id: { + name: 'BubblingEventHandler', + }, + }; + + expect(() => { + throwIfEventHasNoName(typeAnnotation, flowParser); + }).not.toThrow(); + }); + + it('throws an error if typeAnnotation of event have no name in TypeScript', () => { + const typeAnnotation = {}; + + expect(() => { + throwIfEventHasNoName(typeAnnotation, typescriptParser); + }).toThrowError(`typeAnnotation of event doesn't have a name`); + }); + + it('does not throw an error if typeAnnotation of event have a name in TypeScript', () => { + const typeAnnotation = { + typeName: { + name: 'BubblingEventHandler', + }, + }; + + expect(() => { + throwIfEventHasNoName(typeAnnotation, typescriptParser); + }).not.toThrow(); + }); +}); diff --git a/packages/react-native-codegen/src/parsers/error-utils.js b/packages/react-native-codegen/src/parsers/error-utils.js index 87adc6a7af1bee..57774b9938ec9c 100644 --- a/packages/react-native-codegen/src/parsers/error-utils.js +++ b/packages/react-native-codegen/src/parsers/error-utils.js @@ -310,6 +310,15 @@ function throwIfMoreThanOneConfig(foundConfigs: Array<{[string]: string}>) { } } +function throwIfEventHasNoName(typeAnnotation: $FlowFixMe, parser: Parser) { + const name = + parser.language() === 'Flow' ? typeAnnotation.id : typeAnnotation.typeName; + + if (!name) { + throw new Error("typeAnnotation of event doesn't have a name"); + } +} + module.exports = { throwIfModuleInterfaceIsMisnamed, throwIfUnsupportedFunctionReturnTypeAnnotationParserError, @@ -330,4 +339,5 @@ module.exports = { throwIfMoreThanOneCodegenNativecommands, throwIfConfigNotfound, throwIfMoreThanOneConfig, + throwIfEventHasNoName, }; 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 fefa09cefb9f60..6afd8eb3be1d5b 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/events.js +++ b/packages/react-native-codegen/src/parsers/flow/components/events.js @@ -15,6 +15,8 @@ import type { NamedShape, EventTypeAnnotation, } from '../../../CodegenSchema.js'; +import type {Parser} from '../../parser'; +const {throwIfEventHasNoName} = require('../../error-utils'); function getPropertyType( /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's @@ -108,6 +110,7 @@ function getPropertyType( } function findEventArgumentsAndType( + parser: Parser, typeAnnotation: $FlowFixMe, types: TypeMap, bubblingType: void | 'direct' | 'bubble', @@ -117,9 +120,7 @@ function findEventArgumentsAndType( bubblingType: ?('direct' | 'bubble'), paperTopLevelNameDeprecated: ?$FlowFixMe, } { - if (!typeAnnotation.id) { - throw new Error("typeAnnotation of event doesn't have a name"); - } + throwIfEventHasNoName(typeAnnotation, parser); const name = typeAnnotation.id.name; if (name === '$ReadOnly') { return { @@ -144,6 +145,7 @@ function findEventArgumentsAndType( }; } return findEventArgumentsAndType( + parser, typeAnnotation.typeParameters.params[0], types, eventType, @@ -151,6 +153,7 @@ function findEventArgumentsAndType( ); } else if (types[name]) { return findEventArgumentsAndType( + parser, types[name].right, types, bubblingType, @@ -191,6 +194,7 @@ function getEventArgument(argumentProps, name: $FlowFixMe) { function buildEventSchema( types: TypeMap, property: EventTypeAST, + parser: Parser, ): ?EventTypeShape { const name = property.key.name; const optional = @@ -210,7 +214,7 @@ function buildEventSchema( } const {argumentProps, bubblingType, paperTopLevelNameDeprecated} = - findEventArgumentsAndType(typeAnnotation, types); + findEventArgumentsAndType(parser, typeAnnotation, types); if (bubblingType && argumentProps) { if (paperTopLevelNameDeprecated != null) { @@ -258,10 +262,11 @@ type TypeMap = { function getEvents( eventTypeAST: $ReadOnlyArray, types: TypeMap, + parser: Parser, ): $ReadOnlyArray { return eventTypeAST .filter(property => property.type === 'ObjectTypeProperty') - .map(property => buildEventSchema(types, property)) + .map(property => buildEventSchema(types, property, parser)) .filter(Boolean); } 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 ab3aa436de3f61..57d75d6d72cea5 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/index.js +++ b/packages/react-native-codegen/src/parsers/flow/components/index.js @@ -129,7 +129,7 @@ function buildComponentSchema( const {extendsProps, props} = getProps(propProperties, types); const options = getOptions(optionsExpression); - const events = getEvents(propProperties, types); + const events = getEvents(propProperties, types, parser); const commands = getCommands(commandProperties, types); return { 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 a92931afb136db..e28f653de8c5c5 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/events.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/events.js @@ -16,8 +16,10 @@ import type { EventTypeAnnotation, } from '../../../CodegenSchema.js'; import type {TypeDeclarationMap} from '../../utils'; +import type {Parser} from '../../parser'; const {flattenProperties} = require('./componentsUtils'); const {parseTopLevelType} = require('../parseTopLevelType'); +const {throwIfEventHasNoName} = require('../../error-utils'); function getPropertyType( /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's @@ -110,6 +112,7 @@ function getPropertyType( } function findEventArgumentsAndType( + parser: Parser, typeAnnotation: $FlowFixMe, types: TypeDeclarationMap, bubblingType: void | 'direct' | 'bubble', @@ -135,12 +138,11 @@ function findEventArgumentsAndType( }; } - if (!typeAnnotation.typeName) { - throw new Error("typeAnnotation of event doesn't have a name"); - } + throwIfEventHasNoName(typeAnnotation, parser); const name = typeAnnotation.typeName.name; if (name === 'Readonly') { return findEventArgumentsAndType( + parser, typeAnnotation.typeParameters.params[0], types, bubblingType, @@ -163,6 +165,7 @@ function findEventArgumentsAndType( }; default: return findEventArgumentsAndType( + parser, typeAnnotation.typeParameters.params[0], types, eventType, @@ -175,6 +178,7 @@ function findEventArgumentsAndType( elementType = elementType.typeAnnotation; } return findEventArgumentsAndType( + parser, elementType, types, bubblingType, @@ -214,6 +218,7 @@ type EventTypeAST = Object; function buildEventSchema( types: TypeDeclarationMap, property: EventTypeAST, + parser: Parser, ): EventTypeShape { // unpack WithDefault, (T) or T|U const topLevelType = parseTopLevelType( @@ -225,7 +230,7 @@ function buildEventSchema( const typeAnnotation = topLevelType.type; const optional = property.optional || topLevelType.optional; const {argumentProps, bubblingType, paperTopLevelNameDeprecated} = - findEventArgumentsAndType(typeAnnotation, types); + findEventArgumentsAndType(parser, typeAnnotation, types); if (!argumentProps) { throw new Error(`Unable to determine event arguments for "${name}"`); @@ -260,8 +265,11 @@ function buildEventSchema( function getEvents( eventTypeAST: $ReadOnlyArray, types: TypeDeclarationMap, + parser: Parser, ): $ReadOnlyArray { - return eventTypeAST.map(property => buildEventSchema(types, property)); + return eventTypeAST.map(property => + buildEventSchema(types, property, parser), + ); } module.exports = { 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 a42fd058cf9a33..2e1ea1f72951ef 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/index.js @@ -136,7 +136,7 @@ function buildComponentSchema( const componentEventAsts: Array = []; categorizeProps(propProperties, types, componentEventAsts); const {props, extendsProps} = getProps(propProperties, types); - const events = getEvents(componentEventAsts, types); + const events = getEvents(componentEventAsts, types, parser); const commands = getCommands(commandProperties, types); return {