mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
60e3921f9c
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49340 This tool enables checking the boundary between JavaScript and Native for backwards incompatible changes to protect against crashes. This is useful for: - Local Development - Over the Air updates on platforms that support it - Theoretically: Server Components with React Native Check out the Readme for more information Changelog: [General][Added] Open Sourcing React Native's Compatibility Check Reviewed By: panagosg7 Differential Revision: D69476742 fbshipit-source-id: 8af6039839c5475c1258fa82d9750a9320cf0751
208 lines
5.9 KiB
JavaScript
208 lines
5.9 KiB
JavaScript
/**
|
|
* 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.
|
|
*
|
|
* @flow strict-local
|
|
* @format
|
|
*/
|
|
|
|
import type {
|
|
PropertiesComparisonResult,
|
|
TypeComparisonError,
|
|
} from './ComparisonResult';
|
|
import type {CompleteTypeAnnotation} from '@react-native/codegen/src/CodegenSchema';
|
|
|
|
export type ErrorCode =
|
|
| 'addedProps'
|
|
| 'removedProps'
|
|
| 'changedParams'
|
|
| 'incompatibleTypes'
|
|
| 'requiredProps'
|
|
| 'optionalProps'
|
|
| 'nonNullableOfNull'
|
|
| 'nullableOfNonNull'
|
|
| 'removedUnionCases'
|
|
| 'addedUnionCases'
|
|
| 'addedEnumCases'
|
|
| 'removedEnumCases'
|
|
| 'removedIntersectCases'
|
|
| 'addedIntersectCases'
|
|
| 'removedModule'
|
|
| 'removedComponent';
|
|
|
|
export type TypeStore = {
|
|
typeName: string,
|
|
typeInformation: CompleteTypeAnnotation,
|
|
...
|
|
};
|
|
// Stores object properties that are added or removed which could be patchable
|
|
export type ObjectTypeChangeStore = {
|
|
typeName: string,
|
|
newType: CompleteTypeAnnotation,
|
|
oldType: CompleteTypeAnnotation,
|
|
propertyChange: PropertiesComparisonResult,
|
|
};
|
|
export type ErrorStore = {
|
|
typeName: string,
|
|
errorCode: ErrorCode,
|
|
errorInformation: TypeComparisonError,
|
|
};
|
|
export type FormattedErrorStore = {
|
|
message: string,
|
|
errorCode: ErrorCode,
|
|
};
|
|
export type NativeSpecErrorStore = {
|
|
nativeSpecName: string,
|
|
omitted: boolean,
|
|
errorCode: ErrorCode,
|
|
errorInformation?: TypeComparisonError,
|
|
changeInformation?: DiffSet,
|
|
};
|
|
export type ExportableNativeSpecErrorStore = {
|
|
nativeSpecName: string,
|
|
omitted: boolean,
|
|
errorCode: ErrorCode,
|
|
errorInformation?: TypeComparisonError,
|
|
changeInformation?: ExportableDiffSet,
|
|
};
|
|
export type DiffSet = {
|
|
newTypes: Set<TypeStore>,
|
|
deprecatedTypes: Set<TypeStore>,
|
|
objectTypeChanges: Set<ObjectTypeChangeStore>,
|
|
incompatibleChanges: Set<ErrorStore>,
|
|
};
|
|
type ExportableDiffSet = {
|
|
newTypes: Array<TypeStore>,
|
|
deprecatedTypes: Array<TypeStore>,
|
|
objectTypeChanges: Array<ObjectTypeChangeStore>,
|
|
incompatibleChanges: Array<ErrorStore>,
|
|
};
|
|
|
|
export type Framework = 'ReactNative';
|
|
export type SchemaDiffers = {
|
|
incompatibleSpecs: ?Set<NativeSpecErrorStore>,
|
|
};
|
|
type ExportableSchemaDiffers = {
|
|
incompatibleSpecs: ?Array<ExportableNativeSpecErrorStore>,
|
|
};
|
|
export type SchemaDiffCategory = 'new' | 'deprecated' | SchemaDiffers;
|
|
type ExportableSchemaDiffCategory =
|
|
| 'new'
|
|
| 'deprecated'
|
|
| ExportableSchemaDiffers;
|
|
export type SchemaDiff = {
|
|
name: string,
|
|
framework: Framework,
|
|
status: SchemaDiffCategory,
|
|
};
|
|
export type ExportableSchemaDiff = {
|
|
name: string,
|
|
framework: Framework,
|
|
status: ExportableSchemaDiffCategory,
|
|
};
|
|
|
|
export type Version = {
|
|
device: 'android' | 'ios',
|
|
number: string,
|
|
...
|
|
};
|
|
|
|
export type Incompatible = {
|
|
framework: Framework,
|
|
incompatibleSpecs?: Array<NativeSpecErrorStore>,
|
|
};
|
|
export type IncompatiblityReport = {
|
|
[hasteModuleName: string]: Incompatible,
|
|
};
|
|
export type DiffSummary = {
|
|
/** status records how the diff compares against older versions
|
|
* ok: there are no changes that impact older versions
|
|
* patchable: there are additions (or modifications) that are not suported
|
|
* in older versions but is safe with an auto-generated patch
|
|
* incompatible: there are modifications that are not safe for use with older
|
|
* versions and are not fixable with an auto-generated patchable
|
|
*/
|
|
status: 'ok' | 'patchable' | 'incompatible',
|
|
// If there are incompatible changes, provide a record of them, otherwise {}
|
|
incompatibilityReport: IncompatiblityReport,
|
|
};
|
|
|
|
export type FormattedIncompatible = {
|
|
framework: Framework,
|
|
incompatibleSpecs?: Array<FormattedErrorStore>,
|
|
};
|
|
|
|
export type FormattedIncompatiblityReport = {
|
|
[hasteModuleName: string]: FormattedIncompatible,
|
|
};
|
|
|
|
export type FormattedDiffSummary = {
|
|
/** status records how the diff compares against older versions
|
|
* ok: there are no changes that impact older versions
|
|
* patchable: there are additions (or modifications) that are not suported
|
|
* in older versions but is safe with an auto-generated patch
|
|
* incompatible: there are modifications that are not safe for use with older
|
|
* versions and are not fixable with an auto-generated patchable
|
|
*/
|
|
status: 'ok' | 'patchable' | 'incompatible',
|
|
// If there are incompatible changes, provide a record of them, otherwise {}
|
|
incompatibilityReport: FormattedIncompatiblityReport,
|
|
};
|
|
|
|
function diffSetExporter(diffSet: DiffSet): ExportableDiffSet {
|
|
return {
|
|
newTypes: Array.from(diffSet.newTypes),
|
|
deprecatedTypes: Array.from(diffSet.deprecatedTypes),
|
|
objectTypeChanges: Array.from(diffSet.objectTypeChanges),
|
|
incompatibleChanges: Array.from(diffSet.incompatibleChanges),
|
|
};
|
|
}
|
|
|
|
export function nativeSpecErrorExporter(
|
|
nativeSpecError: NativeSpecErrorStore,
|
|
): ExportableNativeSpecErrorStore {
|
|
if (nativeSpecError.changeInformation) {
|
|
return {
|
|
nativeSpecName: nativeSpecError.nativeSpecName,
|
|
omitted: nativeSpecError.omitted,
|
|
errorCode: nativeSpecError.errorCode,
|
|
errorInformation: nativeSpecError.errorInformation,
|
|
changeInformation: diffSetExporter(nativeSpecError.changeInformation),
|
|
};
|
|
}
|
|
return {
|
|
nativeSpecName: nativeSpecError.nativeSpecName,
|
|
omitted: nativeSpecError.omitted,
|
|
errorCode: nativeSpecError.errorCode,
|
|
errorInformation: nativeSpecError.errorInformation,
|
|
};
|
|
}
|
|
|
|
function schemaDiffCategoryExporter(
|
|
status: SchemaDiffCategory,
|
|
): ExportableSchemaDiffCategory {
|
|
switch (status) {
|
|
case 'new':
|
|
case 'deprecated':
|
|
return status;
|
|
default:
|
|
return {
|
|
incompatibleSpecs: status.incompatibleSpecs
|
|
? Array.from(status.incompatibleSpecs).map(nativeSpecErrorExporter)
|
|
: undefined,
|
|
};
|
|
}
|
|
}
|
|
|
|
export function schemaDiffExporter(
|
|
schemaDiff: SchemaDiff,
|
|
): ExportableSchemaDiff {
|
|
return {
|
|
name: schemaDiff.name,
|
|
framework: schemaDiff.framework,
|
|
status: schemaDiffCategoryExporter(schemaDiff.status),
|
|
};
|
|
}
|