forked from facebook/react-native
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathverifyComponentAttributeEquivalence.js
More file actions
134 lines (117 loc) · 3.92 KB
/
verifyComponentAttributeEquivalence.js
File metadata and controls
134 lines (117 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const getNativeComponentAttributes = require('../ReactNative/getNativeComponentAttributes');
import ReactNativeViewViewConfig from '../Components/View/ReactNativeViewViewConfig';
import type {ReactNativeBaseComponentViewConfig} from '../Renderer/shims/ReactNativeTypes';
const IGNORED_KEYS = ['transform', 'hitSlop'];
/**
* The purpose of this function is to validate that the view config that
* native exposes for a given view manager is the same as the view config
* that is specified for that view manager in JS.
*
* In order to improve perf, we want to avoid calling into native to get
* the view config when each view manager is used. To do this, we are moving
* the configs to JS. In the future we will use these JS based view configs
* to codegen the view manager on native to ensure they stay in sync without
* this runtime check.
*
* If this function fails, that likely means a change was made to the native
* view manager without updating the JS config as well. Ideally you can make
* that direct change to the JS config. If you don't know what the differences
* are, the best approach I've found is to create a view that prints
* the return value of getNativeComponentAttributes, and then copying that
* text and pasting it back into JS:
* <Text selectable={true}>{JSON.stringify(getNativeComponentAttributes('RCTView'))}</Text>
*
* This is meant to be a stopgap until the time comes when we only have a
* single source of truth. I wonder if this message will still be here two
* years from now...
*/
function verifyComponentAttributeEquivalence(
componentName: string,
config: ReactNativeBaseComponentViewConfig<>,
) {
if (!global.RN$Bridgeless) {
const nativeAttributes = getNativeComponentAttributes(componentName);
['validAttributes', 'bubblingEventTypes', 'directEventTypes'].forEach(
prop => {
const diffKeys = Object.keys(
lefthandObjectDiff(nativeAttributes[prop], config[prop]),
);
if (diffKeys.length) {
console.error(
`${componentName} generated view config for ${prop} does not match native, missing: ${diffKeys.join(
' ',
)}`,
);
}
},
);
}
}
export function lefthandObjectDiff(leftObj: Object, rightObj: Object): Object {
const differentKeys = {};
function compare(leftItem, rightItem, key) {
if (typeof leftItem !== typeof rightItem && leftItem != null) {
differentKeys[key] = rightItem;
return;
}
if (typeof leftItem === 'object') {
const objDiff = lefthandObjectDiff(leftItem, rightItem);
if (Object.keys(objDiff).length > 1) {
differentKeys[key] = objDiff;
}
return;
}
if (leftItem !== rightItem) {
differentKeys[key] = rightItem;
return;
}
}
for (const key in leftObj) {
if (IGNORED_KEYS.includes(key)) {
continue;
}
if (!rightObj) {
differentKeys[key] = {};
} else if (leftObj.hasOwnProperty(key)) {
compare(leftObj[key], rightObj[key], key);
}
}
return differentKeys;
}
export function getConfigWithoutViewProps(
viewConfig: ReactNativeBaseComponentViewConfig<>,
propName: string,
): {...} {
if (!viewConfig[propName]) {
return {};
}
return Object.keys(viewConfig[propName])
.filter(prop => !ReactNativeViewViewConfig[propName][prop])
.reduce((obj, prop) => {
obj[prop] = viewConfig[propName][prop];
return obj;
}, {});
}
export function stringifyViewConfig(viewConfig: any): string {
return JSON.stringify(
viewConfig,
(key, val) => {
if (typeof val === 'function') {
return `ƒ ${val.name}`;
}
return val;
},
2,
);
}
export default verifyComponentAttributeEquivalence;