dreaction-react-native 1.0.0

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.
Files changed (98) hide show
  1. package/lib/components/ConfigDialog.d.ts +9 -0
  2. package/lib/components/ConfigDialog.d.ts.map +1 -0
  3. package/lib/components/ConfigDialog.js +83 -0
  4. package/lib/components/DraggableBall.d.ts +3 -0
  5. package/lib/components/DraggableBall.d.ts.map +1 -0
  6. package/lib/components/DraggableBall.js +136 -0
  7. package/lib/dreaction.d.ts +29 -0
  8. package/lib/dreaction.d.ts.map +1 -0
  9. package/lib/dreaction.js +134 -0
  10. package/lib/helpers/getHost.d.ts +9 -0
  11. package/lib/helpers/getHost.d.ts.map +1 -0
  12. package/lib/helpers/getHost.js +31 -0
  13. package/lib/helpers/getReactNativeDimensions.d.ts +3 -0
  14. package/lib/helpers/getReactNativeDimensions.d.ts.map +1 -0
  15. package/lib/helpers/getReactNativeDimensions.js +18 -0
  16. package/lib/helpers/getReactNativeDimensions.test.d.ts +2 -0
  17. package/lib/helpers/getReactNativeDimensions.test.d.ts.map +1 -0
  18. package/lib/helpers/getReactNativeDimensions.test.js +20 -0
  19. package/lib/helpers/getReactNativeDimensionsWithDimensions.d.ts +12 -0
  20. package/lib/helpers/getReactNativeDimensionsWithDimensions.d.ts.map +1 -0
  21. package/lib/helpers/getReactNativeDimensionsWithDimensions.js +31 -0
  22. package/lib/helpers/getReactNativePlatformConstants.d.ts +13 -0
  23. package/lib/helpers/getReactNativePlatformConstants.d.ts.map +1 -0
  24. package/lib/helpers/getReactNativePlatformConstants.js +37 -0
  25. package/lib/helpers/getReactNativeVersion.d.ts +2 -0
  26. package/lib/helpers/getReactNativeVersion.d.ts.map +1 -0
  27. package/lib/helpers/getReactNativeVersion.js +8 -0
  28. package/lib/helpers/getReactNativeVersion.test.d.ts +2 -0
  29. package/lib/helpers/getReactNativeVersion.test.d.ts.map +1 -0
  30. package/lib/helpers/getReactNativeVersion.test.js +19 -0
  31. package/lib/helpers/getReactNativeVersionWithModules.d.ts +3 -0
  32. package/lib/helpers/getReactNativeVersionWithModules.d.ts.map +1 -0
  33. package/lib/helpers/getReactNativeVersionWithModules.js +32 -0
  34. package/lib/helpers/parseErrorStack.d.ts +5 -0
  35. package/lib/helpers/parseErrorStack.d.ts.map +1 -0
  36. package/lib/helpers/parseErrorStack.js +2 -0
  37. package/lib/helpers/parseURL.d.ts +9 -0
  38. package/lib/helpers/parseURL.d.ts.map +1 -0
  39. package/lib/helpers/parseURL.js +21 -0
  40. package/lib/helpers/parseURL.test.d.ts +2 -0
  41. package/lib/helpers/parseURL.test.d.ts.map +1 -0
  42. package/lib/helpers/parseURL.test.js +61 -0
  43. package/lib/helpers/symbolicateStackTrace.d.ts +18 -0
  44. package/lib/helpers/symbolicateStackTrace.d.ts.map +1 -0
  45. package/lib/helpers/symbolicateStackTrace.js +2 -0
  46. package/lib/index.d.ts +5 -0
  47. package/lib/index.d.ts.map +1 -0
  48. package/lib/index.js +20 -0
  49. package/lib/plugins/asyncStorage.d.ts +12 -0
  50. package/lib/plugins/asyncStorage.d.ts.map +1 -0
  51. package/lib/plugins/asyncStorage.js +173 -0
  52. package/lib/plugins/devTools.d.ts +3 -0
  53. package/lib/plugins/devTools.d.ts.map +1 -0
  54. package/lib/plugins/devTools.js +24 -0
  55. package/lib/plugins/networking.d.ts +10 -0
  56. package/lib/plugins/networking.d.ts.map +1 -0
  57. package/lib/plugins/networking.js +139 -0
  58. package/lib/plugins/openInEditor.d.ts +9 -0
  59. package/lib/plugins/openInEditor.d.ts.map +1 -0
  60. package/lib/plugins/openInEditor.js +21 -0
  61. package/lib/plugins/overlay/index.d.ts +3 -0
  62. package/lib/plugins/overlay/index.d.ts.map +1 -0
  63. package/lib/plugins/overlay/index.js +29 -0
  64. package/lib/plugins/overlay/overlay.d.ts +161 -0
  65. package/lib/plugins/overlay/overlay.d.ts.map +1 -0
  66. package/lib/plugins/overlay/overlay.js +99 -0
  67. package/lib/plugins/storybook/index.d.ts +6 -0
  68. package/lib/plugins/storybook/index.d.ts.map +1 -0
  69. package/lib/plugins/storybook/index.js +27 -0
  70. package/lib/plugins/storybook/storybook.d.ts +22 -0
  71. package/lib/plugins/storybook/storybook.d.ts.map +1 -0
  72. package/lib/plugins/storybook/storybook.js +31 -0
  73. package/lib/plugins/trackGlobalErrors.d.ts +32 -0
  74. package/lib/plugins/trackGlobalErrors.d.ts.map +1 -0
  75. package/lib/plugins/trackGlobalErrors.js +100 -0
  76. package/lib/plugins/trackGlobalLogs.d.ts +9 -0
  77. package/lib/plugins/trackGlobalLogs.d.ts.map +1 -0
  78. package/lib/plugins/trackGlobalLogs.js +31 -0
  79. package/package.json +29 -0
  80. package/src/components/ConfigDialog.tsx +79 -0
  81. package/src/components/DraggableBall.tsx +139 -0
  82. package/src/dreaction.ts +221 -0
  83. package/src/helpers/getHost.ts +30 -0
  84. package/src/helpers/getReactNativeDimensions.ts +20 -0
  85. package/src/helpers/getReactNativeDimensionsWithDimensions.ts +45 -0
  86. package/src/helpers/getReactNativePlatformConstants.ts +52 -0
  87. package/src/helpers/getReactNativeVersion.ts +6 -0
  88. package/src/helpers/getReactNativeVersionWithModules.ts +37 -0
  89. package/src/helpers/parseErrorStack.ts +13 -0
  90. package/src/helpers/parseURL.ts +19 -0
  91. package/src/helpers/symbolicateStackTrace.ts +28 -0
  92. package/src/index.ts +6 -0
  93. package/src/plugins/asyncStorage.ts +222 -0
  94. package/src/plugins/devTools.ts +30 -0
  95. package/src/plugins/networking.ts +172 -0
  96. package/src/plugins/openInEditor.ts +30 -0
  97. package/src/plugins/trackGlobalErrors.ts +156 -0
  98. package/src/plugins/trackGlobalLogs.ts +42 -0
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export type { Command } from 'dreaction-protocol';
2
+ import { DraggableBall } from './components/DraggableBall';
3
+
4
+ export { DraggableBall as DReactionDraggableBall };
5
+
6
+ export * from './dreaction';
@@ -0,0 +1,222 @@
1
+ import type { DReactionCore, Plugin } from 'dreaction-client-core';
2
+ // @ts-ignore
3
+ import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage';
4
+ export interface AsyncStorageOptions {
5
+ ignore?: string[];
6
+ }
7
+
8
+ const PLUGIN_DEFAULTS: AsyncStorageOptions = {
9
+ ignore: [],
10
+ };
11
+
12
+ const asyncStorage =
13
+ (options?: AsyncStorageOptions) => (reactotron: DReactionCore) => {
14
+ // setup configuration
15
+ const config = Object.assign({}, PLUGIN_DEFAULTS, options || {});
16
+ const ignore = config.ignore || PLUGIN_DEFAULTS.ignore;
17
+
18
+ let swizzSetItem: AsyncStorageStatic['setItem'];
19
+ let swizzRemoveItem: AsyncStorageStatic['removeItem'];
20
+ let swizzMergeItem: AsyncStorageStatic['mergeItem'];
21
+ let swizzClear: AsyncStorageStatic['clear'];
22
+ let swizzMultiSet: AsyncStorageStatic['multiSet'];
23
+ let swizzMultiRemove: AsyncStorageStatic['multiRemove'];
24
+ let swizzMultiMerge: AsyncStorageStatic['multiMerge'];
25
+ let isSwizzled = false;
26
+
27
+ const sendToReactotron = (action: string, data?: any) => {
28
+ reactotron.send('asyncStorage.mutation', { action, data });
29
+ };
30
+
31
+ const setItem: AsyncStorageStatic['setItem'] = async (
32
+ key: string,
33
+ value: any,
34
+ callback: any
35
+ ) => {
36
+ try {
37
+ if (ignore!.indexOf(key) < 0) {
38
+ sendToReactotron('setItem', { key, value });
39
+ }
40
+ } catch (e) {}
41
+ return swizzSetItem(key, value, callback);
42
+ };
43
+
44
+ const removeItem: AsyncStorageStatic['removeItem'] = async (
45
+ key: string,
46
+ callback: any
47
+ ) => {
48
+ try {
49
+ if (ignore!.indexOf(key) < 0) {
50
+ sendToReactotron('removeItem', { key });
51
+ }
52
+ } catch (e) {}
53
+ return swizzRemoveItem(key, callback);
54
+ };
55
+
56
+ const mergeItem: AsyncStorageStatic['mergeItem'] = async (
57
+ key: string,
58
+ value: any,
59
+ callback: any
60
+ ) => {
61
+ try {
62
+ if (ignore!.indexOf(key) < 0) {
63
+ sendToReactotron('mergeItem', { key, value });
64
+ }
65
+ } catch (e) {}
66
+ return swizzMergeItem(key, value, callback);
67
+ };
68
+
69
+ const clear: AsyncStorageStatic['clear'] = async (callback: any) => {
70
+ try {
71
+ sendToReactotron('clear');
72
+ } catch (e) {}
73
+ return swizzClear(callback);
74
+ };
75
+
76
+ const multiSet: AsyncStorageStatic['multiSet'] = async (
77
+ pairs: any,
78
+ callback: any
79
+ ) => {
80
+ try {
81
+ const shippablePairs = (pairs || []).filter(
82
+ (pair: any) => pair && pair[0] && ignore!.indexOf(pair[0]) < 0
83
+ );
84
+ if (shippablePairs.length > 0) {
85
+ sendToReactotron('multiSet', { pairs: shippablePairs });
86
+ }
87
+ } catch (e) {}
88
+ return swizzMultiSet(pairs, callback);
89
+ };
90
+
91
+ const multiRemove: AsyncStorageStatic['multiRemove'] = async (
92
+ keys: any,
93
+ callback: any
94
+ ) => {
95
+ try {
96
+ const shippableKeys = (keys || []).filter(
97
+ (key: any) => ignore!.indexOf(key) < 0
98
+ );
99
+ if (shippableKeys.length > 0) {
100
+ sendToReactotron('multiRemove', { keys: shippableKeys });
101
+ }
102
+ } catch (e) {}
103
+ return swizzMultiRemove(keys, callback);
104
+ };
105
+
106
+ const multiMerge: AsyncStorageStatic['multiMerge'] = async (
107
+ pairs: any,
108
+ callback: any
109
+ ) => {
110
+ try {
111
+ const shippablePairs = (pairs || []).filter(
112
+ (pair: any) => pair && pair[0] && ignore!.indexOf(pair[0]) < 0
113
+ );
114
+ if (shippablePairs.length > 0) {
115
+ sendToReactotron('multiMerge', { pairs: shippablePairs });
116
+ }
117
+ } catch (e) {}
118
+ return swizzMultiMerge(pairs, callback);
119
+ };
120
+
121
+ /**
122
+ * Hijacks the AsyncStorage API.
123
+ */
124
+ const trackAsyncStorage = () => {
125
+ if (isSwizzled) return;
126
+
127
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
128
+ // @ts-ignore: reactotron-apis
129
+ swizzSetItem = reactotron.asyncStorageHandler.setItem;
130
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
131
+ // @ts-ignore: reactotron-apis
132
+ reactotron.asyncStorageHandler.setItem = setItem;
133
+
134
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
135
+ // @ts-ignore: reactotron-apis
136
+ swizzRemoveItem = reactotron.asyncStorageHandler.removeItem;
137
+
138
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
139
+ // @ts-ignore: reactotron-apis
140
+ reactotron.asyncStorageHandler.removeItem = removeItem;
141
+
142
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
143
+ // @ts-ignore: reactotron-apis
144
+ swizzMergeItem = reactotron.asyncStorageHandler.mergeItem;
145
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
146
+ // @ts-ignore: reactotron-apis
147
+ reactotron.asyncStorageHandler.mergeItem = mergeItem;
148
+
149
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
150
+ // @ts-ignore: reactotron-apis
151
+ swizzClear = reactotron.asyncStorageHandler.clear;
152
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
153
+ // @ts-ignore: reactotron-apis
154
+ reactotron.asyncStorageHandler.clear = clear;
155
+
156
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
157
+ // @ts-ignore: reactotron-apis
158
+ swizzMultiSet = reactotron.asyncStorageHandler.multiSet;
159
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
160
+ // @ts-ignore: reactotron-apis
161
+ reactotron.asyncStorageHandler.multiSet = multiSet;
162
+
163
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
164
+ // @ts-ignore: reactotron-apis
165
+ swizzMultiRemove = reactotron.asyncStorageHandler.multiRemove;
166
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
167
+ // @ts-ignore: reactotron-apis
168
+ reactotron.asyncStorageHandler.multiRemove = multiRemove;
169
+
170
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
171
+ // @ts-ignore: reactotron-apis
172
+ swizzMultiMerge = reactotron.asyncStorageHandler.multiMerge;
173
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
174
+ // @ts-ignore: reactotron-apis
175
+ reactotron.asyncStorageHandler.multiMerge = multiMerge;
176
+
177
+ isSwizzled = true;
178
+ };
179
+
180
+ const untrackAsyncStorage = () => {
181
+ if (!isSwizzled) return;
182
+
183
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
184
+ // @ts-ignore: reactotron-apis
185
+ reactotron.asyncStorageHandler.setItem = swizzSetItem;
186
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
187
+ // @ts-ignore: reactotron-apis
188
+ reactotron.asyncStorageHandler.removeItem = swizzRemoveItem;
189
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
190
+ // @ts-ignore: reactotron-apis
191
+ reactotron.asyncStorageHandler.mergeItem = swizzMergeItem;
192
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
193
+ // @ts-ignore: reactotron-apis
194
+ reactotron.asyncStorageHandler.clear = swizzClear;
195
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
196
+ // @ts-ignore: reactotron-apis
197
+ reactotron.asyncStorageHandler.multiSet = swizzMultiSet;
198
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
199
+ // @ts-ignore: reactotron-apis
200
+ reactotron.asyncStorageHandler.multiRemove = swizzMultiRemove;
201
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
202
+ // @ts-ignore: reactotron-apis
203
+ reactotron.asyncStorageHandler.multiMerge = swizzMultiMerge;
204
+
205
+ isSwizzled = false;
206
+ };
207
+
208
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
209
+ // @ts-ignore: reactotron-apis
210
+ if (reactotron.asyncStorageHandler) {
211
+ trackAsyncStorage();
212
+ }
213
+
214
+ return {
215
+ features: {
216
+ trackAsyncStorage,
217
+ untrackAsyncStorage,
218
+ },
219
+ } satisfies Plugin<DReactionCore>;
220
+ };
221
+
222
+ export default asyncStorage;
@@ -0,0 +1,30 @@
1
+ import { Platform } from 'react-native';
2
+ import type { DReactionCore, Plugin } from 'dreaction-client-core';
3
+
4
+ let DevMenu = { show: () => {}, reload: () => {} };
5
+ if (Platform.OS === 'ios') {
6
+ DevMenu = require('react-native/Libraries/NativeModules/specs/NativeDevMenu');
7
+ }
8
+
9
+ const devTools: any = () => () => {
10
+ return {
11
+ onCommand: (command) => {
12
+ if (
13
+ command.type !== 'devtools.open' &&
14
+ command.type !== 'devtools.reload'
15
+ ) {
16
+ return;
17
+ }
18
+
19
+ if (command.type === 'devtools.open') {
20
+ DevMenu.show();
21
+ }
22
+
23
+ if (command.type === 'devtools.reload') {
24
+ DevMenu.reload();
25
+ }
26
+ },
27
+ } satisfies Plugin<DReactionCore>;
28
+ };
29
+
30
+ export default devTools;
@@ -0,0 +1,172 @@
1
+ // @ts-ignore
2
+ import XHRInterceptor from 'react-native/Libraries/Network/XHRInterceptor';
3
+ import type { DReactionCore, Plugin } from 'dreaction-client-core';
4
+
5
+ /**
6
+ * Don't include the response bodies for images by default.
7
+ */
8
+ const DEFAULT_CONTENT_TYPES_RX = /^(image)\/.*$/i;
9
+
10
+ export interface NetworkingOptions {
11
+ ignoreContentTypes?: RegExp;
12
+ ignoreUrls?: RegExp;
13
+ }
14
+
15
+ const DEFAULTS: NetworkingOptions = {};
16
+
17
+ const networking =
18
+ (pluginConfig: NetworkingOptions = {}) =>
19
+ (reactotron: DReactionCore) => {
20
+ const options = Object.assign({}, DEFAULTS, pluginConfig);
21
+
22
+ // a RegExp to suppress adding the body cuz it costs a lot to serialize
23
+ const ignoreContentTypes =
24
+ options.ignoreContentTypes || DEFAULT_CONTENT_TYPES_RX;
25
+
26
+ // a XHR call tracker
27
+ let reactotronCounter = 1000;
28
+
29
+ // a temporary cache to hold requests so we can match up the data
30
+ const requestCache = {};
31
+
32
+ /**
33
+ * Fires when we talk to the server.
34
+ *
35
+ * @param {*} data - The data sent to the server.
36
+ * @param {*} instance - The XMLHTTPRequest instance.
37
+ */
38
+ function onSend(data: any, xhr: any) {
39
+ if (options.ignoreUrls && options.ignoreUrls.test(xhr._url)) {
40
+ xhr._skipReactotron = true;
41
+ return;
42
+ }
43
+
44
+ // bump the counter
45
+ reactotronCounter++;
46
+
47
+ // tag
48
+ xhr._trackingName = reactotronCounter;
49
+
50
+ // cache
51
+ (requestCache as any)[reactotronCounter] = {
52
+ data,
53
+ xhr,
54
+ stopTimer: reactotron.startTimer(),
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Fires when the server gives us a response.
60
+ *
61
+ * @param {number} status - The HTTP response status.
62
+ * @param {boolean} timeout - Did we timeout?
63
+ * @param {*} response - The response data.
64
+ * @param {string} url - The URL we talked to.
65
+ * @param {*} type - Not sure.
66
+ * @param {*} xhr - The XMLHttpRequest instance.
67
+ */
68
+ function onResponse(
69
+ status: any,
70
+ timeout: any,
71
+ response: any,
72
+ url: any,
73
+ type: any,
74
+ xhr: any
75
+ ) {
76
+ if (xhr._skipReactotron) {
77
+ return;
78
+ }
79
+
80
+ let params = null;
81
+ const queryParamIdx = url ? url.indexOf('?') : -1;
82
+ if (queryParamIdx > -1) {
83
+ params = {};
84
+ url
85
+ .substr(queryParamIdx + 1)
86
+ .split('&')
87
+ .forEach((pair: any) => {
88
+ const [key, value] = pair.split('=');
89
+ if (key && value !== undefined) {
90
+ params[key] = decodeURIComponent(value.replace(/\+/g, ' '));
91
+ }
92
+ });
93
+ }
94
+
95
+ // fetch and clear the request data from the cache
96
+ const rid = xhr._trackingName;
97
+ const cachedRequest = (requestCache as any)[rid] || { xhr };
98
+ (requestCache as any)[rid] = null;
99
+
100
+ // assemble the request object
101
+ const { data, stopTimer } = cachedRequest;
102
+ const tronRequest = {
103
+ url: url || cachedRequest.xhr._url,
104
+ method: xhr._method || null,
105
+ data,
106
+ headers: xhr._headers || null,
107
+ params,
108
+ };
109
+
110
+ // what type of content is this?
111
+ const contentType =
112
+ (xhr.responseHeaders && xhr.responseHeaders['content-type']) ||
113
+ (xhr.responseHeaders && xhr.responseHeaders['Content-Type']) ||
114
+ '';
115
+
116
+ const sendResponse = (responseBodyText: string) => {
117
+ let body = `~~~ skipped ~~~`;
118
+ if (responseBodyText) {
119
+ try {
120
+ // all i am saying, is give JSON a chance...
121
+ body = JSON.parse(responseBodyText);
122
+ } catch (boom) {
123
+ body = response;
124
+ }
125
+ }
126
+ const tronResponse = {
127
+ body,
128
+ status,
129
+ headers: xhr.responseHeaders || null,
130
+ };
131
+ (reactotron as any).apiResponse(
132
+ tronRequest,
133
+ tronResponse,
134
+ stopTimer ? stopTimer() : null
135
+ ); // TODO: Fix
136
+ };
137
+
138
+ // can we use the real response?
139
+ const useRealResponse =
140
+ (typeof response === 'string' || typeof response === 'object') &&
141
+ !ignoreContentTypes.test(contentType || '');
142
+
143
+ // prepare the right body to send
144
+ if (useRealResponse) {
145
+ if (type === 'blob' && typeof FileReader !== 'undefined' && response) {
146
+ // Disable reason: FileReader should be in global scope since RN 0.54
147
+ // eslint-disable-next-line no-undef
148
+ const bReader = new FileReader();
149
+ const brListener = () => {
150
+ sendResponse(String(bReader.result));
151
+ bReader.removeEventListener('loadend', brListener);
152
+ };
153
+ bReader.addEventListener('loadend', brListener);
154
+ bReader.readAsText(response);
155
+ } else {
156
+ sendResponse(response);
157
+ }
158
+ } else {
159
+ sendResponse('');
160
+ }
161
+ }
162
+
163
+ return {
164
+ onConnect: () => {
165
+ // register our monkey-patch
166
+ XHRInterceptor.setSendCallback(onSend);
167
+ XHRInterceptor.setResponseCallback(onResponse);
168
+ XHRInterceptor.enableInterception();
169
+ },
170
+ } satisfies Plugin<DReactionCore>;
171
+ };
172
+ export default networking;
@@ -0,0 +1,30 @@
1
+ import type { DReactionCore, Plugin } from 'dreaction-client-core';
2
+ import type { Command } from 'dreaction-protocol';
3
+
4
+ export interface OpenInEditorOptions {
5
+ url?: string;
6
+ }
7
+
8
+ const DEFAULTS: OpenInEditorOptions = {
9
+ url: 'http://localhost:8081',
10
+ };
11
+
12
+ const openInEditor =
13
+ (pluginConfig: OpenInEditorOptions = {}) =>
14
+ () => {
15
+ const options = Object.assign({}, DEFAULTS, pluginConfig);
16
+
17
+ return {
18
+ onCommand: (command: Command) => {
19
+ if (command.type !== 'editor.open') return;
20
+ const { payload } = command;
21
+ const { file, lineNumber } = payload;
22
+ const url = `${options.url}/open-stack-frame`;
23
+ const body = { file, lineNumber: lineNumber || 1 };
24
+ const method = 'POST';
25
+
26
+ fetch(url, { method, body: JSON.stringify(body) });
27
+ },
28
+ } satisfies Plugin<DReactionCore>;
29
+ };
30
+ export default openInEditor;
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Provides a global error handler to report errors..
3
+ */
4
+ import {
5
+ InferFeatures,
6
+ LoggerPlugin,
7
+ DReactionCore,
8
+ assertHasLoggerPlugin,
9
+ Plugin,
10
+ } from 'dreaction-client-core';
11
+ import _LogBox, {
12
+ LogBoxStatic as LogBoxStaticPublic,
13
+ // eslint-disable-next-line import/default, import/namespace
14
+ } from 'react-native/Libraries/LogBox/LogBox';
15
+ // eslint-disable-next-line import/namespace
16
+ // import type { ExtendedExceptionData } from 'react-native/Libraries/LogBox/Data/parseLogBoxLog';
17
+ import type { SymbolicateStackTraceFn } from '../helpers/symbolicateStackTrace';
18
+ import type { ParseErrorStackFn } from '../helpers/parseErrorStack';
19
+
20
+ interface LogBoxStaticPrivate extends LogBoxStaticPublic {
21
+ /**
22
+ * @see https://github.com/facebook/react-native/blob/v0.72.1/packages/react-native/Libraries/LogBox/LogBox.js#L29
23
+ */
24
+ // addException: (error: ExtendedExceptionData) => void;
25
+ addException: (error: any) => void;
26
+ }
27
+
28
+ const LogBox = _LogBox as unknown as LogBoxStaticPrivate;
29
+
30
+ // a few functions to help source map errors -- these seem to be not available immediately
31
+ // so we're lazy loading.
32
+ let parseErrorStack: ParseErrorStackFn;
33
+ let symbolicateStackTrace: SymbolicateStackTraceFn;
34
+
35
+ export interface ErrorStackFrame {
36
+ fileName: string;
37
+ functionName: string;
38
+ lineNumber: number;
39
+ columnNumber?: number | null;
40
+ }
41
+
42
+ export interface TrackGlobalErrorsOptions {
43
+ veto?: (frame: ErrorStackFrame) => boolean;
44
+ }
45
+
46
+ // defaults
47
+ const PLUGIN_DEFAULTS: TrackGlobalErrorsOptions = {
48
+ veto: undefined,
49
+ };
50
+
51
+ const objectifyError = (error: Error) => {
52
+ const objectifiedError = {} as Record<string, unknown>;
53
+ Object.getOwnPropertyNames(error).forEach((key) => {
54
+ objectifiedError[key] = (error as any)[key];
55
+ });
56
+ return objectifiedError;
57
+ };
58
+
59
+ // const reactNativeFrameFinder = frame => contains('/node_modules/react-native/', frame.fileName)
60
+
61
+ /**
62
+ * Track global errors and send them to Reactotron logger.
63
+ */
64
+ const trackGlobalErrors =
65
+ (options?: TrackGlobalErrorsOptions) => (reactotron: DReactionCore) => {
66
+ // make sure we have the logger plugin
67
+ assertHasLoggerPlugin(reactotron);
68
+ const client = reactotron as DReactionCore &
69
+ InferFeatures<DReactionCore, LoggerPlugin>;
70
+
71
+ // setup configuration
72
+ const config = Object.assign({}, PLUGIN_DEFAULTS, options || {});
73
+
74
+ // manually fire an error
75
+ function reportError(error: Parameters<typeof LogBox.addException>[0]) {
76
+ try {
77
+ parseErrorStack =
78
+ parseErrorStack ||
79
+ require('react-native/Libraries/Core/Devtools/parseErrorStack');
80
+ symbolicateStackTrace =
81
+ symbolicateStackTrace ||
82
+ require('react-native/Libraries/Core/Devtools/symbolicateStackTrace');
83
+ } catch (e) {
84
+ client.error(
85
+ 'Unable to load "react-native/Libraries/Core/Devtools/parseErrorStack" or "react-native/Libraries/Core/Devtools/symbolicateStackTrace"',
86
+ []
87
+ );
88
+ client.debug(objectifyError(e as Error));
89
+ return;
90
+ }
91
+
92
+ if (!parseErrorStack || !symbolicateStackTrace) {
93
+ return;
94
+ }
95
+
96
+ let parsedStacktrace: ReturnType<typeof parseErrorStack>;
97
+
98
+ try {
99
+ // parseErrorStack arg type is wrong, it's expecting an array, a string, or a hermes error data, https://github.com/facebook/react-native/blob/v0.72.1/packages/react-native/Libraries/Core/Devtools/parseErrorStack.js#L41
100
+ parsedStacktrace = parseErrorStack(error.stack);
101
+ } catch (e) {
102
+ client.error('Unable to parse stack trace from error object', []);
103
+ client.debug(objectifyError(e as Error));
104
+ return;
105
+ }
106
+
107
+ symbolicateStackTrace(parsedStacktrace)
108
+ .then((symbolicatedStackTrace) => {
109
+ let prettyStackFrames = symbolicatedStackTrace.stack.map(
110
+ (stackFrame) => ({
111
+ fileName: stackFrame.file,
112
+ functionName: stackFrame.methodName,
113
+ lineNumber: stackFrame.lineNumber,
114
+ })
115
+ );
116
+ // does the dev want us to keep each frame?
117
+ if (config.veto) {
118
+ prettyStackFrames = prettyStackFrames.filter((frame) =>
119
+ config?.veto?.(frame)
120
+ );
121
+ }
122
+ client.error(error.message, prettyStackFrames); // TODO: Fix this.
123
+ })
124
+ .catch((e) => {
125
+ client.error(
126
+ 'Unable to symbolicate stack trace from error object',
127
+ []
128
+ );
129
+ client.debug(objectifyError(e));
130
+ });
131
+ }
132
+
133
+ // the reactotron plugin interface
134
+ return {
135
+ onConnect: () => {
136
+ LogBox.addException = new Proxy(LogBox.addException, {
137
+ apply: function (
138
+ target,
139
+ thisArg,
140
+ argumentsList: Parameters<typeof LogBox.addException>
141
+ ) {
142
+ const error = argumentsList[0];
143
+ reportError(error);
144
+ return target.apply(thisArg, argumentsList);
145
+ },
146
+ });
147
+ },
148
+
149
+ // attach these functions to the Reactotron
150
+ features: {
151
+ reportError,
152
+ },
153
+ } satisfies Plugin<DReactionCore>;
154
+ };
155
+
156
+ export default trackGlobalErrors;
@@ -0,0 +1,42 @@
1
+ import {
2
+ InferFeatures,
3
+ LoggerPlugin,
4
+ DReactionCore,
5
+ assertHasLoggerPlugin,
6
+ Plugin,
7
+ } from 'dreaction-client-core';
8
+
9
+ /**
10
+ * Track calls to console.log, console.warn, and console.debug and send them to Reactotron logger
11
+ */
12
+ const trackGlobalLogs = () => (reactotron: DReactionCore) => {
13
+ assertHasLoggerPlugin(reactotron);
14
+ const client = reactotron as DReactionCore &
15
+ InferFeatures<DReactionCore, LoggerPlugin>;
16
+
17
+ return {
18
+ onConnect: () => {
19
+ const originalConsoleLog = console.log;
20
+ console.log = (...args: Parameters<typeof console.log>) => {
21
+ originalConsoleLog(...args);
22
+ client.log(...args);
23
+ };
24
+
25
+ const originalConsoleWarn = console.warn;
26
+ console.warn = (...args: Parameters<typeof console.warn>) => {
27
+ originalConsoleWarn(...args);
28
+ client.warn(args[0]);
29
+ };
30
+
31
+ const originalConsoleDebug = console.debug;
32
+ console.debug = (...args: Parameters<typeof console.debug>) => {
33
+ originalConsoleDebug(...args);
34
+ client.debug(args[0]);
35
+ };
36
+
37
+ // console.error is taken care of by ./trackGlobalErrors.ts
38
+ },
39
+ } satisfies Plugin<DReactionCore>;
40
+ };
41
+
42
+ export default trackGlobalLogs;