/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow strict * @format */ 'use strict'; import type { EventTypeShape, NamedShape, EventTypeAnnotation, } from '../../../CodegenSchema.js'; function getPropertyType( /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ name, optional: boolean, typeAnnotation: $FlowFixMe, ): NamedShape { const type = typeAnnotation.type === 'GenericTypeAnnotation' ? typeAnnotation.id.name : typeAnnotation.type; switch (type) { case 'BooleanTypeAnnotation': return { name, optional, typeAnnotation: { type: 'BooleanTypeAnnotation', }, }; case 'StringTypeAnnotation': return { name, optional, typeAnnotation: { type: 'StringTypeAnnotation', }, }; case 'Int32': return { name, optional, typeAnnotation: { type: 'Int32TypeAnnotation', }, }; case 'Double': return { name, optional, typeAnnotation: { type: 'DoubleTypeAnnotation', }, }; case 'Float': return { name, optional, typeAnnotation: { type: 'FloatTypeAnnotation', }, }; case '$ReadOnly': return getPropertyType( name, optional, typeAnnotation.typeParameters.params[0], ); case 'ObjectTypeAnnotation': return { name, optional, typeAnnotation: { type: 'ObjectTypeAnnotation', properties: typeAnnotation.properties.map(buildPropertiesForEvent), }, }; case 'UnionTypeAnnotation': return { name, optional, typeAnnotation: { type: 'StringEnumTypeAnnotation', options: typeAnnotation.types.map(option => option.value), }, }; default: (type: empty); throw new Error(`Unable to determine event type for "${name}": ${type}`); } } function findEventArgumentsAndType( typeAnnotation: $FlowFixMe, types: TypeMap, bubblingType: void | 'direct' | 'bubble', paperName: ?$FlowFixMe, ): { argumentProps: $FlowFixMe, bubblingType: ?('direct' | 'bubble'), paperTopLevelNameDeprecated: ?$FlowFixMe, } { if (!typeAnnotation.id) { throw new Error("typeAnnotation of event doesn't have a name"); } const name = typeAnnotation.id.name; if (name === '$ReadOnly') { return { argumentProps: typeAnnotation.typeParameters.params[0].properties, paperTopLevelNameDeprecated: paperName, bubblingType, }; } else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') { const eventType = name === 'BubblingEventHandler' ? 'bubble' : 'direct'; const paperTopLevelNameDeprecated = typeAnnotation.typeParameters.params.length > 1 ? typeAnnotation.typeParameters.params[1].value : null; if ( typeAnnotation.typeParameters.params[0].type === 'NullLiteralTypeAnnotation' ) { return { argumentProps: [], bubblingType: eventType, paperTopLevelNameDeprecated, }; } return findEventArgumentsAndType( typeAnnotation.typeParameters.params[0], types, eventType, paperTopLevelNameDeprecated, ); } else if (types[name]) { return findEventArgumentsAndType( types[name].right, types, bubblingType, paperName, ); } else { return { argumentProps: null, bubblingType: null, paperTopLevelNameDeprecated: null, }; } } /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ function buildPropertiesForEvent(property): NamedShape { const name = property.key.name; const optional = property.value.type === 'NullableTypeAnnotation' || property.optional; let typeAnnotation = property.value.type === 'NullableTypeAnnotation' ? property.value.typeAnnotation : property.value; return getPropertyType(name, optional, typeAnnotation); } /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ function getEventArgument(argumentProps, name: $FlowFixMe) { return { type: 'ObjectTypeAnnotation', properties: argumentProps.map(buildPropertiesForEvent), }; } function buildEventSchema( types: TypeMap, property: EventTypeAST, ): ?EventTypeShape { const name = property.key.name; const optional = property.optional || property.value.type === 'NullableTypeAnnotation'; let typeAnnotation = property.value.type === 'NullableTypeAnnotation' ? property.value.typeAnnotation : property.value; if ( typeAnnotation.type !== 'GenericTypeAnnotation' || (typeAnnotation.id.name !== 'BubblingEventHandler' && typeAnnotation.id.name !== 'DirectEventHandler') ) { return null; } const {argumentProps, bubblingType, paperTopLevelNameDeprecated} = findEventArgumentsAndType(typeAnnotation, types); if (bubblingType && argumentProps) { if (paperTopLevelNameDeprecated != null) { return { name, optional, bubblingType, paperTopLevelNameDeprecated, typeAnnotation: { type: 'EventTypeAnnotation', argument: getEventArgument(argumentProps, name), }, }; } return { name, optional, bubblingType, typeAnnotation: { type: 'EventTypeAnnotation', argument: getEventArgument(argumentProps, name), }, }; } if (argumentProps === null) { throw new Error(`Unable to determine event arguments for "${name}"`); } if (bubblingType === null) { throw new Error(`Unable to determine event arguments for "${name}"`); } } // $FlowFixMe[unclear-type] there's no flowtype for ASTs type EventTypeAST = Object; type TypeMap = { // $FlowFixMe[unclear-type] there's no flowtype for ASTs [string]: Object, ... }; function getEvents( eventTypeAST: $ReadOnlyArray, types: TypeMap, ): $ReadOnlyArray { return eventTypeAST .filter(property => property.type === 'ObjectTypeProperty') .map(property => buildEventSchema(types, property)) .filter(Boolean); } module.exports = { getEvents, };