Skip to content

Commit 3428339

Browse files
authored
Fix context menu functions for RN 0.56 (#247)
* Fix patchFetchPolyfill for compatible RN 0.56 * Fix required modules of RNDebugger functions on RN 0.56 * Update docs/network-inspect-of-chrome-devtools.md * Update docs/debugger-integration.md * Update test fixture * Add more comments about Network Inspect manually setup * Lookup for react-native implementation * Assign global.$reactNative for make debug easier * Detect metroRequire for RN 0.56 * Update docs/debugger-integration.md
1 parent 1d05530 commit 3428339

File tree

9 files changed

+127
-66
lines changed

9 files changed

+127
-66
lines changed

__e2e__/fixture/setup.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,27 @@ self.window = global;
55
// Remove native fetch as react-native use whatwg-fetch polyfill
66
self.fetch = undefined;
77

8+
const MessageQueue = function MessageQueue() {};
9+
MessageQueue.spy = () => {};
10+
MessageQueue.prototype.__spy = null;
11+
812
const requiredModules = {
913
NativeModules: {},
1014
Platform: {},
1115
setupDevtools: undefined,
1216
AsyncStorage: {},
13-
MessageQueue: {
14-
spy: () => {},
15-
prototype: { __spy: null },
16-
},
17+
MessageQueue,
1718
};
1819
// Simulate React Native's window.require polyfill
1920
window.require = moduleName => {
20-
// From https://github.com/facebook/react-native/blob/5403946f098cc72c3d33ea5cee263fb3dd03891d/packager/src/Resolver/polyfills/require.js#L97
21-
console.warn(
22-
`Requiring module '${moduleName}' by name is only supported for ` +
23-
'debugging purposes and will BREAK IN PRODUCTION!'
24-
);
21+
if (typeof moduleName !== 'number') {
22+
// From https://github.com/facebook/react-native/blob/5403946f098cc72c3d33ea5cee263fb3dd03891d/packager/src/Resolver/polyfills/require.js#L97
23+
console.warn(
24+
`Requiring module '${moduleName}' by name is only supported for ` +
25+
'debugging purposes and will BREAK IN PRODUCTION!'
26+
);
27+
}
2528
return requiredModules[moduleName];
2629
};
2730
window.__DEV__ = true;
31+
window.__fbBatchedBridge = new MessageQueue();

app/middlewares/debuggerAPI.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ const connectToDebuggerProxy = async () => {
127127
// Check Delta support
128128
try {
129129
if (await checkDeltaAvailable(host, port)) {
130-
const url = await deltaUrlToBlobUrl(object.url.replace('.bundle', '.delta'));
130+
const { url, moduleSize } = await deltaUrlToBlobUrl(
131+
object.url.replace('.bundle', '.delta')
132+
);
133+
object.moduleSize = moduleSize;
131134
clearLogs();
132135
scriptExecuted = true;
133136
worker.postMessage({ ...object, url });

app/middlewares/delta/DeltaPatcher.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ export default class DeltaPatcher {
113113
);
114114
}
115115

116+
getSizeOfAllModules() {
117+
return this._lastBundle.pre.size + this._lastBundle.modules.size + this._lastBundle.post.size;
118+
}
119+
116120
_patchMap(original, patch) {
117121
for (const [key, value] of patch.entries()) {
118122
if (value == null) {

app/middlewares/delta/deltaUrlToBlobUrl.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default async function deltaUrlToBlobUrl(deltaUrl) {
3636
// If nothing changed, avoid recreating a bundle blob by reusing the
3737
// previous one.
3838
if (deltaPatcher.getLastNumModifiedFiles() === 0 && cachedBundle) {
39-
return cachedBundle;
39+
return { url: cachedBundle, moduleSize: deltaPatcher.getSizeOfAllModules() };
4040
}
4141

4242
// Clean up the previous bundle URL to not leak memory.
@@ -56,5 +56,5 @@ export default async function deltaUrlToBlobUrl(deltaUrl) {
5656
const bundleContents = URL.createObjectURL(blob);
5757
cachedBundleUrls.set(deltaUrl, bundleContents);
5858

59-
return bundleContents;
59+
return { url: bundleContents, moduleSize: deltaPatcher.getSizeOfAllModules() };
6060
}

app/middlewares/delta/patchFetchPolyfill.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@
44
* Patch whatwg-fetch code to get `support` var,
55
* so we could use it to fix the issue.
66
*/
7-
const isFetch = /"node_modules\/@?[a-zA-Z-_/]*whatwg-fetch\/fetch.js"/;
87

9-
const fetchSupportFlag = /(if \(self.fetch\) {\n\s+return;\n\s+}\n\s+var support )(=)( {)/g;
8+
const isFetch = [
9+
/"node_modules\/@?[a-zA-Z-_/]*whatwg-fetch\/fetch.js"/,
10+
/*
11+
* RN >= 0.56
12+
*/
13+
/,"whatwg-fetch.js"\)/,
14+
];
15+
16+
const fetchSupportFlag = /(var support )(=)( {)/g;
1017
const fetchSupportReplaceStr = '$1= self.__FETCH_SUPPORT__ =$3';
1118

12-
const toggleFlag = 'self.fetch.polyfill = true';
19+
const toggleFlag = 'fetch.polyfill = true';
1320
// Toggle Network Inspect after define `support` var.
1421
// We have been set up `__NETWORK_INSPECT__` in Worker before import application script.
15-
const toggleReplaceStr = `self.__NETWORK_INSPECT__ && self.__NETWORK_INSPECT__(true);${toggleFlag}`;
22+
const toggleReplaceStr = `${toggleFlag};self.__NETWORK_INSPECT__ && self.__NETWORK_INSPECT__(true)`;
1623

17-
export const checkFetchExists = code => isFetch.test(code);
24+
export const checkFetchExists = code => isFetch.some(regex => regex.test(code));
1825
export const patchFetchPolyfill = code =>
1926
code.replace(fetchSupportFlag, fetchSupportReplaceStr).replace(toggleFlag, toggleReplaceStr);

app/worker/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const setupRNDebugger = async message => {
4545
// doesn't notify to the remote JS runtime
4646
self.__RND_INTERVAL__ = setInterval(function() {}, 100); // eslint-disable-line
4747

48-
const modules = await getRequiredModules();
48+
const modules = await getRequiredModules(message.moduleSize);
4949
if (modules) {
5050
ignoreRNDIntervalSpy(modules);
5151
checkAvailableDevMenuMethods(modules, message.networkInspect);

app/worker/utils.js

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable no-underscore-dangle */
22

33
// Avoid warning of use `window.require` on dev mode
4+
// it actually unnecessary for RN >= 0.56, so it is backward compatibility
45
const avoidWarnForRequire = moduleNames => {
56
if (!moduleNames.length) moduleNames.push('NativeModules');
67
return new Promise(resolve =>
@@ -23,20 +24,49 @@ const avoidWarnForRequire = moduleNames => {
2324
);
2425
};
2526

26-
const requiredModules = [
27-
'MessageQueue',
28-
'NativeModules',
29-
'AsyncStorage',
30-
'Platform',
31-
'setupDevtools',
32-
];
33-
export const getRequiredModules = async () => {
27+
let reactNative;
28+
29+
const lookupForRNModules = (size = 999) => {
30+
for (let moduleId = 0; moduleId <= size - 1; moduleId++) {
31+
const rn = window.require(moduleId);
32+
if (rn.requireNativeComponent && rn.NativeModules) {
33+
return rn;
34+
}
35+
}
36+
return null;
37+
};
38+
39+
const getModule = (name, size) => {
40+
let result;
41+
try {
42+
// RN >= 0.56
43+
if (window.require.name === 'metroRequire') {
44+
reactNative = global.$reactNative = reactNative || lookupForRNModules(size);
45+
result = reactNative && reactNative[name];
46+
} else if (window.require.name === '_require') {
47+
result = window.require(name);
48+
}
49+
} catch (e) {} // eslint-disable-line
50+
return result || { __empty: true };
51+
};
52+
53+
const requiredModules = {
54+
MessageQueue: size =>
55+
(self.__fbBatchedBridge && Object.getPrototypeOf(self.__fbBatchedBridge).constructor) ||
56+
getModule('MessageQueue', size),
57+
NativeModules: size => getModule('NativeModules', size),
58+
AsyncStorage: size => getModule('AsyncStorage', size),
59+
Platform: size => getModule('Platform', size),
60+
setupDevtools: size => getModule('setupDevtools', size),
61+
};
62+
63+
export const getRequiredModules = async size => {
3464
if (!window.__DEV__ || typeof window.require !== 'function') return;
35-
const done = await avoidWarnForRequire(requiredModules);
65+
const done = await avoidWarnForRequire(Object.keys(requiredModules));
3666
const modules = {};
37-
requiredModules.forEach(
38-
name => (modules[name] = window.__DEV__ ? window.require(name) : { __empty: true })
39-
);
67+
for (const name of Object.keys(requiredModules)) {
68+
modules[name] = requiredModules[name](size);
69+
}
4070
done();
4171
return modules;
4272
};

docs/debugger-integration.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
The Debugger worker is referenced from [react-native](https://github.com/facebook/react-native/blob/master/local-cli/server/util/) debugger-ui, so it's only working if you're enabled `Debug JS Remotely`, you can debug your app in Chrome Developer Tools, we keep the following tabs:
44

5-
* `Console`
6-
* `Sources`
7-
* `Network` (Inspect Network requests if you are enabled [Network Inspect](network-inspect-of-chrome-devtools.md))
8-
* `Memory`
5+
- `Console`
6+
- `Sources`
7+
- `Network` (Inspect Network requests if you are enabled [Network Inspect](network-inspect-of-chrome-devtools.md))
8+
- `Memory`
99

1010
## Multiple React Native packager (custom port) support
1111

@@ -21,9 +21,9 @@ For macOS (10.12+), it used native tabs feature, see [the support page](https://
2121

2222
When you enabled remote debugging, RNDebugger should switched context to `RNDebuggerWorker.js` automatically, so you can get global variables of React Native runtime in the console.
2323

24-
* `$r`: You selected element on react-devtools.
25-
* `require('<providesModule>')`: The module specified [`@providesModule`](https://github.com/facebook/react-native/search?l=JavaScript&q=providesModule&type=&utf8=✓) words in React Native, even you can specify in your files. This is example for use `AsyncStorage`: <img width="519" alt="t" src="https://cloud.githubusercontent.com/assets/3001525/25587896/a1253c9e-2ed8-11e7-9d70-6368cfd5e016.png">
26-
* `showAsyncStorageContentInDev()` - Log AsyncStorage content
24+
- `$r`: You selected element on react-devtools.
25+
- `showAsyncStorageContentInDev()` - Log AsyncStorage content
26+
- `require(...)` (RN < 0.56) or `$reactNative.*` (RN >= 0.56) - Get react-native modules. For example, you can get `$reactNative.AsyncStorage`
2727

2828
#### [iOS only] Force your app on debug mode
2929

@@ -44,11 +44,11 @@ if (__DEV__) {
4444

4545
## Other documentations
4646

47-
* [Getting Started](getting-started.md)
48-
* [React DevTools Integration](react-devtools-integration.md)
49-
* [Redux DevTools Integration](redux-devtools-integration.md)
50-
* [Shortcut references](shortcut-references.md)
51-
* [Network inspect of Chrome Developer Tools](network-inspect-of-chrome-devtools.md)
52-
* [Enable open in editor in console](enable-open-in-editor-in-console.md)
53-
* [Troubleshooting](troubleshooting.md)
54-
* [Contributing](contributing.md)
47+
- [Getting Started](getting-started.md)
48+
- [React DevTools Integration](react-devtools-integration.md)
49+
- [Redux DevTools Integration](redux-devtools-integration.md)
50+
- [Shortcut references](shortcut-references.md)
51+
- [Network inspect of Chrome Developer Tools](network-inspect-of-chrome-devtools.md)
52+
- [Enable open in editor in console](enable-open-in-editor-in-console.md)
53+
- [Troubleshooting](troubleshooting.md)
54+
- [Contributing](contributing.md)
Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# Network Inspect of Chrome Developer Tools
22

3-
__*WARNING*__: You should read [the limitations](#limitations) if you want to use this feature.
3+
**_WARNING_**: You should read [the limitations](#limitations) if you want to use this feature.
44

55
You can enable this feature by the [context menu or Touch Bar](shortcut-references.md), then you can inspect network requests that use `XMLHttpRequest` or `fetch` on the `Network` tab of Chrome Developer Tools.
66

77
## How it works
88

9-
See [the comments of `react-native/Libraries/Core/InitializeCore.js#L43-L53`](https://github.com/facebook/react-native/blob/0.45-stable/Libraries/Core/InitializeCore.js#L43-L53). It uses `XMLHttpRequest` from WebWorker in Chrome:
9+
See [the comments of `react-native/Libraries/Core/InitializeCore.js#L43-L53`](https://github.com/facebook/react-native/blob/0.45-stable/Libraries/Core/InitializeCore.js#L43-L53). It uses `XMLHttpRequest` from WebWorker in Chrome, basically it can manually setup by:
1010

1111
```js
1212
global.XMLHttpRequest = global.originalXMLHttpRequest ?
@@ -15,38 +15,51 @@ global.XMLHttpRequest = global.originalXMLHttpRequest ?
1515
global.FormData = global.originalFormData ?
1616
global.originalFormData :
1717
global.FormData;
18-
global.Blob = global.originalBlob ?
19-
global.originalBlob :
20-
global.Blob;
21-
global.FileReader = global.originalFileReader ?
22-
global.originalFileReader :
23-
global.FileReader;
18+
19+
fetch; // Ensure to get the lazy property
20+
21+
if (window.__FETCH_SUPPORT__) { // it's RNDebugger only to have
22+
window.__FETCH_SUPPORT__.blob = false;
23+
} else {
24+
/*
25+
* Set __FETCH_SUPPORT__ to false is just work for `fetch`.
26+
* If you're using another way you can just use the native Blob and remove the `else` statement
27+
*/
28+
global.Blob = global.originalBlob ?
29+
global.originalBlob :
30+
global.Blob;
31+
global.FileReader = global.originalFileReader ?
32+
global.originalFileReader :
33+
global.FileReader;
34+
}
2435
```
2536

37+
> Note that replace `global.Blob` will cause issue like [#56](https://github.com/jhen0409/react-native-debugger/issues/56).
38+
2639
This allows you can open the `Network` tab in devtools to inspect requests of `fetch` and `XMLHttpRequest`.
2740

2841
You can also do this on the official remote debugger, but it has two differences:
2942

30-
* RNDebugger is based on [Electron](https://github.com/electron/electron) so it doesn't have the CORS issue
31-
* We support setting [`Forbidden header names`](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name), so you can use headers like `Origin` and `User-Agent`.
43+
- RNDebugger is based on [Electron](https://github.com/electron/electron) so it doesn't have the CORS issue
44+
- We support setting [`Forbidden header names`](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name), so you can use headers like `Origin` and `User-Agent`.
3245

3346
## Limitations
3447

3548
There are some limitations of debugging network requests using Network Inspect:
3649

37-
* [iOS] Requests pass `NSExceptionDomains` checks. If you forget to set a domain name, the requests will break in production. You should be clear about the difference.
38-
* React Native `FormData` supports the `uri` property. You can use files from `CameraRoll`, but `originalFormData` isn't supported.
39-
* It can't inspect request like `Image`s loaded from urls for `src`, so if your `Image` source has a set session, the session can't apply to `fetch` and `XMLHttpRequest`.
50+
- [iOS] Requests pass `NSExceptionDomains` checks. If you forget to set a domain name, the requests will break in production. You should be clear about the difference.
51+
- React Native `FormData` supports the `uri` property. You can use files from `CameraRoll`, but `originalFormData` isn't supported.
52+
- It can't inspect request like `Image`s loaded from urls for `src`, so if your `Image` source has a set session, the session can't apply to `fetch` and `XMLHttpRequest`.
4053

4154
If you want to inspect deeper network requests (like requests made with `Image`), use tools like [Charles](https://www.charlesproxy.com) or [Stetho](https://facebook.github.io/stetho).
4255

4356
## Other documentations
4457

45-
* [Getting Started](getting-started.md)
46-
* [Debugger Integration](debugger-integration.md)
47-
* [React DevTools Integration](react-devtools-integration.md)
48-
* [Redux DevTools Integration](redux-devtools-integration.md)
49-
* [Shortcut references](shortcut-references.md)
50-
* [Enable open in editor in console](enable-open-in-editor-in-console.md)
51-
* [Troubleshooting](troubleshooting.md)
52-
* [Contributing](contributing.md)
58+
- [Getting Started](getting-started.md)
59+
- [Debugger Integration](debugger-integration.md)
60+
- [React DevTools Integration](react-devtools-integration.md)
61+
- [Redux DevTools Integration](redux-devtools-integration.md)
62+
- [Shortcut references](shortcut-references.md)
63+
- [Enable open in editor in console](enable-open-in-editor-in-console.md)
64+
- [Troubleshooting](troubleshooting.md)
65+
- [Contributing](contributing.md)

0 commit comments

Comments
 (0)