microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
6e491635e08c261310d93f18da08074c210b3da2

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/cdp-proxy/reactNativeCDPProxy.ts

139lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import {
5 Connection,
6 Server,
7 WebSocketTransport,
8 IProtocolCommand,
9 IProtocolError,
10 IProtocolSuccess
11} from "vscode-cdp-proxy";
12import { IncomingMessage } from "http";
13import { OutputChannelLogger } from "../extension/log/OutputChannelLogger";
14import { LogLevel } from "../extension/log/LogHelper";
15import { DebuggerEndpointHelper } from "./debuggerEndpointHelper";
16
17export class ReactNativeCDPProxy {
18
19 private readonly PROXY_LOG_TAGS = {
20 DEBUGGER_COMMAND: "Command Debugger To Target",
21 APPLICATION_COMMAND: "Command Target To Debugger",
22 DEBUGGER_REPLY: "Reply From Debugger To Target",
23 APPLICATION_REPLY: "Reply From Target To Debugger",
24 };
25
26 private server: Server | null;
27 private hostAddress: string;
28 private port: number;
29 private debuggerTarget: Connection;
30 private applicationTarget: Connection;
31 private logger: OutputChannelLogger;
32 private logLevel: LogLevel;
33 private firstStop: boolean;
34 private debuggerEndpointHelper: DebuggerEndpointHelper;
35 private applicationTargetPort: number;
36
37 constructor(hostAddress: string, port: number, logLevel: LogLevel) {
38 this.port = port;
39 this.hostAddress = hostAddress;
40 this.logger = OutputChannelLogger.getChannel("React Native Chrome Proxy", true, false, true);
41 this.logLevel = logLevel;
42 this.firstStop = true;
43 this.debuggerEndpointHelper = new DebuggerEndpointHelper();
44 }
45
46 public createServer(): Promise<void> {
47 return Server.create({ port: this.port, host: this.hostAddress })
48 .then((server: Server) => {
49 this.server = server;
50 this.server.onConnection(this.onConnectionHandler.bind(this));
51 });
52 }
53
54 public stopServer(): void {
55 if (this.server) {
56 this.server.dispose();
57 this.server = null;
58 }
59 }
60
61 public setApplicationTargetPort(applicationTargetPort: number): void {
62 this.applicationTargetPort = applicationTargetPort;
63 }
64
65 private async onConnectionHandler([debuggerTarget, request]: [Connection, IncomingMessage]): Promise<void> {
66 this.debuggerTarget = debuggerTarget;
67
68 this.debuggerTarget.pause(); // don't listen for events until the target is ready
69
70 const browserInspectUri = await this.debuggerEndpointHelper.getWSEndpoint(`http://localhost:${this.applicationTargetPort}`);
71
72 this.applicationTarget = new Connection(await WebSocketTransport.create(browserInspectUri));
73
74 this.applicationTarget.onError(this.onApplicationTargetError.bind(this));
75 this.debuggerTarget.onError(this.onDebuggerTargetError.bind(this));
76
77 this.applicationTarget.onCommand(this.handleApplicationTargetCommand.bind(this));
78 this.debuggerTarget.onCommand(this.handleDebuggerTargetCommand.bind(this));
79
80 this.applicationTarget.onReply(this.handleApplicationTargetReply.bind(this));
81 this.debuggerTarget.onReply(this.handleDebuggerTargetReply.bind(this));
82
83 this.debuggerTarget.onEnd(this.onDebuggerTargetClosed.bind(this));
84
85 // dequeue any messages we got in the meantime
86 this.debuggerTarget.unpause();
87 }
88
89 private handleDebuggerTargetCommand(evt: IProtocolCommand) {
90 this.logger.logWithCustomTag(this.PROXY_LOG_TAGS.DEBUGGER_COMMAND, JSON.stringify(evt, null , 2), this.logLevel);
91 this.applicationTarget.send(evt);
92 }
93
94 private handleApplicationTargetCommand(evt: IProtocolCommand) {
95 if (evt.method === "Debugger.paused" && this.firstStop) {
96 evt.params = this.handleAppBundleFirstPauseEvent(evt);
97 }
98 this.logger.logWithCustomTag(this.PROXY_LOG_TAGS.APPLICATION_COMMAND, JSON.stringify(evt, null , 2), this.logLevel);
99 this.debuggerTarget.send(evt);
100 }
101
102 /** Since the bundle runs inside the Node.js VM in `debuggerWorker.js` in runtime
103 * Node debug adapter need time to parse new added code source maps
104 * So we added `debugger;` statement at the start of the bundle code
105 * and wait for the adapter to receive a signal to stop on that statement
106 * and then change pause reason to `Break on start` so js-debug can process all breakpoints in the bundle and
107 * continue the code execution using `continueOnAttach` flag
108 */
109 private handleAppBundleFirstPauseEvent(evt: IProtocolCommand): any {
110 let params: any = evt.params;
111 if (params.reason && params.reason === "other") {
112 this.firstStop = false;
113 params.reason = "Break on start";
114 }
115 return params;
116 }
117
118 private handleDebuggerTargetReply(evt: IProtocolError | IProtocolSuccess) {
119 this.logger.logWithCustomTag(this.PROXY_LOG_TAGS.DEBUGGER_REPLY, JSON.stringify(evt, null , 2), this.logLevel);
120 this.applicationTarget.send(evt);
121 }
122
123 private handleApplicationTargetReply(evt: IProtocolError | IProtocolSuccess) {
124 this.logger.logWithCustomTag(this.PROXY_LOG_TAGS.APPLICATION_REPLY, JSON.stringify(evt, null , 2), this.logLevel);
125 this.debuggerTarget.send(evt);
126 }
127
128 private onDebuggerTargetError(err: Error) {
129 this.logger.error("Error on debugger transport", err);
130 }
131
132 private onApplicationTargetError(err: Error) {
133 this.logger.error("Error on application transport", err);
134 }
135
136 private async onDebuggerTargetClosed() {
137 this.firstStop = true;
138 }
139}
140