microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
34472878f9e8d227bd5d0902161c571864c5d12d

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/rnDebugSession.ts

335lines · 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 attachArgs.cwd,
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 undefined,
180 );
181 this.appLauncher.setAppWorker(this.appWorker);
182
183 this.appWorker.on("connected", (port: number) => {
184 if (
185 this.cancellationTokenSource.token.isCancellationRequested
186 ) {
187 return this.appWorker?.stop();
188 }
189
190 logger.log(
191 localize(
192 "DebuggerWorkerLoadedRuntimeOnPort",
193 "Debugger worker loaded runtime on port {0}",
194 port,
195 ),
196 );
197
198 this.appLauncher.getRnCdpProxy().setApplicationTargetPort(port);
199
200 if (
201 this.debugSessionStatus ===
202 DebugSessionStatus.ConnectionPending
203 ) {
204 return;
205 }
206
207 if (
208 this.debugSessionStatus ===
209 DebugSessionStatus.FirstConnection
210 ) {
211 this.debugSessionStatus =
212 DebugSessionStatus.FirstConnectionPending;
213 this.establishDebugSession(attachArgs, resolve);
214 } else if (
215 this.debugSessionStatus ===
216 DebugSessionStatus.ConnectionAllowed
217 ) {
218 if (this.nodeSession) {
219 this.debugSessionStatus =
220 DebugSessionStatus.ConnectionPending;
221 this.nodeSession.customRequest(this.terminateCommand);
222 }
223 }
224 });
225 if (this.cancellationTokenSource.token.isCancellationRequested) {
226 return this.appWorker.stop();
227 }
228 return this.appWorker.start();
229 });
230 });
231 })
232 .catch(err => {
233 reject(
234 ErrorHelper.getInternalError(
235 InternalErrorCode.CouldNotAttachToDebugger,
236 err.message || err,
237 ),
238 );
239 }),
240 ).catch(err => this.showError(err, response));
241 }
242
243 protected async disconnectRequest(
244 response: DebugProtocol.DisconnectResponse,
245 args: DebugProtocol.DisconnectArguments,
246 request?: DebugProtocol.Request,
247 ): Promise<void> {
248 // The client is about to disconnect so first we need to stop app worker
249 if (this.appWorker) {
250 this.appWorker.stop();
251 }
252
253 this.onDidStartDebugSessionHandler.dispose();
254 this.onDidTerminateDebugSessionHandler.dispose();
255
256 super.disconnectRequest(response, args, request);
257 }
258
259 protected establishDebugSession(
260 attachArgs: IAttachRequestArgs,
261 resolve?: (value?: void | PromiseLike<void> | undefined) => void,
262 ): void {
263 const attachConfiguration = JsDebugConfigAdapter.createDebuggingConfigForPureRN(
264 attachArgs,
265 this.appLauncher.getCdpProxyPort(),
266 this.session.id,
267 );
268
269 vscode.debug
270 .startDebugging(this.appLauncher.getWorkspaceFolder(), attachConfiguration, {
271 parentSession: this.session,
272 consoleMode: vscode.DebugConsoleMode.MergeWithParent,
273 })
274 .then(
275 (childDebugSessionStarted: boolean) => {
276 if (childDebugSessionStarted) {
277 this.debugSessionStatus = DebugSessionStatus.ConnectionDone;
278 this.setConnectionAllowedIfPossible();
279 if (resolve) {
280 this.debugSessionStatus = DebugSessionStatus.ConnectionAllowed;
281 resolve();
282 }
283 } else {
284 this.debugSessionStatus = DebugSessionStatus.ConnectionFailed;
285 this.setConnectionAllowedIfPossible();
286 this.resetFirstConnectionStatus();
287 throw new Error("Cannot start child debug session");
288 }
289 },
290 err => {
291 this.debugSessionStatus = DebugSessionStatus.ConnectionFailed;
292 this.setConnectionAllowedIfPossible();
293 this.resetFirstConnectionStatus();
294 throw err;
295 },
296 );
297 }
298
299 private handleStartDebugSession(debugSession: vscode.DebugSession) {
300 if (
301 debugSession.configuration.rnDebugSessionId === this.session.id &&
302 debugSession.type === this.pwaNodeSessionName
303 ) {
304 this.nodeSession = debugSession;
305 }
306 }
307
308 private handleTerminateDebugSession(debugSession: vscode.DebugSession) {
309 if (
310 debugSession.configuration.rnDebugSessionId === this.session.id &&
311 debugSession.type === this.pwaNodeSessionName
312 ) {
313 if (this.debugSessionStatus === DebugSessionStatus.ConnectionPending) {
314 this.establishDebugSession(this.previousAttachArgs);
315 } else {
316 vscode.commands.executeCommand(this.stopCommand, this.session);
317 }
318 }
319 }
320
321 private setConnectionAllowedIfPossible(): void {
322 if (
323 this.debugSessionStatus === DebugSessionStatus.ConnectionDone ||
324 this.debugSessionStatus === DebugSessionStatus.ConnectionFailed
325 ) {
326 this.debugSessionStatus = DebugSessionStatus.ConnectionAllowed;
327 }
328 }
329
330 private resetFirstConnectionStatus(): void {
331 if (this.debugSessionStatus === DebugSessionStatus.FirstConnectionPending) {
332 this.debugSessionStatus = DebugSessionStatus.FirstConnection;
333 }
334 }
335}
336