microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
caa0dc5eb84731caee69a4d06915c6a3856ec289

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/rnDebugSession.ts

336lines · 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 * as vscode from "vscode";
5import * as path from "path";
6import * as mkdirp from "mkdirp";
7import { logger } from "vscode-debugadapter";
8import { DebugProtocol } from "vscode-debugprotocol";
9import { ProjectVersionHelper } from "../common/projectVersionHelper";
10import { TelemetryHelper } from "../common/telemetryHelper";
11import { MultipleLifetimesAppWorker } from "./appWorker";
12import { RnCDPMessageHandler } from "../cdp-proxy/CDPMessageHandlers/rnCDPMessageHandler";
13import {
14 DebugSessionBase,
15 DebugSessionStatus,
16 IAttachRequestArgs,
17 ILaunchRequestArgs,
18} from "./debugSessionBase";
19import { JsDebugConfigAdapter } from "./jsDebugConfigAdapter";
20import { ErrorHelper } from "../common/error/errorHelper";
21import { InternalErrorCode } from "../common/error/internalErrorCode";
22import * as nls from "vscode-nls";
23nls.config({
24 messageFormat: nls.MessageFormat.bundle,
25 bundleFormat: nls.BundleFormat.standalone,
26})();
27const localize = nls.loadMessageBundle();
28
29export class RNDebugSession extends DebugSessionBase {
30 private readonly terminateCommand: string;
31
32 private appWorker: MultipleLifetimesAppWorker | null;
33 private nodeSession: vscode.DebugSession | null;
34 private onDidStartDebugSessionHandler: vscode.Disposable;
35 private onDidTerminateDebugSessionHandler: vscode.Disposable;
36
37 constructor(session: vscode.DebugSession) {
38 super(session);
39
40 // constants definition
41 this.terminateCommand = "terminate"; // the "terminate" command is sent from the client to the debug adapter in order to give the debuggee a chance for terminating itself
42
43 // variables definition
44 this.appWorker = null;
45
46 this.onDidStartDebugSessionHandler = vscode.debug.onDidStartDebugSession(
47 this.handleStartDebugSession.bind(this),
48 );
49
50 this.onDidTerminateDebugSessionHandler = vscode.debug.onDidTerminateDebugSession(
51 this.handleTerminateDebugSession.bind(this),
52 );
53 }
54
55 protected async launchRequest(
56 response: DebugProtocol.LaunchResponse,
57 launchArgs: ILaunchRequestArgs,
58 // eslint-disable-next-line @typescript-eslint/no-unused-vars
59 request?: DebugProtocol.Request,
60 ): Promise<void> {
61 return new Promise<void>((resolve, reject) =>
62 this.initializeSettings(launchArgs)
63 .then(() => {
64 logger.log("Launching the application");
65 logger.verbose(
66 `Launching the application: ${JSON.stringify(launchArgs, null, 2)}`,
67 );
68
69 this.appLauncher
70 .launch(launchArgs)
71 .then(() => {
72 if (launchArgs.enableDebug) {
73 launchArgs.port =
74 launchArgs.port ||
75 this.appLauncher.getPackagerPort(launchArgs.cwd);
76 this.attachRequest(response, launchArgs)
77 .then(() => {
78 resolve();
79 })
80 .catch(e => reject(e));
81 } else {
82 this.sendResponse(response);
83 resolve();
84 }
85 })
86 .catch(err => {
87 reject(
88 ErrorHelper.getInternalError(
89 InternalErrorCode.ApplicationLaunchFailed,
90 err.message || err,
91 ),
92 );
93 });
94 })
95 .catch(err => {
96 reject(
97 ErrorHelper.getInternalError(
98 InternalErrorCode.ApplicationLaunchFailed,
99 err.message || err,
100 ),
101 );
102 }),
103 ).catch(err => this.showError(err, response));
104 }
105
106 protected async attachRequest(
107 response: DebugProtocol.AttachResponse,
108 attachArgs: IAttachRequestArgs,
109 // eslint-disable-next-line @typescript-eslint/no-unused-vars
110 request?: DebugProtocol.Request,
111 ): Promise<void> {
112 let extProps = {
113 platform: {
114 value: attachArgs.platform,
115 isPii: false,
116 },
117 };
118
119 this.previousAttachArgs = attachArgs;
120 return new Promise<void>((resolve, reject) =>
121 this.initializeSettings(attachArgs)
122 .then(() => {
123 logger.log("Attaching to the application");
124 logger.verbose(
125 `Attaching to the application: ${JSON.stringify(attachArgs, null, 2)}`,
126 );
127 return ProjectVersionHelper.getReactNativeVersions(
128 this.projectRootPath,
129 ProjectVersionHelper.generateAdditionalPackagesToCheckByPlatform(
130 attachArgs,
131 ),
132 );
133 })
134 .then(versions => {
135 extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
136 attachArgs,
137 versions,
138 extProps,
139 );
140
141 // eslint-disable-next-line @typescript-eslint/no-unused-vars
142 return TelemetryHelper.generate("attach", extProps, generator => {
143 attachArgs.port =
144 attachArgs.port || this.appLauncher.getPackagerPort(attachArgs.cwd);
145 return this.appLauncher
146 .getRnCdpProxy()
147 .stopServer()
148 .then(() =>
149 this.appLauncher
150 .getRnCdpProxy()
151 .initializeServer(
152 new RnCDPMessageHandler(),
153 this.cdpProxyLogLevel,
154 ),
155 )
156 .then(() => this.appLauncher.getPackager().start())
157 .then(() => {
158 logger.log(
159 localize(
160 "StartingDebuggerAppWorker",
161 "Starting debugger app worker.",
162 ),
163 );
164
165 const sourcesStoragePath = path.join(
166 this.projectRootPath,
167 ".vscode",
168 ".react",
169 );
170 // Create folder if not exist to avoid problems if
171 // RN project root is not a ${workspaceFolder}
172 mkdirp.sync(sourcesStoragePath);
173
174 // If launch is invoked first time, appWorker is undefined, so create it here
175 this.appWorker = new MultipleLifetimesAppWorker(
176 attachArgs,
177 sourcesStoragePath,
178 this.projectRootPath,
179 this.cancellationTokenSource.token,
180 undefined,
181 );
182 this.appLauncher.setAppWorker(this.appWorker);
183
184 this.appWorker.on("connected", (port: number) => {
185 if (
186 this.cancellationTokenSource.token.isCancellationRequested
187 ) {
188 return this.appWorker?.stop();
189 }
190
191 logger.log(
192 localize(
193 "DebuggerWorkerLoadedRuntimeOnPort",
194 "Debugger worker loaded runtime on port {0}",
195 port,
196 ),
197 );
198
199 this.appLauncher.getRnCdpProxy().setApplicationTargetPort(port);
200
201 if (
202 this.debugSessionStatus ===
203 DebugSessionStatus.ConnectionPending
204 ) {
205 return;
206 }
207
208 if (
209 this.debugSessionStatus ===
210 DebugSessionStatus.FirstConnection
211 ) {
212 this.debugSessionStatus =
213 DebugSessionStatus.FirstConnectionPending;
214 this.establishDebugSession(attachArgs, resolve);
215 } else if (
216 this.debugSessionStatus ===
217 DebugSessionStatus.ConnectionAllowed
218 ) {
219 if (this.nodeSession) {
220 this.debugSessionStatus =
221 DebugSessionStatus.ConnectionPending;
222 this.nodeSession.customRequest(this.terminateCommand);
223 }
224 }
225 });
226 if (this.cancellationTokenSource.token.isCancellationRequested) {
227 return this.appWorker.stop();
228 }
229 return this.appWorker.start();
230 });
231 });
232 })
233 .catch(err => {
234 reject(
235 ErrorHelper.getInternalError(
236 InternalErrorCode.CouldNotAttachToDebugger,
237 err.message || err,
238 ),
239 );
240 }),
241 ).catch(err => this.showError(err, response));
242 }
243
244 protected async disconnectRequest(
245 response: DebugProtocol.DisconnectResponse,
246 args: DebugProtocol.DisconnectArguments,
247 request?: DebugProtocol.Request,
248 ): Promise<void> {
249 // The client is about to disconnect so first we need to stop app worker
250 if (this.appWorker) {
251 this.appWorker.stop();
252 }
253
254 this.onDidStartDebugSessionHandler.dispose();
255 this.onDidTerminateDebugSessionHandler.dispose();
256
257 super.disconnectRequest(response, args, request);
258 }
259
260 protected establishDebugSession(
261 attachArgs: IAttachRequestArgs,
262 resolve?: (value?: void | PromiseLike<void> | undefined) => void,
263 ): void {
264 const attachConfiguration = JsDebugConfigAdapter.createDebuggingConfigForPureRN(
265 attachArgs,
266 this.appLauncher.getCdpProxyPort(),
267 this.session.id,
268 );
269
270 vscode.debug
271 .startDebugging(this.appLauncher.getWorkspaceFolder(), attachConfiguration, {
272 parentSession: this.session,
273 consoleMode: vscode.DebugConsoleMode.MergeWithParent,
274 })
275 .then(
276 (childDebugSessionStarted: boolean) => {
277 if (childDebugSessionStarted) {
278 this.debugSessionStatus = DebugSessionStatus.ConnectionDone;
279 this.setConnectionAllowedIfPossible();
280 if (resolve) {
281 this.debugSessionStatus = DebugSessionStatus.ConnectionAllowed;
282 resolve();
283 }
284 } else {
285 this.debugSessionStatus = DebugSessionStatus.ConnectionFailed;
286 this.setConnectionAllowedIfPossible();
287 this.resetFirstConnectionStatus();
288 throw new Error("Cannot start child debug session");
289 }
290 },
291 err => {
292 this.debugSessionStatus = DebugSessionStatus.ConnectionFailed;
293 this.setConnectionAllowedIfPossible();
294 this.resetFirstConnectionStatus();
295 throw err;
296 },
297 );
298 }
299
300 private handleStartDebugSession(debugSession: vscode.DebugSession) {
301 if (
302 debugSession.configuration.rnDebugSessionId === this.session.id &&
303 debugSession.type === this.pwaNodeSessionName
304 ) {
305 this.nodeSession = debugSession;
306 }
307 }
308
309 private handleTerminateDebugSession(debugSession: vscode.DebugSession) {
310 if (
311 debugSession.configuration.rnDebugSessionId === this.session.id &&
312 debugSession.type === this.pwaNodeSessionName
313 ) {
314 if (this.debugSessionStatus === DebugSessionStatus.ConnectionPending) {
315 this.establishDebugSession(this.previousAttachArgs);
316 } else {
317 vscode.commands.executeCommand(this.stopCommand, this.session);
318 }
319 }
320 }
321
322 private setConnectionAllowedIfPossible(): void {
323 if (
324 this.debugSessionStatus === DebugSessionStatus.ConnectionDone ||
325 this.debugSessionStatus === DebugSessionStatus.ConnectionFailed
326 ) {
327 this.debugSessionStatus = DebugSessionStatus.ConnectionAllowed;
328 }
329 }
330
331 private resetFirstConnectionStatus(): void {
332 if (this.debugSessionStatus === DebugSessionStatus.FirstConnectionPending) {
333 this.debugSessionStatus = DebugSessionStatus.FirstConnection;
334 }
335 }
336}