UNPKG

@react-native/compatibility-check

Version:

Check a React Native app's boundary between JS and Native for incompatibilities

1,252 lines (1,250 loc) 36.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true, }); exports.compareEnumDeclarationMemberArrays = compareEnumDeclarationMemberArrays; exports.compareEnumDeclarationWithMembers = compareEnumDeclarationWithMembers; exports.compareEnumDeclarations = compareEnumDeclarations; exports.compareFunctionTypes = compareFunctionTypes; exports.compareGenericObjectTypes = compareGenericObjectTypes; exports.compareNumberLiteralTypes = compareNumberLiteralTypes; exports.compareObjectTypes = compareObjectTypes; exports.comparePromiseTypes = comparePromiseTypes; exports.compareStringLiteralTypes = compareStringLiteralTypes; exports.compareStringLiteralUnionTypes = compareStringLiteralUnionTypes; exports.compareTypeAnnotation = compareTypeAnnotation; exports.compareTypes = compareTypes; exports.compareUnionTypes = compareUnionTypes; var _ComparisonResult = require("./ComparisonResult"); var _SortTypeAnnotations = require("./SortTypeAnnotations.js"); var _invariant = _interopRequireDefault(require("invariant")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const EQUALITY_MSG = "previousType and afterType differ despite check"; let _newerTypesReg, _olderTypesReg, _newerEnumMap, _olderEnumMap; function compareTypes( newerType, olderType, newerTypesReg, olderTypesReg, newerEnumMap, olderEnumMap, ) { if (!olderType) { return { status: "skipped", }; } _newerTypesReg = newerTypesReg; _olderTypesReg = olderTypesReg; _newerEnumMap = newerEnumMap; _olderEnumMap = olderEnumMap; const res = compareTypeAnnotation(newerType, olderType); _newerTypesReg = undefined; _olderTypesReg = undefined; _newerEnumMap = undefined; _olderEnumMap = undefined; return res; } function removeNullableTypeAnnotations(annotation) { if (annotation.type === "NullableTypeAnnotation") { return removeNullableTypeAnnotations(annotation.typeAnnotation); } return annotation; } function lookupType(name, aliases) { return aliases?.[name]; } function lookupEnum(name, enums) { return enums?.[name]; } function compareTypeAnnotation( originalNewerAnnotation, originalOlderAnnotation, ) { const newerAnnotation = originalNewerAnnotation; const olderAnnotation = originalOlderAnnotation; if (newerAnnotation.type === "TypeAliasTypeAnnotation") { const newerAnnotationDefinition = lookupType( newerAnnotation.name, _newerTypesReg, ); if (newerAnnotationDefinition != null) { return compareTypeAnnotation(newerAnnotationDefinition, olderAnnotation); } } if (olderAnnotation.type === "TypeAliasTypeAnnotation") { const olderAnnotationDefinition = lookupType( olderAnnotation.name, _olderTypesReg, ); if (olderAnnotationDefinition != null) { return compareTypeAnnotation(newerAnnotation, olderAnnotationDefinition); } } (0, _invariant.default)( newerAnnotation.type !== "TypeAliasTypeAnnotation" && olderAnnotation.type !== "TypeAliasTypeAnnotation", EQUALITY_MSG, ); if (newerAnnotation.type !== olderAnnotation.type) { if ( newerAnnotation.type === "NullableTypeAnnotation" || olderAnnotation.type === "NullableTypeAnnotation" ) { return compareNullableChange(newerAnnotation, olderAnnotation); } return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Type annotations are not the same.", newerAnnotation, olderAnnotation, ), ); } switch (newerAnnotation.type) { case "AnyTypeAnnotation": case "MixedTypeAnnotation": case "DoubleTypeAnnotation": case "FloatTypeAnnotation": case "Int32TypeAnnotation": case "BooleanTypeAnnotation": case "NumberTypeAnnotation": case "StringTypeAnnotation": case "VoidTypeAnnotation": return { status: "matching", }; case "ArrayTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "ArrayTypeAnnotation", EQUALITY_MSG, ); return compareTypeAnnotation( newerAnnotation.elementType, olderAnnotation.elementType, ); case "EnumDeclaration": (0, _invariant.default)( olderAnnotation.type === "EnumDeclaration", EQUALITY_MSG, ); return compareEnumDeclarations(newerAnnotation, olderAnnotation); case "EnumDeclarationWithMembers": (0, _invariant.default)( olderAnnotation.type === "EnumDeclarationWithMembers", EQUALITY_MSG, ); return compareEnumDeclarationWithMembers( newerAnnotation, olderAnnotation, ); case "FunctionTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "FunctionTypeAnnotation", EQUALITY_MSG, ); return compareFunctionTypes(newerAnnotation, olderAnnotation); case "PromiseTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "PromiseTypeAnnotation", EQUALITY_MSG, ); return comparePromiseTypes(newerAnnotation, olderAnnotation); case "GenericObjectTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "GenericObjectTypeAnnotation", EQUALITY_MSG, ); return compareGenericObjectTypes(newerAnnotation, olderAnnotation); case "NullableTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "NullableTypeAnnotation", EQUALITY_MSG, ); return compareTypeAnnotation( newerAnnotation.typeAnnotation, olderAnnotation.typeAnnotation, ); case "ObjectTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "ObjectTypeAnnotation", EQUALITY_MSG, ); return compareObjectTypes( newerAnnotation.properties, olderAnnotation.properties, ); case "NumberLiteralTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "NumberLiteralTypeAnnotation", EQUALITY_MSG, ); return compareNumberLiteralTypes(newerAnnotation, olderAnnotation); case "StringLiteralUnionTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "StringLiteralUnionTypeAnnotation", EQUALITY_MSG, ); return compareStringLiteralUnionTypes(newerAnnotation, olderAnnotation); case "StringLiteralTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "StringLiteralTypeAnnotation", EQUALITY_MSG, ); return compareStringLiteralTypes(newerAnnotation, olderAnnotation); case "UnionTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "UnionTypeAnnotation", EQUALITY_MSG, ); return compareUnionTypes(newerAnnotation, olderAnnotation); case "EventEmitterTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "EventEmitterTypeAnnotation", EQUALITY_MSG, ); return compareEventEmitterTypes(newerAnnotation, olderAnnotation); case "ReservedTypeAnnotation": (0, _invariant.default)( olderAnnotation.type === "ReservedTypeAnnotation", EQUALITY_MSG, ); return compareReservedTypeAnnotation(newerAnnotation, olderAnnotation); default: throw new Error(`Unsupported type annotation: ${newerAnnotation.type}`); } } function compareObjectTypeProperty(first, second) { if (first.name < second.name) { return -1; } else if (first.name > second.name) { return 1; } return 0; } function compareEnumMember(first, second) { if (first.name < second.name) { return -1; } else if (first.name > second.name) { return 1; } return 0; } function updatePropertyError(name, newType, oldType, result) { return (oldError) => { const comparisonError = (0, _ComparisonResult.typeAnnotationComparisonError)( "has conflicting type changes", newType, oldType, oldError, ); const newFault = { property: name, fault: comparisonError, }; if (result.errorProperties) { result.errorProperties.push(newFault); } else { result.errorProperties = [newFault]; } }; } function updateEnumMemberError(name, newType, oldType, result) { return (oldError) => { const comparisonError = (0, _ComparisonResult.typeAnnotationComparisonError)( "has conflicting changes", newType, oldType, oldError, ); const newFault = { member: name, fault: comparisonError, }; if (result.errorMembers) { result.errorMembers.push(newFault); } else { result.errorMembers = [newFault]; } }; } function updateNestedProperties(name, propertyChange, result) { if (result.nestedPropertyChanges) { result.nestedPropertyChanges.push([name, propertyChange]); } else { result.nestedPropertyChanges = [[name, propertyChange]]; } } function updateMadeOptional(name, result, furtherChange) { if (result.madeOptional) { result.madeOptional.push({ property: name, furtherChange, }); } else { result.madeOptional = [ { property: name, furtherChange, }, ]; } } function updateMadeStrict(name, result, furtherChange) { if (result.madeStrict) { result.madeStrict.push({ property: name, furtherChange, }); } else { result.madeStrict = [ { property: name, furtherChange, }, ]; } } function checkOptionalityChanges( name, newOptionality, oldOptionality, result, furtherChange, ) { if (newOptionality === oldOptionality) { if (furtherChange) { updateNestedProperties(name, furtherChange, result); } return result; } if (newOptionality) { updateMadeOptional(name, result, furtherChange); } else { updateMadeStrict(name, result, furtherChange); } return result; } function comparePropertyArrays(newerOriginal, olderOriginal) { const newer = newerOriginal.slice(0); const older = olderOriginal.slice(0); if (newer.length === 0 && older.length === 0) { return {}; } if (newer.length === 0) { return { missingProperties: older, }; } if (older.length === 0) { return { addedProperties: newer, }; } const newerHead = newer.pop(); const olderHead = older.pop(); (0, _invariant.default)( newerHead != null && olderHead != null, "Array is empty", ); const newerName = newerHead.name; const olderName = olderHead.name; if (newerName === olderName) { const comparedTypes = compareTypeAnnotation( newerHead.typeAnnotation, olderHead.typeAnnotation, ); const result = comparePropertyArrays(newer, older); switch (comparedTypes.status) { case "matching": return checkOptionalityChanges( newerName, newerHead.optional, olderHead.optional, result, ); case "skipped": throw new Error( "Internal error: returned 'skipped' for non-optional older type", ); case "nullableChange": return checkOptionalityChanges( newerName, !comparedTypes.nullableLog.optionsReduced, comparedTypes.nullableLog.optionsReduced, result, ); case "members": case "properties": case "functionChange": case "positionalTypeChange": return checkOptionalityChanges( newerName, newerHead.optional, olderHead.optional, result, comparedTypes, ); case "error": updatePropertyError( newerName, newerHead.typeAnnotation, olderHead.typeAnnotation, result, )(comparedTypes.errorLog); return result; default: throw new Error("Unsupported status " + comparedTypes.status); } } if (newerName > olderName) { older.push(olderHead); const result = comparePropertyArrays(newer, older); if (result.hasOwnProperty("addedProperties") && result.addedProperties) { result.addedProperties = result.addedProperties.concat([newerHead]); } else { result.addedProperties = [newerHead]; } return result; } newer.push(newerHead); const result = comparePropertyArrays(newer, older); if (result.hasOwnProperty("missingProperties") && result.missingProperties) { result.missingProperties = result.missingProperties.concat([olderHead]); } else { result.missingProperties = [olderHead]; } return result; } function compareObjectTypes(newerPropertyTypes, olderPropertyTypes) { if (newerPropertyTypes.length === 0 && olderPropertyTypes.length === 0) { return { status: "matching", }; } const sortedNewerTypes = []; newerPropertyTypes.forEach((prop) => sortedNewerTypes.push(prop)); if (sortedNewerTypes.length !== 0) { sortedNewerTypes.sort(compareObjectTypeProperty); } const sortedOlderTypes = []; olderPropertyTypes.forEach((prop) => sortedOlderTypes.push(prop)); if (sortedOlderTypes.length !== 0) { sortedOlderTypes.sort(compareObjectTypeProperty); } if (sortedNewerTypes.length === 0) { return { status: "properties", propertyLog: { missingProperties: sortedOlderTypes, }, }; } if (sortedOlderTypes.length === 0) { return { status: "properties", propertyLog: { addedProperties: sortedNewerTypes, }, }; } const result = comparePropertyArrays(sortedNewerTypes, sortedOlderTypes); if ((0, _ComparisonResult.isPropertyLogEmpty)(result)) { return { status: "matching", }; } if (result.errorProperties) { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.propertyComparisonError)( result.errorProperties.length > 1 ? "Object contained properties with type mismatches" : "Object contained a property with a type mismatch", result.errorProperties, ), ); } if ( (result.addedProperties && result.addedProperties.length > 0 && result.addedProperties.length === newerPropertyTypes.length) || (result.missingProperties && result.missingProperties.length > 0 && result.missingProperties.length === olderPropertyTypes.length) ) { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Object types do not match.", objectTypeAnnotation(newerPropertyTypes), objectTypeAnnotation(olderPropertyTypes), ), ); } return { status: "properties", propertyLog: result, }; } function objectTypeAnnotation(properties) { return { type: "ObjectTypeAnnotation", properties, baseTypes: [], }; } function compareEnumDeclarations(newerDeclaration, olderDeclaration) { if (newerDeclaration.memberType !== olderDeclaration.memberType) { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "EnumDeclaration member types are not the same", newerDeclaration, olderDeclaration, ), ); } const newerAnnotationDefinition = lookupEnum( newerDeclaration.name, _newerEnumMap, ); const olderAnnotationDefinition = lookupEnum( olderDeclaration.name, _olderEnumMap, ); (0, _invariant.default)( newerAnnotationDefinition != null && olderAnnotationDefinition != null, "Could not find enum definition", ); return compareTypeAnnotation( newerAnnotationDefinition, olderAnnotationDefinition, ); } function compareEnumDeclarationMemberArrays(newer, older) { if (newer.length === 0 && older.length === 0) { return {}; } else if (newer.length === 0) { return { missingMembers: older, }; } else if (older.length === 0) { return { addedMembers: newer, }; } const newerHead = newer.pop(); const olderHead = older.pop(); (0, _invariant.default)( newerHead != null && olderHead != null, "Array is empty", ); const newerName = newerHead.name; const olderName = olderHead.name; if (newerName === olderName) { const comparedTypes = compareTypeAnnotation( newerHead.value, olderHead.value, ); const result = compareEnumDeclarationMemberArrays(newer, older); switch (comparedTypes.status) { case "matching": return result; case "error": updateEnumMemberError( newerName, newerHead.value, olderHead.value, result, )(comparedTypes.errorLog); return result; case "skipped": throw new Error( "Internal error: returned 'skipped' for non-optional older type", ); case "nullableChange": case "properties": case "functionChange": case "positionalTypeChange": case "members": break; default: throw new Error("Unsupported status " + comparedTypes.status); } } else if (newerName > olderName) { older.push(olderHead); const result = compareEnumDeclarationMemberArrays(newer, older); if (result.hasOwnProperty("addedMembers") && result.addedMembers) { result.addedMembers.push(newerHead); } else { result.addedMembers = [newerHead]; } return result; } else if (newerName < olderName) { newer.push(newerHead); const result = compareEnumDeclarationMemberArrays(newer, older); if (result.hasOwnProperty("missingMembers") && result.missingMembers) { result.missingMembers.push(olderHead); } else { result.missingMembers = [olderHead]; } return result; } throw new Error("Internal error: should not reach here"); } function compareEnumDeclarationWithMembers(newerDeclaration, olderDeclaration) { const sortedNewerTypes = Array.from(newerDeclaration.members).sort( compareEnumMember, ); const sortedOlderTypes = Array.from(olderDeclaration.members).sort( compareEnumMember, ); const result = compareEnumDeclarationMemberArrays( sortedNewerTypes, sortedOlderTypes, ); if ((0, _ComparisonResult.isMemberLogEmpty)(result)) { return { status: "matching", }; } else if (result.errorMembers) { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Enum types do not match", newerDeclaration, olderDeclaration, (0, _ComparisonResult.memberComparisonError)( result.errorMembers.length > 1 ? "Enum contained members with type mismatches" : "Enum contained a member with a type mismatch", result.errorMembers, ), ), ); } else if ( (result.addedMembers && result.addedMembers.length > 0 && result.addedMembers.length === newerDeclaration.members.length) || (result.missingMembers && result.missingMembers.length > 0 && result.missingMembers.length === olderDeclaration.members.length) ) { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Enum types do not match.", newerDeclaration, olderDeclaration, ), ); } return { status: "members", memberLog: result, }; } function compareNullableChange(newerAnnotation, olderAnnotation) { const newVoidRemoved = newerAnnotation.type === "NullableTypeAnnotation" ? removeNullableTypeAnnotations(newerAnnotation) : newerAnnotation; const oldVoidRemoved = olderAnnotation.type === "NullableTypeAnnotation" ? removeNullableTypeAnnotations(olderAnnotation) : olderAnnotation; const optionalNew = newVoidRemoved.type !== newerAnnotation.type; const optionalOld = oldVoidRemoved.type !== olderAnnotation.type; (0, _invariant.default)( optionalNew !== optionalOld, "compareNullableChange called with both being nullable", ); const optionsReduced = !optionalNew && optionalOld; if ( newVoidRemoved.type === "VoidTypeAnnotation" || oldVoidRemoved.type === "VoidTypeAnnotation" ) { return { status: "nullableChange", nullableLog: { typeRefined: true, optionsReduced, interiorLog: null, newType: newerAnnotation, oldType: olderAnnotation, }, }; } const interiorLog = compareTypeAnnotation(newVoidRemoved, oldVoidRemoved); switch (interiorLog.status) { case "error": return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Type annotations are not the same.", newerAnnotation, olderAnnotation, ), ); case "matching": return { status: "nullableChange", nullableLog: { typeRefined: false, optionsReduced, interiorLog, newType: newerAnnotation, oldType: olderAnnotation, }, }; default: return { status: "nullableChange", nullableLog: { typeRefined: false, optionsReduced, interiorLog, newType: newerAnnotation, oldType: olderAnnotation, }, }; } } function compareUnionTypes(newerType, olderType) { if (newerType.memberType !== olderType.memberType) { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Union member type does not match", newerType, olderType, ), ); } return { status: "matching", }; } function comparePromiseTypes(newerType, olderType) { if (newerType.elementType == null || olderType.elementType == null) { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Promise has differing arguments", newerType, olderType, ), ); } (0, _invariant.default)( newerType.elementType != null && olderType.elementType != null, EQUALITY_MSG, ); return compareTypeAnnotation(newerType.elementType, olderType.elementType); } function compareGenericObjectTypes(newerType, olderType) { if ( newerType.dictionaryValueType == null && olderType.dictionaryValueType == null ) { return { status: "matching", }; } if ( newerType.dictionaryValueType != null && olderType.dictionaryValueType != null ) { return compareTypeAnnotation( newerType.dictionaryValueType, olderType.dictionaryValueType, ); } return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Generic Object types do not have matching dictionary types", newerType, olderType, ), ); } function compareNumberLiteralTypes(newerType, olderType) { return newerType.value === olderType.value ? { status: "matching", } : (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Numeric literals are not equal", newerType, olderType, ), ); } function compareStringLiteralTypes(newerType, olderType) { return newerType.value === olderType.value ? { status: "matching", } : (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "String literals are not equal", newerType, olderType, ), ); } function compareStringLiteralUnionTypes(newerType, olderType) { const results = compareArrayOfTypes( false, false, newerType.types, olderType.types, ); switch (results.status) { case "length-mismatch": throw new Error("length-mismatch returned with length changes allowed"); case "type-mismatch": return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( `Subtype of union at position ${results.newIndex} did not match`, newerType, olderType, results.error, ), ); case "subtypable-changes": if (results.nestedChanges.length > 0) { throw new Error( "Unexpected inline objects/functions in string literal union", ); } if ( results.addedElements.length <= 0 && results.removedElements.length <= 0 ) { throw new Error("string union returned unexpected set of changes"); } const changeLog = { typeKind: "stringUnion", nestedChanges: [], }; if (results.addedElements.length > 0) { changeLog.addedElements = results.addedElements; } if (results.removedElements.length > 0) { changeLog.removedElements = results.removedElements; } return { status: "positionalTypeChange", changeLog, }; case "matching": return { status: "matching", }; default: throw new Error("Unknown status"); } } function compareFunctionTypes(newerType, olderType) { const returnTypeResult = compareTypeAnnotation( newerType.returnTypeAnnotation, olderType.returnTypeAnnotation, ); if (returnTypeResult.status === "error") { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Function return types do not match", newerType, olderType, returnTypeResult.errorLog, ), ); } const functionChanges = {}; if ( returnTypeResult.status === "properties" || returnTypeResult.status === "members" || returnTypeResult.status === "functionChange" || returnTypeResult.status === "positionalTypeChange" || returnTypeResult.status === "nullableChange" ) { functionChanges.returnType = returnTypeResult; } const argumentResults = compareArrayOfTypes( true, true, newerType.params.map((_) => _.typeAnnotation), olderType.params.map((_) => _.typeAnnotation), ); switch (argumentResults.status) { case "length-mismatch": return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Function types have differing length of arguments", newerType, olderType, ), ); case "type-mismatch": return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( `Parameter at index ${argumentResults.newIndex} did not match`, newerType, olderType, argumentResults.error, ), ); case "subtypable-changes": functionChanges.parameterTypes = { typeKind: "parameter", nestedChanges: argumentResults.nestedChanges, }; break; case "matching": default: break; } if ((0, _ComparisonResult.isFunctionLogEmpty)(functionChanges)) { return { status: "matching", }; } return { status: "functionChange", functionChangeLog: functionChanges, }; } function compareArrayOfTypes(fixedOrder, fixedLength, newerTypes, olderTypes) { const sameLength = newerTypes.length === olderTypes.length; if (fixedLength && !sameLength) { return { status: "length-mismatch", }; } const nestedChanges = []; const minLength = Math.min(newerTypes.length, olderTypes.length); if (fixedOrder) { for (let i = 0; i < minLength; i++) { const result = compareTypeAnnotation(newerTypes[i], olderTypes[i]); if (result.status === "error") { return { status: "type-mismatch", error: result.errorLog, newIndex: i, oldIndex: i, }; } if ( result.status === "properties" || result.status === "members" || result.status === "functionChange" || result.status === "positionalTypeChange" || result.status === "nullableChange" ) { nestedChanges.push([i, i, result]); } } if (nestedChanges.length === 0 && sameLength) { return { status: "matching", }; } const addedElements = []; const removedElements = []; if (newerTypes.length < olderTypes.length) { const elements = olderTypes.slice(minLength, olderTypes.length); for (let i = 0; i < elements.length; i++) { removedElements.push([i + minLength + 1, elements[i]]); } } if (newerTypes.length > olderTypes.length) { const elements = newerTypes.slice(minLength, newerTypes.length); for (let i = 0; i < elements.length; i++) { addedElements.push([i + minLength + 1, elements[i]]); } } return { status: "subtypable-changes", nestedChanges, addedElements, removedElements, }; } return compareArrayTypesOutOfOrder( (0, _SortTypeAnnotations.sortTypeAnnotations)(newerTypes), 0, (0, _SortTypeAnnotations.sortTypeAnnotations)(olderTypes), 0, [], [], [], ); } function compareArrayTypesOutOfOrder( newerTypes, newerIndex, olderTypes, olderIndex, potentiallyAddedElements, potentiallyRemovedElements, nestedChanges, ) { const newLength = newerTypes.length; const oldLength = olderTypes.length; if (newerIndex === newLength || olderIndex === oldLength) { const [errors, added, removed] = resolvePotentials( potentiallyAddedElements, potentiallyRemovedElements, ); if (errors.length !== 0) { return { status: "type-mismatch", error: errors[0][0], oldIndex: errors[0][1], newIndex: errors[0][2], }; } if ( added.length === 0 && removed.length === 0 && nestedChanges.length === 0 && newerIndex === newLength && olderIndex === oldLength ) { return { status: "matching", }; } if (newerIndex === newLength && olderIndex === oldLength) { return { status: "subtypable-changes", nestedChanges, addedElements: added, removedElements: removed, }; } if (newerIndex === newLength) { return { status: "subtypable-changes", nestedChanges, addedElements: added, removedElements: removed.concat( olderTypes.slice(olderIndex, oldLength), ), }; } return { status: "subtypable-changes", nestedChanges, addedElements: added.concat(newerTypes.slice(newerIndex, newLength)), removedElements: removed, }; } const newTypePosn = newerTypes[newerIndex][0]; const newType = newerTypes[newerIndex][1]; const oldTypePosn = olderTypes[olderIndex][0]; const oldType = olderTypes[olderIndex][1]; const currentResult = compareTypeAnnotation(newType, oldType); const sortComparison = (0, _SortTypeAnnotations.compareTypeAnnotationForSorting)( newerTypes[newerIndex], olderTypes[olderIndex], ); switch (currentResult.status) { case "matching": return compareArrayTypesOutOfOrder( newerTypes, newerIndex + 1, olderTypes, olderIndex + 1, potentiallyAddedElements, potentiallyRemovedElements, nestedChanges, ); case "properties": case "functionChange": case "positionalTypeChange": case "nullableChange": return compareArrayTypesOutOfOrder( newerTypes, newerIndex + 1, olderTypes, olderIndex + 1, potentiallyAddedElements, potentiallyRemovedElements, nestedChanges.concat[[oldTypePosn, newTypePosn, currentResult]], ); case "error": if (sortComparison === 0) { return { status: "type-mismatch", error: currentResult.errorLog, newIndex: newTypePosn, oldIndex: oldTypePosn, }; } if (sortComparison < 0) { return compareArrayTypesOutOfOrder( newerTypes, newerIndex + 1, olderTypes, olderIndex, potentiallyAddedElements.concat([ { olderPosition: oldTypePosn, newerPosition: newTypePosn, error: currentResult.errorLog, annotation: newType, }, ]), potentiallyRemovedElements, nestedChanges, ); } return compareArrayTypesOutOfOrder( newerTypes, newerIndex, olderTypes, olderIndex + 1, potentiallyAddedElements, potentiallyRemovedElements.concat([ { olderPosition: oldTypePosn, newerPosition: newTypePosn, error: currentResult.errorLog, annotation: oldType, }, ]), nestedChanges, ); case "skipped": throw new Error( "Unexpected skipped status for array of type annotations", ); default: throw new Error("Unsupported status " + currentResult.status); } } function resolvePotentials(potentiallyAdded, potentiallyRemoved) { const addedLength = potentiallyAdded.length; const removedLength = potentiallyRemoved.length; if (addedLength === 0 && removedLength === 0) { return [[], [], []]; } if (addedLength === 0) { return [ [], [], potentiallyRemoved.map((removed) => [ removed.olderPosition, removed.annotation, ]), ]; } if (removedLength === 0) { return [ [], potentiallyAdded.map((added) => [added.newerPosition, added.annotation]), [], ]; } const addedHead = potentiallyAdded[0]; const removedHead = potentiallyRemoved[0]; if (addedHead.olderPosition === removedHead.olderPosition) { return [ [[addedHead.error, addedHead.olderPosition, addedHead.newerPosition]], [], [], ]; } if (removedHead.newerPosition === addedHead.newerPosition) { return [ [ [ removedHead.error, removedHead.olderPosition, removedHead.newerPosition, ], ], [], [], ]; } const sortedOrder = (0, _SortTypeAnnotations.compareTypeAnnotationForSorting)( [addedHead.newerPosition, addedHead.annotation], [removedHead.olderPosition, removedHead.annotation], ); if (sortedOrder === 0) { const [errors, added, removed] = resolvePotentials( potentiallyAdded.slice(1, addedLength), potentiallyRemoved.slice(1, removedLength), ); return [ errors, added.concat([[addedHead.newerPosition, addedHead.annotation]]), removed.concat([[removedHead.olderPosition, removedHead.annotation]]), ]; } if (sortedOrder < 0) { const [errors, added, removed] = resolvePotentials( potentiallyAdded.slice(1, addedLength), potentiallyRemoved, ); return [ errors, added.concat([[addedHead.newerPosition, addedHead.annotation]]), removed, ]; } const [errors, added, removed] = resolvePotentials( potentiallyAdded, potentiallyRemoved.slice(1, removedLength), ); return [ errors, added, removed.concat([[removedHead.olderPosition, removedHead.annotation]]), ]; } function compareEventEmitterTypes(newerAnnotation, olderAnnotation) { const comparison = compareTypeAnnotation( newerAnnotation.typeAnnotation, olderAnnotation.typeAnnotation, ); if (comparison.status === "error") { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "EventEmitter eventTypes are not equivalent", newerAnnotation, olderAnnotation, comparison.errorLog, ), ); } return comparison; } function compareReservedTypeAnnotation(newerAnnotation, olderAnnotation) { if (newerAnnotation.name !== olderAnnotation.name) { return (0, _ComparisonResult.makeError)( (0, _ComparisonResult.typeAnnotationComparisonError)( "Types are not equivalent", newerAnnotation, olderAnnotation, ), ); } switch (newerAnnotation.name) { case "RootTag": case "ColorPrimitive": case "ImageSourcePrimitive": case "PointPrimitive": case "EdgeInsetsPrimitive": case "ImageRequestPrimitive": case "DimensionPrimitive": return { status: "matching", }; default: newerAnnotation.name; throw new Error("Unknown reserved type " + newerAnnotation.name); } }