UNPKG

@react-native/codegen

Version:
258 lines (253 loc) • 6.94 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'; const { throwIfArgumentPropsAreNull, throwIfBubblingTypeIsNull, throwIfEventHasNoName, } = require('../../error-utils'); const { buildPropertiesForEvent, emitBuildEventSchema, getEventArgument, handleEventHandler, } = require('../../parsers-commons'); const { emitBoolProp, emitDoubleProp, emitFloatProp, emitInt32Prop, emitMixedProp, emitObjectProp, emitStringProp, emitUnionProp, } = require('../../parsers-primitives'); const {parseTopLevelType} = require('../parseTopLevelType'); const {flattenProperties} = require('./componentsUtils'); function getPropertyType( /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ name, optionalProperty, /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ annotation, parser, ) { const topLevelType = parseTopLevelType(annotation, parser); const typeAnnotation = topLevelType.type; const optional = optionalProperty || topLevelType.optional; const type = typeAnnotation.type === 'TSTypeReference' ? parser.getTypeAnnotationName(typeAnnotation) : typeAnnotation.type; switch (type) { case 'TSBooleanKeyword': return emitBoolProp(name, optional); case 'TSStringKeyword': return emitStringProp(name, optional); case 'Int32': return emitInt32Prop(name, optional); case 'Double': return emitDoubleProp(name, optional); case 'Float': return emitFloatProp(name, optional); case 'TSTypeLiteral': return emitObjectProp( name, optional, parser, typeAnnotation, extractArrayElementType, ); case 'TSUnionType': return emitUnionProp(name, optional, parser, typeAnnotation); case 'UnsafeMixed': return emitMixedProp(name, optional); case 'TSArrayType': return { name, optional, typeAnnotation: extractArrayElementType(typeAnnotation, name, parser), }; default: throw new Error(`Unable to determine event type for "${name}": ${type}`); } } function extractArrayElementType(typeAnnotation, name, parser) { const type = extractTypeFromTypeAnnotation(typeAnnotation, parser); switch (type) { case 'TSParenthesizedType': return extractArrayElementType( typeAnnotation.typeAnnotation, name, parser, ); case 'TSBooleanKeyword': return { type: 'BooleanTypeAnnotation', }; case 'TSStringKeyword': return { type: 'StringTypeAnnotation', }; case 'Float': return { type: 'FloatTypeAnnotation', }; case 'Int32': return { type: 'Int32TypeAnnotation', }; case 'TSNumberKeyword': case 'Double': return { type: 'DoubleTypeAnnotation', }; case 'TSUnionType': return { type: 'StringLiteralUnionTypeAnnotation', types: typeAnnotation.types.map(option => ({ type: 'StringLiteralTypeAnnotation', value: parser.getLiteralValue(option), })), }; case 'TSTypeLiteral': return { type: 'ObjectTypeAnnotation', properties: parser .getObjectProperties(typeAnnotation) .map(member => buildPropertiesForEvent(member, parser, getPropertyType), ), }; case 'TSArrayType': return { type: 'ArrayTypeAnnotation', elementType: extractArrayElementType( typeAnnotation.elementType, name, parser, ), }; default: throw new Error( `Unrecognized ${type} for Array ${name} in events.\n${JSON.stringify( typeAnnotation, null, 2, )}`, ); } } function extractTypeFromTypeAnnotation(typeAnnotation, parser) { return typeAnnotation.type === 'TSTypeReference' ? parser.getTypeAnnotationName(typeAnnotation) : typeAnnotation.type; } function findEventArgumentsAndType( parser, typeAnnotation, types, bubblingType, paperName, ) { if (typeAnnotation.type === 'TSInterfaceDeclaration') { return { argumentProps: flattenProperties([typeAnnotation], types, parser), paperTopLevelNameDeprecated: paperName, bubblingType, }; } if (typeAnnotation.type === 'TSTypeLiteral') { return { argumentProps: parser.getObjectProperties(typeAnnotation), paperTopLevelNameDeprecated: paperName, bubblingType, }; } throwIfEventHasNoName(typeAnnotation, parser); const name = parser.getTypeAnnotationName(typeAnnotation); if (name === 'Readonly') { return findEventArgumentsAndType( parser, typeAnnotation.typeParameters.params[0], types, bubblingType, paperName, ); } else if (name === 'BubblingEventHandler' || name === 'DirectEventHandler') { return handleEventHandler( name, typeAnnotation, parser, types, findEventArgumentsAndType, ); } else if (types[name]) { let elementType = types[name]; if (elementType.type === 'TSTypeAliasDeclaration') { elementType = elementType.typeAnnotation; } return findEventArgumentsAndType( parser, elementType, types, bubblingType, paperName, ); } else { return { argumentProps: null, bubblingType: null, paperTopLevelNameDeprecated: null, }; } } // $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser function buildEventSchema(types, property, parser) { // unpack WithDefault, (T) or T|U const topLevelType = parseTopLevelType( property.typeAnnotation.typeAnnotation, parser, types, ); const name = property.key.name; const typeAnnotation = topLevelType.type; const optional = property.optional || topLevelType.optional; const {argumentProps, bubblingType, paperTopLevelNameDeprecated} = findEventArgumentsAndType(parser, typeAnnotation, types); const nonNullableArgumentProps = throwIfArgumentPropsAreNull( argumentProps, name, ); const nonNullableBubblingType = throwIfBubblingTypeIsNull(bubblingType, name); const argument = getEventArgument( nonNullableArgumentProps, parser, getPropertyType, ); return emitBuildEventSchema( paperTopLevelNameDeprecated, name, optional, nonNullableBubblingType, argument, ); } function getEvents(eventTypeAST, types, parser) { return eventTypeAST .map(property => buildEventSchema(types, property, parser)) .filter(Boolean); } module.exports = { getEvents, extractArrayElementType, };