react-native-onyx 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +187 -0
- package/LICENSE.md +21 -0
- package/README.md +258 -0
- package/dist/web.development.js +3289 -0
- package/dist/web.development.js.map +1 -0
- package/dist/web.min.js +2 -0
- package/dist/web.min.js.map +1 -0
- package/lib/Logger.js +35 -0
- package/lib/MDTable.js +69 -0
- package/lib/Onyx.js +901 -0
- package/lib/OnyxCache.js +195 -0
- package/lib/SyncQueue.js +51 -0
- package/lib/compose.js +29 -0
- package/lib/createDeferredTask.js +16 -0
- package/lib/index.js +5 -0
- package/lib/metrics/index.native.js +263 -0
- package/lib/metrics/index.web.js +13 -0
- package/lib/storage/NativeStorage.js +3 -0
- package/lib/storage/WebStorage.js +56 -0
- package/lib/storage/__mocks__/index.native.js +8 -0
- package/lib/storage/index.native.js +8 -0
- package/lib/storage/index.web.js +3 -0
- package/lib/storage/providers/AsyncStorage.js +83 -0
- package/lib/storage/providers/LocalForage.js +111 -0
- package/lib/withOnyx.js +200 -0
- package/native.js +11 -0
- package/package.json +109 -0
- package/web.js +12 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AsyncStorage provider stores everything in a key/value store by
|
|
3
|
+
* converting the value to a JSON string
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import _ from 'underscore';
|
|
7
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
|
+
|
|
9
|
+
const provider = {
|
|
10
|
+
/**
|
|
11
|
+
* Get the value of a given key or return `null` if it's not available in storage
|
|
12
|
+
* @param {String} key
|
|
13
|
+
* @return {Promise<*>}
|
|
14
|
+
*/
|
|
15
|
+
getItem(key) {
|
|
16
|
+
return AsyncStorage.getItem(key)
|
|
17
|
+
.then((value) => {
|
|
18
|
+
const parsed = value && JSON.parse(value);
|
|
19
|
+
return parsed;
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get multiple key-value pairs for the give array of keys in a batch
|
|
25
|
+
* @param {String[]} keys
|
|
26
|
+
* @return {Promise<Array<[key, value]>>}
|
|
27
|
+
*/
|
|
28
|
+
multiGet(keys) {
|
|
29
|
+
return AsyncStorage.multiGet(keys)
|
|
30
|
+
.then(pairs => _.map(pairs, ([key, value]) => [key, value && JSON.parse(value)]));
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Sets the value for a given key. The only requirement is that the value should be serializable to JSON string
|
|
35
|
+
* @param {String} key
|
|
36
|
+
* @param {*} value
|
|
37
|
+
* @return {Promise<void>}
|
|
38
|
+
*/
|
|
39
|
+
setItem(key, value) {
|
|
40
|
+
return AsyncStorage.setItem(key, JSON.stringify(value));
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Stores multiple key-value pairs in a batch
|
|
45
|
+
* @param {Array<[key, value]>} pairs
|
|
46
|
+
* @return {Promise<void>}
|
|
47
|
+
*/
|
|
48
|
+
multiSet(pairs) {
|
|
49
|
+
const stringPairs = _.map(pairs, ([key, value]) => [key, JSON.stringify(value)]);
|
|
50
|
+
return AsyncStorage.multiSet(stringPairs);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Multiple merging of existing and new values in a batch
|
|
55
|
+
* @param {Array<[key, value]>} pairs
|
|
56
|
+
* @return {Promise<void>}
|
|
57
|
+
*/
|
|
58
|
+
multiMerge(pairs) {
|
|
59
|
+
const stringPairs = _.map(pairs, ([key, value]) => [key, JSON.stringify(value)]);
|
|
60
|
+
return AsyncStorage.multiMerge(stringPairs);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Returns all keys available in storage
|
|
65
|
+
* @returns {Promise<String[]>}
|
|
66
|
+
*/
|
|
67
|
+
getAllKeys: AsyncStorage.getAllKeys,
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Remove given key and it's value from storage
|
|
71
|
+
* @param {String} key
|
|
72
|
+
* @returns {Promise<void>}
|
|
73
|
+
*/
|
|
74
|
+
removeItem: AsyncStorage.removeItem,
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Clear absolutely everything from storage
|
|
78
|
+
* @returns {Promise<void>}
|
|
79
|
+
*/
|
|
80
|
+
clear: AsyncStorage.clear,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default provider;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file
|
|
3
|
+
* The storage provider based on localforage allows us to store most anything in its
|
|
4
|
+
* natural form in the underlying DB without having to stringify or de-stringify it
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import localforage from 'localforage';
|
|
8
|
+
import _ from 'underscore';
|
|
9
|
+
import lodashMerge from 'lodash/merge';
|
|
10
|
+
import SyncQueue from '../../SyncQueue';
|
|
11
|
+
|
|
12
|
+
localforage.config({
|
|
13
|
+
name: 'OnyxDB'
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const provider = {
|
|
17
|
+
/**
|
|
18
|
+
* Writing very quickly to IndexedDB causes performance issues and can lock up the page and lead to jank.
|
|
19
|
+
* So, we are slowing this process down by waiting until one write is complete before moving on
|
|
20
|
+
* to the next.
|
|
21
|
+
*/
|
|
22
|
+
setItemQueue: new SyncQueue(({key, value, shouldMerge}) => {
|
|
23
|
+
if (shouldMerge) {
|
|
24
|
+
return localforage.getItem(key)
|
|
25
|
+
.then((existingValue) => {
|
|
26
|
+
const newValue = _.isObject(existingValue)
|
|
27
|
+
? lodashMerge({}, existingValue, value)
|
|
28
|
+
: value;
|
|
29
|
+
return localforage.setItem(key, newValue);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return localforage.setItem(key, value);
|
|
34
|
+
}),
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get multiple key-value pairs for the give array of keys in a batch
|
|
38
|
+
* @param {String[]} keys
|
|
39
|
+
* @return {Promise<Array<[key, value]>>}
|
|
40
|
+
*/
|
|
41
|
+
multiGet(keys) {
|
|
42
|
+
const pairs = _.map(
|
|
43
|
+
keys,
|
|
44
|
+
key => localforage.getItem(key)
|
|
45
|
+
.then(value => [key, value])
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return Promise.all(pairs);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Multiple merging of existing and new values in a batch
|
|
53
|
+
* @param {Array<[key, value]>} pairs
|
|
54
|
+
* @return {Promise<void>}
|
|
55
|
+
*/
|
|
56
|
+
multiMerge(pairs) {
|
|
57
|
+
const tasks = _.map(pairs, ([key, value]) => this.setItemQueue.push({key, value, shouldMerge: true}));
|
|
58
|
+
|
|
59
|
+
// We're returning Promise.resolve, otherwise the array of task results will be returned to the caller
|
|
60
|
+
return Promise.all(tasks).then(() => Promise.resolve());
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Stores multiple key-value pairs in a batch
|
|
65
|
+
* @param {Array<[key, value]>} pairs
|
|
66
|
+
* @return {Promise<void>}
|
|
67
|
+
*/
|
|
68
|
+
multiSet(pairs) {
|
|
69
|
+
// We're returning Promise.resolve, otherwise the array of task results will be returned to the caller
|
|
70
|
+
const tasks = _.map(pairs, ([key, value]) => this.setItem(key, value));
|
|
71
|
+
return Promise.all(tasks).then(() => Promise.resolve());
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Clear absolutely everything from storage
|
|
76
|
+
* @returns {Promise<void>}
|
|
77
|
+
*/
|
|
78
|
+
clear: localforage.clear,
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Returns all keys available in storage
|
|
82
|
+
* @returns {Promise<String[]>}
|
|
83
|
+
*/
|
|
84
|
+
getAllKeys: localforage.keys,
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get the value of a given key or return `null` if it's not available in storage
|
|
88
|
+
* @param {String} key
|
|
89
|
+
* @return {Promise<*>}
|
|
90
|
+
*/
|
|
91
|
+
getItem: localforage.getItem,
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Remove given key and it's value from storage
|
|
95
|
+
* @param {String} key
|
|
96
|
+
* @returns {Promise<void>}
|
|
97
|
+
*/
|
|
98
|
+
removeItem: localforage.removeItem,
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Sets the value for a given key. The only requirement is that the value should be serializable to JSON string
|
|
102
|
+
* @param {String} key
|
|
103
|
+
* @param {*} value
|
|
104
|
+
* @return {Promise<void>}
|
|
105
|
+
*/
|
|
106
|
+
setItem(key, value) {
|
|
107
|
+
return this.setItemQueue.push({key, value});
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default provider;
|
package/lib/withOnyx.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is a higher order component that provides the ability to map a state property directly to
|
|
3
|
+
* something in Onyx (a key/value store). That way, as soon as data in Onyx changes, the state will be set and the view
|
|
4
|
+
* will automatically change to reflect the new data.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import _ from 'underscore';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
import Str from 'expensify-common/lib/str';
|
|
10
|
+
import Onyx from './Onyx';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns the display name of a component
|
|
14
|
+
*
|
|
15
|
+
* @param {object} component
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
function getDisplayName(component) {
|
|
19
|
+
return component.displayName || component.name || 'Component';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function (mapOnyxToState) {
|
|
23
|
+
// A list of keys that must be present in tempState before we can render the WrappedComponent
|
|
24
|
+
const requiredKeysForInit = _.chain(mapOnyxToState)
|
|
25
|
+
.omit(config => config.initWithStoredValues === false)
|
|
26
|
+
.keys()
|
|
27
|
+
.value();
|
|
28
|
+
|
|
29
|
+
return (WrappedComponent) => {
|
|
30
|
+
class withOnyx extends React.Component {
|
|
31
|
+
constructor(props) {
|
|
32
|
+
super(props);
|
|
33
|
+
|
|
34
|
+
this.setWithOnyxState = this.setWithOnyxState.bind(this);
|
|
35
|
+
|
|
36
|
+
// This stores all the Onyx connection IDs to be used when the component unmounts so everything can be
|
|
37
|
+
// disconnected. It is a key value store with the format {[mapping.key]: connectionID}.
|
|
38
|
+
this.activeConnectionIDs = {};
|
|
39
|
+
|
|
40
|
+
// Object holding the temporary initial state for the component while we load the various Onyx keys
|
|
41
|
+
this.tempState = {};
|
|
42
|
+
|
|
43
|
+
this.state = {
|
|
44
|
+
// If there are no required keys for init then we can render the wrapped component immediately
|
|
45
|
+
loading: requiredKeysForInit.length > 0,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
componentDidMount() {
|
|
50
|
+
// Subscribe each of the state properties to the proper Onyx key
|
|
51
|
+
_.each(mapOnyxToState, (mapping, propertyName) => {
|
|
52
|
+
this.connectMappingToOnyx(mapping, propertyName);
|
|
53
|
+
});
|
|
54
|
+
this.checkEvictableKeys();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
componentDidUpdate(prevProps) {
|
|
58
|
+
// If any of the mappings use data from the props, then when the props change, all the
|
|
59
|
+
// connections need to be reconnected with the new props
|
|
60
|
+
_.each(mapOnyxToState, (mapping, propertyName) => {
|
|
61
|
+
const previousKey = Str.result(mapping.key, prevProps);
|
|
62
|
+
const newKey = Str.result(mapping.key, this.props);
|
|
63
|
+
|
|
64
|
+
if (previousKey !== newKey) {
|
|
65
|
+
Onyx.disconnect(this.activeConnectionIDs[previousKey], previousKey);
|
|
66
|
+
delete this.activeConnectionIDs[previousKey];
|
|
67
|
+
this.connectMappingToOnyx(mapping, propertyName);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
this.checkEvictableKeys();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
componentWillUnmount() {
|
|
74
|
+
// Disconnect everything from Onyx
|
|
75
|
+
_.each(mapOnyxToState, (mapping) => {
|
|
76
|
+
const key = Str.result(mapping.key, this.props);
|
|
77
|
+
const connectionID = this.activeConnectionIDs[key];
|
|
78
|
+
Onyx.disconnect(connectionID, key);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* This method is used externally by sendDataToConnection to prevent unnecessary renders while a component
|
|
84
|
+
* still in a loading state. The temporary initial state is saved to the component instance and setState()
|
|
85
|
+
* only called once all the necessary data has been collected.
|
|
86
|
+
*
|
|
87
|
+
* @param {String} statePropertyName
|
|
88
|
+
* @param {*} val
|
|
89
|
+
*/
|
|
90
|
+
setWithOnyxState(statePropertyName, val) {
|
|
91
|
+
if (!this.state.loading) {
|
|
92
|
+
this.setState({[statePropertyName]: val});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.tempState[statePropertyName] = val;
|
|
97
|
+
|
|
98
|
+
// All state keys should exist and at least have a value of null
|
|
99
|
+
if (_.some(requiredKeysForInit, key => _.isUndefined(this.tempState[key]))) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.setState({...this.tempState, loading: false});
|
|
104
|
+
delete this.tempState;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Makes sure each Onyx key we requested has been set to state with a value of some kind.
|
|
109
|
+
* We are doing this so that the wrapped component will only render when all the data
|
|
110
|
+
* it needs is available to it.
|
|
111
|
+
*/
|
|
112
|
+
checkEvictableKeys() {
|
|
113
|
+
// We will add this key to our list of recently accessed keys
|
|
114
|
+
// if the canEvict function returns true. This is necessary criteria
|
|
115
|
+
// we MUST use to specify if a key can be removed or not.
|
|
116
|
+
_.each(mapOnyxToState, (mapping) => {
|
|
117
|
+
if (_.isUndefined(mapping.canEvict)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const canEvict = Str.result(mapping.canEvict, this.props);
|
|
122
|
+
const key = Str.result(mapping.key, this.props);
|
|
123
|
+
|
|
124
|
+
if (!Onyx.isSafeEvictionKey(key)) {
|
|
125
|
+
// eslint-disable-next-line max-len
|
|
126
|
+
throw new Error(`canEvict cannot be used on key '${key}'. This key must explicitly be flagged as safe for removal by adding it to Onyx.init({safeEvictionKeys: []}).`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (canEvict) {
|
|
130
|
+
Onyx.removeFromEvictionBlockList(key, mapping.connectionID);
|
|
131
|
+
} else {
|
|
132
|
+
Onyx.addToEvictionBlockList(key, mapping.connectionID);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Takes a single mapping and binds the state of the component to the store
|
|
139
|
+
*
|
|
140
|
+
* @param {object} mapping
|
|
141
|
+
* @param {string|function} mapping.key key to connect to. can be a string or a
|
|
142
|
+
* function that takes this.props as an argument and returns a string
|
|
143
|
+
* @param {string} statePropertyName the name of the state property that Onyx will add the data to
|
|
144
|
+
* @param {boolean} [mapping.initWithStoredValues] If set to false, then no data will be prefilled into the
|
|
145
|
+
* component
|
|
146
|
+
*/
|
|
147
|
+
connectMappingToOnyx(mapping, statePropertyName) {
|
|
148
|
+
const key = Str.result(mapping.key, this.props);
|
|
149
|
+
|
|
150
|
+
this.activeConnectionIDs[key] = Onyx.connect({
|
|
151
|
+
...mapping,
|
|
152
|
+
key,
|
|
153
|
+
statePropertyName,
|
|
154
|
+
withOnyxInstance: this,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
render() {
|
|
159
|
+
if (this.state.loading) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Remove any internal state properties used by withOnyx
|
|
164
|
+
// that should not be passed to a wrapped component
|
|
165
|
+
let stateToPass = _.omit(this.state, 'loading');
|
|
166
|
+
stateToPass = _.omit(stateToPass, value => _.isNull(value));
|
|
167
|
+
|
|
168
|
+
// Remove any null values so that React replaces them with default props
|
|
169
|
+
const propsToPass = _.omit(this.props, value => _.isNull(value));
|
|
170
|
+
|
|
171
|
+
// Spreading props and state is necessary in an HOC where the data cannot be predicted
|
|
172
|
+
return (
|
|
173
|
+
<WrappedComponent
|
|
174
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
175
|
+
{...propsToPass}
|
|
176
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
177
|
+
{...stateToPass}
|
|
178
|
+
ref={this.props.forwardedRef}
|
|
179
|
+
/>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
withOnyx.propTypes = {
|
|
185
|
+
forwardedRef: PropTypes.oneOfType([
|
|
186
|
+
PropTypes.func,
|
|
187
|
+
PropTypes.shape({current: PropTypes.instanceOf(React.Component)}),
|
|
188
|
+
]),
|
|
189
|
+
};
|
|
190
|
+
withOnyx.defaultProps = {
|
|
191
|
+
forwardedRef: undefined,
|
|
192
|
+
};
|
|
193
|
+
withOnyx.displayName = `withOnyx(${getDisplayName(WrappedComponent)})`;
|
|
194
|
+
return React.forwardRef((props, ref) => {
|
|
195
|
+
const Component = withOnyx;
|
|
196
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
197
|
+
return <Component {...props} forwardedRef={ref} />;
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
}
|
package/native.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file
|
|
3
|
+
* Native entry point for Onyx
|
|
4
|
+
* This file is resolved by react-native projects
|
|
5
|
+
*/
|
|
6
|
+
import Onyx from './lib';
|
|
7
|
+
|
|
8
|
+
// We resolve pure source for react-native projects and let `metro` bundle it
|
|
9
|
+
// We can test small changes directly from the parent project `node_modules/react-native-onyx` source
|
|
10
|
+
export * from './lib';
|
|
11
|
+
export default Onyx;
|
package/package.json
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-onyx",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"author": "Expensify, Inc.",
|
|
5
|
+
"homepage": "https://expensify.com",
|
|
6
|
+
"description": "State management for React Native",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"private": false,
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/**/*",
|
|
11
|
+
"lib/**/*",
|
|
12
|
+
"native.js",
|
|
13
|
+
"web.js",
|
|
14
|
+
"API.md",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE.md"
|
|
17
|
+
],
|
|
18
|
+
"react-native": "native.js",
|
|
19
|
+
"main": "native.js",
|
|
20
|
+
"browser": "web.js",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"lint-tests": "eslint tests/**",
|
|
24
|
+
"test": "jest",
|
|
25
|
+
"build:web": "webpack --config webpack.config.js",
|
|
26
|
+
"build:docs": "node buildDocs.js"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"ascii-table": "0.0.9",
|
|
30
|
+
"lodash": "^4.17.21",
|
|
31
|
+
"underscore": "^1.13.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@babel/core": "^7.17.10",
|
|
35
|
+
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
|
36
|
+
"@babel/runtime": "^7.11.2",
|
|
37
|
+
"@react-native-async-storage/async-storage": "^1.15.5",
|
|
38
|
+
"@react-native-community/eslint-config": "^2.0.0",
|
|
39
|
+
"@testing-library/jest-native": "^3.4.2",
|
|
40
|
+
"@testing-library/react-native": "^7.0.2",
|
|
41
|
+
"babel-eslint": "^10.1.0",
|
|
42
|
+
"babel-jest": "^26.2.2",
|
|
43
|
+
"babel-loader": "^8.2.5",
|
|
44
|
+
"babel-plugin-module-resolver": "^4.0.0",
|
|
45
|
+
"babel-plugin-react-native-web": "^0.13.5",
|
|
46
|
+
"babel-plugin-transform-class-properties": "^6.24.1",
|
|
47
|
+
"eslint": "^7.6.0",
|
|
48
|
+
"eslint-config-expensify": "^2.0.11",
|
|
49
|
+
"expensify-common": "git+https://github.com/Expensify/expensify-common.git#427295da130a4eacc184d38693664280d020dffd",
|
|
50
|
+
"jest": "^26.5.2",
|
|
51
|
+
"jest-cli": "^26.5.2",
|
|
52
|
+
"jsdoc-to-markdown": "^7.1.0",
|
|
53
|
+
"localforage": "^1.10.0",
|
|
54
|
+
"metro-react-native-babel-preset": "^0.61.0",
|
|
55
|
+
"prop-types": "^15.7.2",
|
|
56
|
+
"react": "^17.0.2",
|
|
57
|
+
"react-native": "0.64.1",
|
|
58
|
+
"react-native-performance": "^2.0.0",
|
|
59
|
+
"react-test-renderer": "16.13.1",
|
|
60
|
+
"webpack": "^5.72.1",
|
|
61
|
+
"webpack-cli": "^4.9.2",
|
|
62
|
+
"webpack-merge": "^5.8.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"@react-native-async-storage/async-storage": "^1.15.5",
|
|
66
|
+
"expensify-common": ">=1",
|
|
67
|
+
"localforage": "^1.10.0",
|
|
68
|
+
"react": ">=17.0.2",
|
|
69
|
+
"react-native-performance": "^2.0.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"react-native-performance": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@react-native-async-storage/async-storage": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"localforage": {
|
|
79
|
+
"optional": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"jest": {
|
|
83
|
+
"preset": "react-native",
|
|
84
|
+
"transform": {
|
|
85
|
+
"^.+\\.jsx?$": "babel-jest"
|
|
86
|
+
},
|
|
87
|
+
"transformIgnorePatterns": [
|
|
88
|
+
"node_modules/(?!react-native)/"
|
|
89
|
+
],
|
|
90
|
+
"testPathIgnorePatterns": [
|
|
91
|
+
"<rootDir>/node_modules/",
|
|
92
|
+
"<rootDir>/tests/unit/mocks/"
|
|
93
|
+
],
|
|
94
|
+
"testMatch": [
|
|
95
|
+
"**/tests/unit/**/*.[jt]s?(x)",
|
|
96
|
+
"**/?(*.)+(spec|test).[jt]s?(x)"
|
|
97
|
+
],
|
|
98
|
+
"globals": {
|
|
99
|
+
"__DEV__": true,
|
|
100
|
+
"WebSocket": {}
|
|
101
|
+
},
|
|
102
|
+
"timers": "fake",
|
|
103
|
+
"testEnvironment": "jsdom",
|
|
104
|
+
"setupFilesAfterEnv": [
|
|
105
|
+
"@testing-library/jest-native/extend-expect"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"sideEffects": false
|
|
109
|
+
}
|
package/web.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file
|
|
3
|
+
* Web entry point for Onyx
|
|
4
|
+
* This file is resolved by non react-native projects
|
|
5
|
+
* Like React for web or pure JS
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
if (process.env.NODE_ENV === 'production') {
|
|
9
|
+
module.exports = require('./dist/web.min.js');
|
|
10
|
+
} else {
|
|
11
|
+
module.exports = require('./dist/web.development.js');
|
|
12
|
+
}
|