amis-rpc-design/node_modules/react-native/Libraries/NativeComponent/StaticViewConfigValidator.js

164 lines
3.9 KiB
JavaScript
Raw Normal View History

2023-10-07 19:42:30 +08:00
/**
* 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
* @format
*/
import {type ViewConfig} from '../Renderer/shims/ReactNativeTypes';
import {isIgnored} from './ViewConfigIgnore';
export type Difference =
| {
type: 'missing',
path: Array<string>,
nativeValue: mixed,
}
| {
type: 'unequal',
path: Array<string>,
nativeValue: mixed,
staticValue: mixed,
}
| {
type: 'unexpected',
path: Array<string>,
staticValue: mixed,
};
export type ValidationResult = ValidResult | InvalidResult;
type ValidResult = {
type: 'valid',
};
type InvalidResult = {
type: 'invalid',
differences: Array<Difference>,
};
/**
* During the migration from native view configs to static view configs, this is
* used to validate that the two are equivalent.
*/
export function validate(
name: string,
nativeViewConfig: ViewConfig,
staticViewConfig: ViewConfig,
): ValidationResult {
const differences: Array<Difference> = [];
accumulateDifferences(
differences,
[],
{
bubblingEventTypes: nativeViewConfig.bubblingEventTypes,
directEventTypes: nativeViewConfig.directEventTypes,
uiViewClassName: nativeViewConfig.uiViewClassName,
validAttributes: nativeViewConfig.validAttributes,
},
{
bubblingEventTypes: staticViewConfig.bubblingEventTypes,
directEventTypes: staticViewConfig.directEventTypes,
uiViewClassName: staticViewConfig.uiViewClassName,
validAttributes: staticViewConfig.validAttributes,
},
);
if (differences.length === 0) {
return {type: 'valid'};
}
return {
type: 'invalid',
differences,
};
}
export function stringifyValidationResult(
name: string,
validationResult: InvalidResult,
): string {
const {differences} = validationResult;
return [
`StaticViewConfigValidator: Invalid static view config for '${name}'.`,
'',
...differences.map(difference => {
const {type, path} = difference;
switch (type) {
case 'missing':
return `- '${path.join('.')}' is missing.`;
case 'unequal':
return `- '${path.join('.')}' is the wrong value.`;
case 'unexpected':
return `- '${path.join('.')}' is present but not expected to be.`;
}
}),
'',
].join('\n');
}
function accumulateDifferences(
differences: Array<Difference>,
path: Array<string>,
nativeObject: {...},
staticObject: {...},
): void {
for (const nativeKey in nativeObject) {
const nativeValue = nativeObject[nativeKey];
if (!staticObject.hasOwnProperty(nativeKey)) {
differences.push({
path: [...path, nativeKey],
type: 'missing',
nativeValue,
});
continue;
}
const staticValue = staticObject[nativeKey];
const nativeValueIfObject = ifObject(nativeValue);
if (nativeValueIfObject != null) {
const staticValueIfObject = ifObject(staticValue);
if (staticValueIfObject != null) {
path.push(nativeKey);
accumulateDifferences(
differences,
path,
nativeValueIfObject,
staticValueIfObject,
);
path.pop();
continue;
}
}
if (nativeValue !== staticValue) {
differences.push({
path: [...path, nativeKey],
type: 'unequal',
nativeValue,
staticValue,
});
}
}
for (const staticKey in staticObject) {
if (
!nativeObject.hasOwnProperty(staticKey) &&
!isIgnored(staticObject[staticKey])
) {
differences.push({
path: [...path, staticKey],
type: 'unexpected',
staticValue: staticObject[staticKey],
});
}
}
}
function ifObject(value: mixed): ?{...} {
return typeof value === 'object' && !Array.isArray(value) ? value : null;
}