UNPKG

react-native-codegen

Version:
254 lines (245 loc) 7.02 kB
/** * 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, /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ typeAnnotation, ) { const type = typeAnnotation.type === 'TSTypeReference' ? typeAnnotation.typeName.name : typeAnnotation.type; switch (type) { case 'TSBooleanKeyword': return { name, optional, typeAnnotation: { type: 'BooleanTypeAnnotation', }, }; case 'TSStringKeyword': 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 'TSTypeLiteral': return { name, optional, typeAnnotation: { type: 'ObjectTypeAnnotation', properties: typeAnnotation.members.map(buildPropertiesForEvent), }, }; case 'TSUnionType': // Check for <T | null | undefined> if ( typeAnnotation.types.some( t => t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword', ) ) { const optionalType = typeAnnotation.types.filter( t => t.type !== 'TSNullKeyword' && t.type !== 'TSUndefinedKeyword', )[0]; // Check for <(T | T2) | null | undefined> if (optionalType.type === 'TSParenthesizedType') { return getPropertyType(name, true, optionalType.typeAnnotation); } return getPropertyType(name, true, optionalType); } return { name, optional, typeAnnotation: { type: 'StringEnumTypeAnnotation', options: typeAnnotation.types.map(option => option.literal.value), }, }; default: type; throw new Error(`Unable to determine event type for "${name}": ${type}`); } } function findEventArgumentsAndType( typeAnnotation, types, bubblingType, paperName, ) { if (!typeAnnotation.typeName) { throw new Error("typeAnnotation of event doesn't have a name"); } const name = typeAnnotation.typeName.name; if (name === 'Readonly') { return { argumentProps: typeAnnotation.typeParameters.params[0].members, 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].literal.value : null; if (typeAnnotation.typeParameters.params[0].type === 'TSNullKeyword') { return { argumentProps: [], bubblingType: eventType, paperTopLevelNameDeprecated, }; } return findEventArgumentsAndType( typeAnnotation.typeParameters.params[0], types, eventType, paperTopLevelNameDeprecated, ); } else if (types[name]) { return findEventArgumentsAndType( types[name].typeAnnotation, 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.optional || false; let typeAnnotation = property.typeAnnotation.typeAnnotation; 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; let optional = property.optional || false; let typeAnnotation = property.typeAnnotation.typeAnnotation; // Check for T | null | undefined if ( typeAnnotation.type === 'TSUnionType' && typeAnnotation.types.some( t => t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword', ) ) { typeAnnotation = typeAnnotation.types.filter( t => t.type !== 'TSNullKeyword' && t.type !== 'TSUndefinedKeyword', )[0]; optional = true; } if ( typeAnnotation.type !== 'TSTypeReference' || (typeAnnotation.typeName.name !== 'BubblingEventHandler' && typeAnnotation.typeName.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] TODO(T108222691): Use flow-types for @babel/parser function getEvents(eventTypeAST, types) { return eventTypeAST .filter(property => property.type === 'TSPropertySignature') .map(property => buildEventSchema(types, property)) .filter(Boolean); } module.exports = { getEvents, };