@react-native/dev-middleware 0.75.0-rc.2 → 0.76.0-nightly-20240628-TEMP
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/dist/createDevMiddleware.js +0 -11
- package/dist/inspector-proxy/Device.d.ts +10 -1
- package/dist/inspector-proxy/Device.js +119 -256
- package/dist/inspector-proxy/Device.js.flow +10 -1
- package/dist/inspector-proxy/DeviceEventReporter.d.ts +2 -1
- package/dist/inspector-proxy/DeviceEventReporter.js +0 -11
- package/dist/inspector-proxy/DeviceEventReporter.js.flow +2 -1
- package/dist/inspector-proxy/InspectorProxy.d.ts +4 -0
- package/dist/inspector-proxy/InspectorProxy.js +24 -91
- package/dist/inspector-proxy/InspectorProxy.js.flow +4 -0
- package/dist/inspector-proxy/cdp-types/messages.d.ts +10 -9
- package/dist/inspector-proxy/cdp-types/messages.js.flow +12 -11
- package/dist/inspector-proxy/cdp-types/protocol.d.ts +14 -10
- package/dist/inspector-proxy/cdp-types/protocol.js.flow +13 -11
- package/dist/inspector-proxy/types.d.ts +6 -0
- package/dist/inspector-proxy/types.js.flow +7 -0
- package/dist/middleware/deprecated_openFlipperMiddleware.js +0 -18
- package/dist/middleware/openDebuggerMiddleware.js +25 -38
- package/dist/utils/DefaultBrowserLauncher.js +0 -24
- package/dist/utils/getDevToolsFrontendUrl.js +0 -17
- package/package.json +3 -3
|
@@ -8,6 +8,7 @@ var _DeviceEventReporter = _interopRequireDefault(
|
|
|
8
8
|
require("./DeviceEventReporter")
|
|
9
9
|
);
|
|
10
10
|
var fs = _interopRequireWildcard(require("fs"));
|
|
11
|
+
var _invariant = _interopRequireDefault(require("invariant"));
|
|
11
12
|
var _nodeFetch = _interopRequireDefault(require("node-fetch"));
|
|
12
13
|
var path = _interopRequireWildcard(require("path"));
|
|
13
14
|
var _ws = _interopRequireDefault(require("ws"));
|
|
@@ -54,90 +55,28 @@ function _interopRequireWildcard(obj, nodeInterop) {
|
|
|
54
55
|
function _interopRequireDefault(obj) {
|
|
55
56
|
return obj && obj.__esModule ? obj : { default: obj };
|
|
56
57
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
59
|
-
*
|
|
60
|
-
* This source code is licensed under the MIT license found in the
|
|
61
|
-
* LICENSE file in the root directory of this source tree.
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* @format
|
|
65
|
-
* @oncall react_native
|
|
66
|
-
*/
|
|
67
|
-
|
|
68
58
|
const debug = require("debug")("Metro:InspectorProxy");
|
|
69
59
|
const PAGES_POLLING_INTERVAL = 1000;
|
|
70
|
-
|
|
71
|
-
// Replace hosts appearing in the `url` and `sourceMapURL` fields of
|
|
72
|
-
// `Debugger.scriptParsed`, and back again in messages from the debugger,
|
|
73
|
-
// to account for device/debugger/proxy running on different networks.
|
|
74
|
-
const REWRITE_HOSTS_TO_LOCALHOST = [
|
|
75
|
-
// A device may retrieve a bundle through 127.0.0.1 via a (SSH) tunnel, but
|
|
76
|
-
// the (remote) Metro server may be on a host without an IPv4 loopback, so
|
|
77
|
-
// 127.0.0.1 may not be addressible locally for (e.g., for source map
|
|
78
|
-
// fetching). Replacing with the more general 'localhost' should always be
|
|
79
|
-
// safe while also more compatible with IPv6-only setups.
|
|
80
|
-
"127.0.0.1",
|
|
81
|
-
// Android's stock emulator and other emulators such as genymotion use a
|
|
82
|
-
// standard localhost alias.
|
|
83
|
-
"10.0.2.2",
|
|
84
|
-
"10.0.3.2",
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
// Prefix for script URLs that are alphanumeric IDs. See comment in #processMessageFromDeviceLegacy method for
|
|
88
|
-
// more details.
|
|
60
|
+
const REWRITE_HOSTS_TO_LOCALHOST = ["127.0.0.1", "10.0.2.2", "10.0.3.2"];
|
|
89
61
|
const FILE_PREFIX = "file://";
|
|
90
62
|
const REACT_NATIVE_RELOADABLE_PAGE_ID = "-1";
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Device class represents single device connection to Inspector Proxy. Each device
|
|
94
|
-
* can have multiple inspectable pages.
|
|
95
|
-
*/
|
|
96
63
|
class Device {
|
|
97
|
-
// ID of the device.
|
|
98
64
|
#id;
|
|
99
|
-
|
|
100
|
-
// Name of the device.
|
|
101
65
|
#name;
|
|
102
|
-
|
|
103
|
-
// Package name of the app.
|
|
104
66
|
#app;
|
|
105
|
-
|
|
106
|
-
// Sequences async processing of messages from device to preserve order. Only
|
|
107
|
-
// necessary while we need to accommodate #processMessageFromDeviceLegacy's
|
|
108
|
-
// async fetch.
|
|
109
67
|
#messageFromDeviceQueue = Promise.resolve();
|
|
110
|
-
|
|
111
|
-
// Stores socket connection between Inspector Proxy and device.
|
|
112
68
|
#deviceSocket;
|
|
113
|
-
|
|
114
|
-
// Stores the most recent listing of device's pages, keyed by the `id` field.
|
|
115
|
-
#pages;
|
|
116
|
-
|
|
117
|
-
// Stores information about currently connected debugger (if any).
|
|
69
|
+
#pages = new Map();
|
|
118
70
|
#debuggerConnection = null;
|
|
119
|
-
|
|
120
|
-
// Last known Page ID of the React Native page.
|
|
121
|
-
// This is used by debugger connections that don't have PageID specified
|
|
122
|
-
// (and will interact with the latest React Native page).
|
|
123
71
|
#lastConnectedLegacyReactNativePage = null;
|
|
124
|
-
|
|
125
|
-
// Whether we are in the middle of a reload in the REACT_NATIVE_RELOADABLE_PAGE.
|
|
126
72
|
#isLegacyPageReloading = false;
|
|
127
|
-
|
|
128
|
-
// The previous "GetPages" message, for deduplication in debug logs.
|
|
129
73
|
#lastGetPagesMessage = "";
|
|
130
|
-
|
|
131
|
-
// Mapping built from scriptParsed events and used to fetch file content in `Debugger.getScriptSource`.
|
|
132
74
|
#scriptIdToSourcePathMapping = new Map();
|
|
133
|
-
|
|
134
|
-
// Root of the project used for relative to absolute source path conversion.
|
|
135
75
|
#projectRoot;
|
|
136
76
|
#deviceEventReporter;
|
|
137
77
|
#pagesPollingIntervalId;
|
|
138
|
-
|
|
139
|
-
// The device message middleware factory function allowing implementers to handle unsupported CDP messages.
|
|
140
78
|
#createCustomMessageHandler;
|
|
79
|
+
#connectedPageIds = new Set();
|
|
141
80
|
constructor(
|
|
142
81
|
id,
|
|
143
82
|
name,
|
|
@@ -146,11 +85,29 @@ class Device {
|
|
|
146
85
|
projectRoot,
|
|
147
86
|
eventReporter,
|
|
148
87
|
createMessageMiddleware
|
|
88
|
+
) {
|
|
89
|
+
this.#dangerouslyConstruct(
|
|
90
|
+
id,
|
|
91
|
+
name,
|
|
92
|
+
app,
|
|
93
|
+
socket,
|
|
94
|
+
projectRoot,
|
|
95
|
+
eventReporter,
|
|
96
|
+
createMessageMiddleware
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
#dangerouslyConstruct(
|
|
100
|
+
id,
|
|
101
|
+
name,
|
|
102
|
+
app,
|
|
103
|
+
socket,
|
|
104
|
+
projectRoot,
|
|
105
|
+
eventReporter,
|
|
106
|
+
createMessageMiddleware
|
|
149
107
|
) {
|
|
150
108
|
this.#id = id;
|
|
151
109
|
this.#name = name;
|
|
152
110
|
this.#app = app;
|
|
153
|
-
this.#pages = new Map();
|
|
154
111
|
this.#deviceSocket = socket;
|
|
155
112
|
this.#projectRoot = projectRoot;
|
|
156
113
|
this.#deviceEventReporter = eventReporter
|
|
@@ -161,14 +118,11 @@ class Device {
|
|
|
161
118
|
})
|
|
162
119
|
: null;
|
|
163
120
|
this.#createCustomMessageHandler = createMessageMiddleware;
|
|
164
|
-
|
|
165
|
-
// $FlowFixMe[incompatible-call]
|
|
166
121
|
this.#deviceSocket.on("message", (message) => {
|
|
167
122
|
this.#messageFromDeviceQueue = this.#messageFromDeviceQueue
|
|
168
123
|
.then(async () => {
|
|
169
124
|
const parsedMessage = JSON.parse(message);
|
|
170
125
|
if (parsedMessage.event === "getPages") {
|
|
171
|
-
// There's a 'getPages' message every second, so only show them if they change
|
|
172
126
|
if (message !== this.#lastGetPagesMessage) {
|
|
173
127
|
debug(
|
|
174
128
|
"(Debugger) (Proxy) <- (Device), getPages ping has changed: " +
|
|
@@ -197,7 +151,6 @@ class Device {
|
|
|
197
151
|
}
|
|
198
152
|
});
|
|
199
153
|
});
|
|
200
|
-
// Sends 'getPages' request to device every PAGES_POLLING_INTERVAL milliseconds.
|
|
201
154
|
this.#pagesPollingIntervalId = setInterval(
|
|
202
155
|
() =>
|
|
203
156
|
this.#sendMessageToDevice({
|
|
@@ -206,15 +159,59 @@ class Device {
|
|
|
206
159
|
PAGES_POLLING_INTERVAL
|
|
207
160
|
);
|
|
208
161
|
this.#deviceSocket.on("close", () => {
|
|
209
|
-
this.#
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
this.#
|
|
213
|
-
this.#debuggerConnection = null;
|
|
162
|
+
if (socket === this.#deviceSocket) {
|
|
163
|
+
this.#deviceEventReporter?.logDisconnection("device");
|
|
164
|
+
this.#terminateDebuggerConnection();
|
|
165
|
+
clearInterval(this.#pagesPollingIntervalId);
|
|
214
166
|
}
|
|
215
|
-
clearInterval(this.#pagesPollingIntervalId);
|
|
216
167
|
});
|
|
217
168
|
}
|
|
169
|
+
#terminateDebuggerConnection() {
|
|
170
|
+
const debuggerConnection = this.#debuggerConnection;
|
|
171
|
+
if (debuggerConnection) {
|
|
172
|
+
this.#sendDisconnectEventToDevice(
|
|
173
|
+
this.#mapToDevicePageId(debuggerConnection.pageId)
|
|
174
|
+
);
|
|
175
|
+
debuggerConnection.socket.close();
|
|
176
|
+
this.#debuggerConnection = null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
dangerouslyRecreateDevice(
|
|
180
|
+
id,
|
|
181
|
+
name,
|
|
182
|
+
app,
|
|
183
|
+
socket,
|
|
184
|
+
projectRoot,
|
|
185
|
+
eventReporter,
|
|
186
|
+
createMessageMiddleware
|
|
187
|
+
) {
|
|
188
|
+
(0, _invariant.default)(
|
|
189
|
+
id === this.#id,
|
|
190
|
+
"dangerouslyRecreateDevice() can only be used for the same device ID"
|
|
191
|
+
);
|
|
192
|
+
const oldDebugger = this.#debuggerConnection;
|
|
193
|
+
if (this.#app !== app || this.#name !== name) {
|
|
194
|
+
this.#deviceSocket.close();
|
|
195
|
+
this.#terminateDebuggerConnection();
|
|
196
|
+
}
|
|
197
|
+
this.#debuggerConnection = null;
|
|
198
|
+
if (oldDebugger) {
|
|
199
|
+
oldDebugger.socket.removeAllListeners();
|
|
200
|
+
this.#deviceSocket.close();
|
|
201
|
+
this.handleDebuggerConnection(oldDebugger.socket, oldDebugger.pageId, {
|
|
202
|
+
userAgent: oldDebugger.userAgent,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
this.#dangerouslyConstruct(
|
|
206
|
+
id,
|
|
207
|
+
name,
|
|
208
|
+
app,
|
|
209
|
+
socket,
|
|
210
|
+
projectRoot,
|
|
211
|
+
eventReporter,
|
|
212
|
+
createMessageMiddleware
|
|
213
|
+
);
|
|
214
|
+
}
|
|
218
215
|
getName() {
|
|
219
216
|
return this.#name;
|
|
220
217
|
}
|
|
@@ -228,24 +225,26 @@ class Device {
|
|
|
228
225
|
return [...this.#pages.values()];
|
|
229
226
|
}
|
|
230
227
|
}
|
|
231
|
-
|
|
232
|
-
// Handles new debugger connection to this device:
|
|
233
|
-
// 1. Sends connect event to device
|
|
234
|
-
// 2. Forwards all messages from the debugger to device as wrappedEvent
|
|
235
|
-
// 3. Sends disconnect event to device when debugger connection socket closes.
|
|
236
228
|
handleDebuggerConnection(socket, pageId, metadata) {
|
|
237
|
-
|
|
229
|
+
const page =
|
|
230
|
+
pageId === REACT_NATIVE_RELOADABLE_PAGE_ID
|
|
231
|
+
? this.#createSyntheticPage()
|
|
232
|
+
: this.#pages.get(pageId);
|
|
233
|
+
if (!page) {
|
|
234
|
+
debug(
|
|
235
|
+
`Got new debugger connection for page ${pageId} of ${
|
|
236
|
+
this.#name
|
|
237
|
+
}, but no such page exists`
|
|
238
|
+
);
|
|
239
|
+
socket.close();
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
238
242
|
this.#deviceEventReporter?.logDisconnection("debugger");
|
|
243
|
+
this.#terminateDebuggerConnection();
|
|
239
244
|
this.#deviceEventReporter?.logConnection("debugger", {
|
|
240
245
|
pageId,
|
|
241
246
|
frontendUserAgent: metadata.userAgent,
|
|
242
247
|
});
|
|
243
|
-
|
|
244
|
-
// Disconnect current debugger if we already have debugger connected.
|
|
245
|
-
if (this.#debuggerConnection) {
|
|
246
|
-
this.#debuggerConnection.socket.close();
|
|
247
|
-
this.#debuggerConnection = null;
|
|
248
|
-
}
|
|
249
248
|
const debuggerInfo = {
|
|
250
249
|
socket,
|
|
251
250
|
prependedFilePrefix: false,
|
|
@@ -253,16 +252,9 @@ class Device {
|
|
|
253
252
|
userAgent: metadata.userAgent,
|
|
254
253
|
customHandler: null,
|
|
255
254
|
};
|
|
256
|
-
|
|
257
|
-
// TODO(moti): Handle null case explicitly, e.g. refuse to connect to
|
|
258
|
-
// unknown pages.
|
|
259
|
-
const page =
|
|
260
|
-
pageId === REACT_NATIVE_RELOADABLE_PAGE_ID
|
|
261
|
-
? this.#createSyntheticPage()
|
|
262
|
-
: this.#pages.get(pageId);
|
|
263
255
|
this.#debuggerConnection = debuggerInfo;
|
|
264
256
|
debug(`Got new debugger connection for page ${pageId} of ${this.#name}`);
|
|
265
|
-
if (
|
|
257
|
+
if (this.#debuggerConnection && this.#createCustomMessageHandler) {
|
|
266
258
|
this.#debuggerConnection.customHandler = this.#createCustomMessageHandler(
|
|
267
259
|
{
|
|
268
260
|
page,
|
|
@@ -304,14 +296,7 @@ class Device {
|
|
|
304
296
|
);
|
|
305
297
|
}
|
|
306
298
|
}
|
|
307
|
-
this.#
|
|
308
|
-
event: "connect",
|
|
309
|
-
payload: {
|
|
310
|
-
pageId: this.#mapToDevicePageId(pageId),
|
|
311
|
-
},
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
// $FlowFixMe[incompatible-call]
|
|
299
|
+
this.#sendConnectEventToDevice(this.#mapToDevicePageId(pageId));
|
|
315
300
|
socket.on("message", (message) => {
|
|
316
301
|
debug("(Debugger) -> (Proxy) (Device): " + message);
|
|
317
302
|
const debuggerRequest = JSON.parse(message);
|
|
@@ -330,7 +315,7 @@ class Device {
|
|
|
330
315
|
) {
|
|
331
316
|
return;
|
|
332
317
|
}
|
|
333
|
-
if (!
|
|
318
|
+
if (!this.#pageHasCapability(page, "nativeSourceCodeFetching")) {
|
|
334
319
|
processedReq = this.#interceptClientMessageForSourceFetching(
|
|
335
320
|
debuggerRequest,
|
|
336
321
|
debuggerInfo,
|
|
@@ -350,66 +335,43 @@ class Device {
|
|
|
350
335
|
socket.on("close", () => {
|
|
351
336
|
debug(`Debugger for page ${pageId} and ${this.#name} disconnected.`);
|
|
352
337
|
this.#deviceEventReporter?.logDisconnection("debugger");
|
|
353
|
-
this.#
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
pageId: this.#mapToDevicePageId(pageId),
|
|
357
|
-
},
|
|
358
|
-
});
|
|
359
|
-
this.#debuggerConnection = null;
|
|
338
|
+
if (this.#debuggerConnection?.socket === socket) {
|
|
339
|
+
this.#terminateDebuggerConnection();
|
|
340
|
+
}
|
|
360
341
|
});
|
|
361
|
-
|
|
362
|
-
// $FlowFixMe[method-unbinding]
|
|
363
342
|
const sendFunc = socket.send;
|
|
364
|
-
// $FlowFixMe[cannot-write]
|
|
365
343
|
socket.send = function (message) {
|
|
366
344
|
debug("(Debugger) <- (Proxy) (Device): " + message);
|
|
367
345
|
return sendFunc.call(socket, message);
|
|
368
346
|
};
|
|
369
347
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
* 1. Checks if the same device is attempting to reconnect for the same app.
|
|
374
|
-
* 2. If not, close both the device and debugger socket.
|
|
375
|
-
* 3. If the debugger connection can be reused, close the device socket only.
|
|
376
|
-
*
|
|
377
|
-
* This allows users to reload the app, either as result of a crash, or manually
|
|
378
|
-
* reloading, without having to restart the debugger.
|
|
379
|
-
*/
|
|
380
|
-
handleDuplicateDeviceConnection(newDevice) {
|
|
381
|
-
if (
|
|
382
|
-
this.#app !== newDevice.getApp() ||
|
|
383
|
-
this.#name !== newDevice.getName()
|
|
384
|
-
) {
|
|
385
|
-
this.#deviceSocket.close();
|
|
386
|
-
this.#debuggerConnection?.socket.close();
|
|
348
|
+
#sendConnectEventToDevice(devicePageId) {
|
|
349
|
+
if (this.#connectedPageIds.has(devicePageId)) {
|
|
350
|
+
return;
|
|
387
351
|
}
|
|
388
|
-
|
|
389
|
-
this.#
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
);
|
|
352
|
+
this.#connectedPageIds.add(devicePageId);
|
|
353
|
+
this.#sendMessageToDevice({
|
|
354
|
+
event: "connect",
|
|
355
|
+
payload: {
|
|
356
|
+
pageId: devicePageId,
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
#sendDisconnectEventToDevice(devicePageId) {
|
|
361
|
+
if (!this.#connectedPageIds.has(devicePageId)) {
|
|
362
|
+
return;
|
|
400
363
|
}
|
|
364
|
+
this.#connectedPageIds.delete(devicePageId);
|
|
365
|
+
this.#sendMessageToDevice({
|
|
366
|
+
event: "disconnect",
|
|
367
|
+
payload: {
|
|
368
|
+
pageId: devicePageId,
|
|
369
|
+
},
|
|
370
|
+
});
|
|
401
371
|
}
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Returns `true` if a page supports the given target capability flag.
|
|
405
|
-
*/
|
|
406
372
|
#pageHasCapability(page, flag) {
|
|
407
373
|
return page.capabilities[flag] === true;
|
|
408
374
|
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Returns the synthetic "React Native Experimental (Improved Chrome Reloads)" page.
|
|
412
|
-
*/
|
|
413
375
|
#createSyntheticPage() {
|
|
414
376
|
return {
|
|
415
377
|
id: REACT_NATIVE_RELOADABLE_PAGE_ID,
|
|
@@ -419,14 +381,6 @@ class Device {
|
|
|
419
381
|
capabilities: {},
|
|
420
382
|
};
|
|
421
383
|
}
|
|
422
|
-
|
|
423
|
-
// Handles messages received from device:
|
|
424
|
-
// 1. For getPages responses updates local #pages list.
|
|
425
|
-
// 2. All other messages are forwarded to debugger as wrappedEvent.
|
|
426
|
-
//
|
|
427
|
-
// In the future more logic will be added to this method for modifying
|
|
428
|
-
// some of the messages (like updating messages with source maps and file
|
|
429
|
-
// locations).
|
|
430
384
|
async #handleMessageFromDevice(message) {
|
|
431
385
|
if (message.event === "getPages") {
|
|
432
386
|
this.#pages = new Map(
|
|
@@ -454,12 +408,6 @@ class Device {
|
|
|
454
408
|
)}`
|
|
455
409
|
);
|
|
456
410
|
}
|
|
457
|
-
|
|
458
|
-
// Check if device has a new legacy React Native page.
|
|
459
|
-
// There is usually no more than 2-3 pages per device so this operation
|
|
460
|
-
// is not expensive.
|
|
461
|
-
// TODO(hypuk): It is better for VM to send update event when new page is
|
|
462
|
-
// created instead of manually checking this on every getPages result.
|
|
463
411
|
for (const page of this.#pages.values()) {
|
|
464
412
|
if (this.#pageHasCapability(page, "nativePageReloads")) {
|
|
465
413
|
continue;
|
|
@@ -472,11 +420,7 @@ class Device {
|
|
|
472
420
|
}
|
|
473
421
|
}
|
|
474
422
|
} else if (message.event === "disconnect") {
|
|
475
|
-
// Device sends disconnect events only when page is reloaded or
|
|
476
|
-
// if debugger socket was disconnected.
|
|
477
423
|
const pageId = message.payload.pageId;
|
|
478
|
-
// TODO(moti): Handle null case explicitly, e.g. swallow disconnect events
|
|
479
|
-
// for unknown pages.
|
|
480
424
|
const page = this.#pages.get(pageId);
|
|
481
425
|
if (page != null && this.#pageHasCapability(page, "nativePageReloads")) {
|
|
482
426
|
return;
|
|
@@ -501,17 +445,11 @@ class Device {
|
|
|
501
445
|
if (this.#debuggerConnection == null) {
|
|
502
446
|
return;
|
|
503
447
|
}
|
|
504
|
-
|
|
505
|
-
// FIXME: Is it possible that we received message for pageID that does not
|
|
506
|
-
// correspond to current debugger connection?
|
|
507
|
-
// TODO(moti): yes, fix multi-debugger case
|
|
508
|
-
|
|
509
448
|
const debuggerSocket = this.#debuggerConnection.socket;
|
|
510
449
|
if (
|
|
511
450
|
debuggerSocket == null ||
|
|
512
451
|
debuggerSocket.readyState !== _ws.default.OPEN
|
|
513
452
|
) {
|
|
514
|
-
// TODO(hypuk): Send error back to device?
|
|
515
453
|
return;
|
|
516
454
|
}
|
|
517
455
|
const parsedPayload = JSON.parse(message.payload.wrappedEvent);
|
|
@@ -544,8 +482,6 @@ class Device {
|
|
|
544
482
|
}
|
|
545
483
|
}
|
|
546
484
|
}
|
|
547
|
-
|
|
548
|
-
// Sends single message to device.
|
|
549
485
|
#sendMessageToDevice(message) {
|
|
550
486
|
try {
|
|
551
487
|
if (message.event !== "getPages") {
|
|
@@ -554,43 +490,22 @@ class Device {
|
|
|
554
490
|
this.#deviceSocket.send(JSON.stringify(message));
|
|
555
491
|
} catch (error) {}
|
|
556
492
|
}
|
|
557
|
-
|
|
558
|
-
// We received new React Native Page ID.
|
|
559
493
|
#newLegacyReactNativePage(page) {
|
|
560
494
|
debug(`React Native page updated to ${page.id}`);
|
|
561
495
|
if (
|
|
562
496
|
this.#debuggerConnection == null ||
|
|
563
497
|
this.#debuggerConnection.pageId !== REACT_NATIVE_RELOADABLE_PAGE_ID
|
|
564
498
|
) {
|
|
565
|
-
// We can just remember new page ID without any further actions if no
|
|
566
|
-
// debugger is currently attached or attached debugger is not
|
|
567
|
-
// "Reloadable React Native" connection.
|
|
568
499
|
this.#lastConnectedLegacyReactNativePage = page;
|
|
569
500
|
return;
|
|
570
501
|
}
|
|
571
502
|
const oldPageId = this.#lastConnectedLegacyReactNativePage?.id;
|
|
572
503
|
this.#lastConnectedLegacyReactNativePage = page;
|
|
573
504
|
this.#isLegacyPageReloading = true;
|
|
574
|
-
|
|
575
|
-
// We already had a debugger connected to React Native page and a
|
|
576
|
-
// new one appeared - in this case we need to emulate execution context
|
|
577
|
-
// detroy and resend Debugger.enable and Runtime.enable commands to new
|
|
578
|
-
// page.
|
|
579
|
-
|
|
580
505
|
if (oldPageId != null) {
|
|
581
|
-
this.#
|
|
582
|
-
event: "disconnect",
|
|
583
|
-
payload: {
|
|
584
|
-
pageId: oldPageId,
|
|
585
|
-
},
|
|
586
|
-
});
|
|
506
|
+
this.#sendDisconnectEventToDevice(oldPageId);
|
|
587
507
|
}
|
|
588
|
-
this.#
|
|
589
|
-
event: "connect",
|
|
590
|
-
payload: {
|
|
591
|
-
pageId: page.id,
|
|
592
|
-
},
|
|
593
|
-
});
|
|
508
|
+
this.#sendConnectEventToDevice(page.id);
|
|
594
509
|
const toSend = [
|
|
595
510
|
{
|
|
596
511
|
method: "Runtime.enable",
|
|
@@ -617,15 +532,8 @@ class Device {
|
|
|
617
532
|
});
|
|
618
533
|
}
|
|
619
534
|
}
|
|
620
|
-
|
|
621
|
-
// Allows to make changes in incoming message from device.
|
|
622
535
|
async #processMessageFromDeviceLegacy(payload, debuggerInfo, pageId) {
|
|
623
|
-
// TODO(moti): Handle null case explicitly, or ideally associate a copy
|
|
624
|
-
// of the page metadata object with the connection so this can never be
|
|
625
|
-
// null.
|
|
626
536
|
const page = pageId != null ? this.#pages.get(pageId) : null;
|
|
627
|
-
|
|
628
|
-
// Replace Android addresses for scriptParsed event.
|
|
629
537
|
if (
|
|
630
538
|
(!page || !this.#pageHasCapability(page, "nativeSourceCodeFetching")) &&
|
|
631
539
|
payload.method === "Debugger.scriptParsed" &&
|
|
@@ -635,7 +543,6 @@ class Device {
|
|
|
635
543
|
if ("sourceMapURL" in params) {
|
|
636
544
|
for (const hostToRewrite of REWRITE_HOSTS_TO_LOCALHOST) {
|
|
637
545
|
if (params.sourceMapURL.includes(hostToRewrite)) {
|
|
638
|
-
// $FlowFixMe[cannot-write]
|
|
639
546
|
payload.params.sourceMapURL = params.sourceMapURL.replace(
|
|
640
547
|
hostToRewrite,
|
|
641
548
|
"localhost"
|
|
@@ -645,14 +552,8 @@ class Device {
|
|
|
645
552
|
}
|
|
646
553
|
const sourceMapURL = this.#tryParseHTTPURL(params.sourceMapURL);
|
|
647
554
|
if (sourceMapURL) {
|
|
648
|
-
// Some debug clients do not support fetching HTTP URLs. If the
|
|
649
|
-
// message headed to the debug client identifies the source map with
|
|
650
|
-
// an HTTP URL, fetch the content here and convert the content to a
|
|
651
|
-
// Data URL (which is more widely supported) before passing the
|
|
652
|
-
// message to the debug client.
|
|
653
555
|
try {
|
|
654
556
|
const sourceMap = await this.#fetchText(sourceMapURL);
|
|
655
|
-
// $FlowFixMe[cannot-write]
|
|
656
557
|
payload.params.sourceMapURL =
|
|
657
558
|
"data:application/json;charset=utf-8;base64," +
|
|
658
559
|
Buffer.from(sourceMap).toString("base64");
|
|
@@ -666,23 +567,14 @@ class Device {
|
|
|
666
567
|
if ("url" in params) {
|
|
667
568
|
for (const hostToRewrite of REWRITE_HOSTS_TO_LOCALHOST) {
|
|
668
569
|
if (params.url.includes(hostToRewrite)) {
|
|
669
|
-
// $FlowFixMe[cannot-write]
|
|
670
570
|
payload.params.url = params.url.replace(hostToRewrite, "localhost");
|
|
671
571
|
debuggerInfo.originalSourceURLAddress = hostToRewrite;
|
|
672
572
|
}
|
|
673
573
|
}
|
|
674
|
-
|
|
675
|
-
// Chrome doesn't download source maps if URL param is not a valid
|
|
676
|
-
// URL. Some frameworks pass alphanumeric script ID instead of URL which causes
|
|
677
|
-
// Chrome to not download source maps. In this case we want to prepend script ID
|
|
678
|
-
// with 'file://' prefix.
|
|
679
574
|
if (payload.params.url.match(/^[0-9a-z]+$/)) {
|
|
680
|
-
// $FlowFixMe[cannot-write]
|
|
681
575
|
payload.params.url = FILE_PREFIX + payload.params.url;
|
|
682
576
|
debuggerInfo.prependedFilePrefix = true;
|
|
683
577
|
}
|
|
684
|
-
|
|
685
|
-
// $FlowFixMe[prop-missing]
|
|
686
578
|
if (params.scriptId != null) {
|
|
687
579
|
this.#scriptIdToSourcePathMapping.set(params.scriptId, params.url);
|
|
688
580
|
}
|
|
@@ -692,22 +584,11 @@ class Device {
|
|
|
692
584
|
payload.method === "Runtime.executionContextCreated" &&
|
|
693
585
|
this.#isLegacyPageReloading
|
|
694
586
|
) {
|
|
695
|
-
// The new context is ready. First notify Chrome that we've reloaded so
|
|
696
|
-
// it'll resend its breakpoints. If we do this earlier, we may not be
|
|
697
|
-
// ready to receive them.
|
|
698
587
|
debuggerInfo.socket.send(
|
|
699
588
|
JSON.stringify({
|
|
700
589
|
method: "Runtime.executionContextsCleared",
|
|
701
590
|
})
|
|
702
591
|
);
|
|
703
|
-
|
|
704
|
-
// The VM starts in a paused mode. Ask it to resume.
|
|
705
|
-
// Note that if setting breakpoints in early initialization functions,
|
|
706
|
-
// there's a currently race condition between these functions executing
|
|
707
|
-
// and Chrome re-applying the breakpoints due to the message above.
|
|
708
|
-
//
|
|
709
|
-
// This is not an issue in VSCode/Nuclide where the IDE knows to resume
|
|
710
|
-
// at its convenience.
|
|
711
592
|
const resumeMessage = {
|
|
712
593
|
method: "Debugger.resume",
|
|
713
594
|
id: 0,
|
|
@@ -729,18 +610,11 @@ class Device {
|
|
|
729
610
|
this.#isLegacyPageReloading = false;
|
|
730
611
|
}
|
|
731
612
|
}
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
* Intercept an incoming message from a connected debugger. Returns either an
|
|
735
|
-
* original/replacement CDP message object, or `null` (will forward nothing
|
|
736
|
-
* to the target).
|
|
737
|
-
*/
|
|
738
613
|
#interceptClientMessageForSourceFetching(req, debuggerInfo, socket) {
|
|
739
614
|
switch (req.method) {
|
|
740
615
|
case "Debugger.setBreakpointByUrl":
|
|
741
616
|
return this.#processDebuggerSetBreakpointByUrl(req, debuggerInfo);
|
|
742
617
|
case "Debugger.getScriptSource":
|
|
743
|
-
// Sends response to debugger via side-effect
|
|
744
618
|
this.#processDebuggerGetScriptSource(req, socket);
|
|
745
619
|
return null;
|
|
746
620
|
default:
|
|
@@ -748,7 +622,6 @@ class Device {
|
|
|
748
622
|
}
|
|
749
623
|
}
|
|
750
624
|
#processDebuggerSetBreakpointByUrl(req, debuggerInfo) {
|
|
751
|
-
// If we replaced Android emulator's address to localhost we need to change it back.
|
|
752
625
|
if (debuggerInfo.originalSourceURLAddress != null) {
|
|
753
626
|
const processedReq = {
|
|
754
627
|
...req,
|
|
@@ -766,8 +639,6 @@ class Device {
|
|
|
766
639
|
processedReq.params.url.startsWith(FILE_PREFIX) &&
|
|
767
640
|
debuggerInfo.prependedFilePrefix
|
|
768
641
|
) {
|
|
769
|
-
// Remove fake URL prefix if we modified URL in #processMessageFromDeviceLegacy.
|
|
770
|
-
// $FlowFixMe[incompatible-use]
|
|
771
642
|
processedReq.params.url = processedReq.params.url.slice(
|
|
772
643
|
FILE_PREFIX.length
|
|
773
644
|
);
|
|
@@ -776,7 +647,6 @@ class Device {
|
|
|
776
647
|
if (processedReq.params.urlRegex != null) {
|
|
777
648
|
processedReq.params.urlRegex = processedReq.params.urlRegex.replace(
|
|
778
649
|
/localhost/g,
|
|
779
|
-
// $FlowFixMe[incompatible-call]
|
|
780
650
|
debuggerInfo.originalSourceURLAddress
|
|
781
651
|
);
|
|
782
652
|
}
|
|
@@ -801,8 +671,6 @@ class Device {
|
|
|
801
671
|
});
|
|
802
672
|
};
|
|
803
673
|
const sendErrorResponse = (error) => {
|
|
804
|
-
// Tell the client that the request failed
|
|
805
|
-
|
|
806
674
|
const response = {
|
|
807
675
|
id: req.id,
|
|
808
676
|
result: {
|
|
@@ -812,8 +680,6 @@ class Device {
|
|
|
812
680
|
},
|
|
813
681
|
};
|
|
814
682
|
socket.send(JSON.stringify(response));
|
|
815
|
-
|
|
816
|
-
// Send to the console as well, so the user can see it
|
|
817
683
|
this.#sendErrorToDebugger(error);
|
|
818
684
|
const pageId = this.#debuggerConnection?.pageId ?? null;
|
|
819
685
|
this.#deviceEventReporter?.logResponse(response, "proxy", {
|
|
@@ -874,18 +740,12 @@ class Device {
|
|
|
874
740
|
}
|
|
875
741
|
return parsedURL;
|
|
876
742
|
}
|
|
877
|
-
|
|
878
|
-
// Fetch text, raising an exception if the text could not be fetched,
|
|
879
|
-
// or is too large.
|
|
880
743
|
async #fetchText(url) {
|
|
881
|
-
// $FlowFixMe[incompatible-call] Suppress arvr node-fetch flow error
|
|
882
744
|
const response = await (0, _nodeFetch.default)(url);
|
|
883
745
|
if (!response.ok) {
|
|
884
746
|
throw new Error("HTTP " + response.status + " " + response.statusText);
|
|
885
747
|
}
|
|
886
748
|
const text = await response.text();
|
|
887
|
-
// Restrict the length to well below the 500MB limit for nodejs (leaving
|
|
888
|
-
// room some some later manipulation, e.g. base64 or wrapping in JSON)
|
|
889
749
|
if (text.length > 350000000) {
|
|
890
750
|
throw new Error("file too large to fetch via HTTP");
|
|
891
751
|
}
|
|
@@ -918,5 +778,8 @@ class Device {
|
|
|
918
778
|
}
|
|
919
779
|
return this.#pageHasCapability(page, "prefersFuseboxFrontend");
|
|
920
780
|
}
|
|
781
|
+
dangerouslyGetSocket() {
|
|
782
|
+
return this.#deviceSocket;
|
|
783
|
+
}
|
|
921
784
|
}
|
|
922
785
|
exports.default = Device;
|
|
@@ -29,6 +29,15 @@ declare export default class Device {
|
|
|
29
29
|
eventReporter: ?EventReporter,
|
|
30
30
|
createMessageMiddleware: ?CreateCustomMessageHandlerFn
|
|
31
31
|
): void;
|
|
32
|
+
dangerouslyRecreateDevice(
|
|
33
|
+
id: string,
|
|
34
|
+
name: string,
|
|
35
|
+
app: string,
|
|
36
|
+
socket: WS,
|
|
37
|
+
projectRoot: string,
|
|
38
|
+
eventReporter: ?EventReporter,
|
|
39
|
+
createMessageMiddleware: ?CreateCustomMessageHandlerFn
|
|
40
|
+
): void;
|
|
32
41
|
getName(): string;
|
|
33
42
|
getApp(): string;
|
|
34
43
|
getPagesList(): $ReadOnlyArray<Page>;
|
|
@@ -39,5 +48,5 @@ declare export default class Device {
|
|
|
39
48
|
userAgent: string | null,
|
|
40
49
|
}>
|
|
41
50
|
): void;
|
|
42
|
-
|
|
51
|
+
dangerouslyGetSocket(): WS;
|
|
43
52
|
}
|