UNPKG

@react-native/codegen

Version:
220 lines (217 loc) • 6.21 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 getValueFromTypes(value, types) { switch (value.type) { case 'TSTypeReference': if (types[value.typeName.name]) { return getValueFromTypes(types[value.typeName.name], types); } else { return value; } case 'TSTypeAliasDeclaration': return getValueFromTypes(value.typeAnnotation, types); default: return value; } } function isNull(t) { return t.type === 'TSNullKeyword' || t.type === 'TSUndefinedKeyword'; } function isNullOrVoid(t) { return isNull(t) || t.type === 'TSVoidKeyword'; } function couldBeNumericLiteral(type) { return type === 'Literal' || type === 'NumericLiteral'; } function couldBeSimpleLiteral(type) { return ( couldBeNumericLiteral(type) || type === 'StringLiteral' || type === 'BooleanLiteral' ); } function evaluateLiteral(literalNode) { const valueType = literalNode.type; if (valueType === 'TSLiteralType') { const literal = literalNode.literal; if (couldBeSimpleLiteral(literal.type)) { if ( typeof literal.value === 'string' || typeof literal.value === 'number' || typeof literal.value === 'boolean' ) { return literal.value; } } else if ( literal.type === 'UnaryExpression' && literal.operator === '-' && couldBeNumericLiteral(literal.argument.type) && typeof literal.argument.value === 'number' ) { return -literal.argument.value; } } else if (isNull(literalNode)) { return null; } throw new Error( 'The default value in WithDefault must be string, number, boolean or null .', ); } function handleUnionAndParen(type, result, parser, knownTypes) { switch (type.type) { case 'TSParenthesizedType': { handleUnionAndParen(type.typeAnnotation, result, parser, knownTypes); break; } case 'TSUnionType': { // the order is important // result.optional must be set first for (const t of type.types) { if (isNullOrVoid(t)) { result.optional = true; } } for (const t of type.types) { if (!isNullOrVoid(t)) { handleUnionAndParen(t, result, parser, knownTypes); } } break; } case 'TSTypeReference': if (type.typeName.name === 'Readonly') { handleUnionAndParen( type.typeParameters.params[0], result, parser, knownTypes, ); } else if (parser.getTypeAnnotationName(type) === 'WithDefault') { if (result.optional) { throw new Error( 'WithDefault<> is optional and does not need to be marked as optional. Please remove the union of undefined and/or null', ); } if (type.typeParameters.params.length !== 2) { throw new Error( 'WithDefault requires two parameters: type and default value.', ); } if (result.defaultValue !== undefined) { throw new Error( 'Multiple WithDefault is not allowed nested or in a union type.', ); } result.optional = true; result.defaultValue = evaluateLiteral(type.typeParameters.params[1]); handleUnionAndParen( type.typeParameters.params[0], result, parser, knownTypes, ); } else if (!knownTypes) { result.unions.push(type); } else { const resolvedType = getValueFromTypes(type, knownTypes); if ( resolvedType.type === 'TSTypeReference' && resolvedType.typeName.name === type.typeName.name ) { result.unions.push(type); } else { handleUnionAndParen(resolvedType, result, parser, knownTypes); } } break; default: result.unions.push(type); } } function parseTopLevelType(type, parser, knownTypes) { let result = { unions: [], optional: false, }; handleUnionAndParen(type, result, parser, knownTypes); if (result.unions.length === 0) { throw new Error('Union type could not be just null or undefined.'); } else if (result.unions.length === 1) { return { type: result.unions[0], optional: result.optional, defaultValue: result.defaultValue, }; } else { return { type: { type: 'TSUnionType', types: result.unions, }, optional: result.optional, defaultValue: result.defaultValue, }; } } function handleIntersectionAndParen(type, result, parser, knownTypes) { switch (type.type) { case 'TSParenthesizedType': { handleIntersectionAndParen( type.typeAnnotation, result, parser, knownTypes, ); break; } case 'TSIntersectionType': { for (const t of type.types) { handleIntersectionAndParen(t, result, parser, knownTypes); } break; } case 'TSTypeReference': if (type.typeName.name === 'Readonly') { handleIntersectionAndParen( type.typeParameters.params[0], result, parser, knownTypes, ); } else if (parser.getTypeAnnotationName(type) === 'WithDefault') { throw new Error('WithDefault<> is now allowed in intersection types.'); } else if (!knownTypes) { result.push(type); } else { const resolvedType = getValueFromTypes(type, knownTypes); if ( resolvedType.type === 'TSTypeReference' && resolvedType.typeName.name === type.typeName.name ) { result.push(type); } else { handleIntersectionAndParen(resolvedType, result, parser, knownTypes); } } break; default: result.push(type); } } function flattenIntersectionType(type, parser, knownTypes) { const result = []; handleIntersectionAndParen(type, result, parser, knownTypes); return result; } module.exports = { parseTopLevelType, flattenIntersectionType, };