/** * 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. * * * @format */ 'use strict'; function getPropertyType( /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ name, optional, typeAnnotation, ) { 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; throw new Error(`Unable to determine event type for "${name}": ${type}`); } } function findEventArgumentsAndType( typeAnnotation, types, bubblingType, paperName, ) { 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) { 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) { return { type: 'ObjectTypeAnnotation', properties: argumentProps.map(buildPropertiesForEvent), }; } function buildEventSchema(types, property) { 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 _findEventArgumentsAn = findEventArgumentsAndType( typeAnnotation, types, ), argumentProps = _findEventArgumentsAn.argumentProps, bubblingType = _findEventArgumentsAn.bubblingType, paperTopLevelNameDeprecated = _findEventArgumentsAn.paperTopLevelNameDeprecated; 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 function getEvents(eventTypeAST, types) { return eventTypeAST .filter(property => property.type === 'ObjectTypeProperty') .map(property => buildEventSchema(types, property)) .filter(Boolean); } module.exports = { getEvents, };