microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.9.3

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/debugSessionBase.ts

280lines · 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 path from "path";
5import * as vscode from "vscode";
6import { LoggingDebugSession, Logger, logger, ErrorDestination } from "vscode-debugadapter";
7import { DebugProtocol } from "vscode-debugprotocol";
8import * as nls from "vscode-nls";
9import { getLoggingDirectory, LogHelper, LogLevel } from "../extension/log/LogHelper";
10import { ReactNativeProjectHelper } from "../common/reactNativeProjectHelper";
11import { ErrorHelper } from "../common/error/errorHelper";
12import { InternalErrorCode } from "../common/error/internalErrorCode";
13import { InternalError, NestedError } from "../common/error/internalError";
14import { ILaunchArgs, IRunOptions, PlatformType } from "../extension/launchArgs";
15import { AppLauncher } from "../extension/appLauncher";
16import { RNPackageVersions } from "../common/projectVersionHelper";
17import { SettingsHelper } from "../extension/settingsHelper";
18import { OutputChannelLogger } from "../extension/log/OutputChannelLogger";
19import { RNSession } from "./debugSessionWrapper";
20
21nls.config({
22 messageFormat: nls.MessageFormat.bundle,
23 bundleFormat: nls.BundleFormat.standalone,
24})();
25const localize = nls.loadMessageBundle();
26
27/**
28 * Enum of possible statuses of debug session
29 */
30export enum DebugSessionStatus {
31 /** A session has been just created */
32 FirstConnection,
33 /** This status is required in order to exclude the possible creation of several debug sessions at the first start */
34 FirstConnectionPending,
35 /** This status means that an application can be reloaded */
36 ConnectionAllowed,
37 /** This status means that an application is reloading now, and we shouldn't terminate the current debug session */
38 ConnectionPending,
39 /** A debuggee connected successfully */
40 ConnectionDone,
41 /** A debuggee failed to connect */
42 ConnectionFailed,
43 /** The session is handling disconnect request now */
44 Stopping,
45 /** The session is stopped */
46 Stopped,
47}
48
49export interface TerminateEventArgs {
50 debugSession: vscode.DebugSession;
51 args: any;
52}
53
54export interface IAttachRequestArgs
55 extends DebugProtocol.AttachRequestArguments,
56 IRunOptions,
57 vscode.DebugConfiguration {
58 webkitRangeMax: number;
59 webkitRangeMin: number;
60 cwd: string /* Automatically set by VS Code to the currently opened folder */;
61 port: number;
62 url?: string;
63 useHermesEngine: boolean;
64 address?: string;
65 trace?: string;
66 skipFiles?: [];
67 sourceMaps?: boolean;
68 sourceMapPathOverrides?: { [key: string]: string };
69}
70
71export interface ILaunchRequestArgs
72 extends DebugProtocol.LaunchRequestArguments,
73 IAttachRequestArgs {}
74
75export abstract class DebugSessionBase extends LoggingDebugSession {
76 protected static rootSessionTerminatedEventEmitter: vscode.EventEmitter<TerminateEventArgs> =
77 new vscode.EventEmitter<TerminateEventArgs>();
78 public static readonly onDidTerminateRootDebugSession =
79 DebugSessionBase.rootSessionTerminatedEventEmitter.event;
80
81 protected readonly stopCommand: string;
82 protected readonly terminateCommand: string;
83 protected readonly pwaNodeSessionName: string;
84
85 protected appLauncher: AppLauncher;
86 protected projectRootPath: string;
87 protected isSettingsInitialized: boolean; // used to prevent parameters reinitialization when attach is called from launch function
88 protected previousAttachArgs: IAttachRequestArgs;
89 protected cdpProxyLogLevel: LogLevel;
90 protected debugSessionStatus: DebugSessionStatus;
91 protected nodeSession: vscode.DebugSession | null;
92 protected rnSession: RNSession;
93 protected vsCodeDebugSession: vscode.DebugSession;
94 protected cancellationTokenSource: vscode.CancellationTokenSource;
95
96 constructor(rnSession: RNSession) {
97 super();
98
99 // constants definition
100 this.pwaNodeSessionName = "pwa-node"; // the name of node debug session created by js-debug extension
101 this.stopCommand = "workbench.action.debug.stop"; // the command which simulates a click on the "Stop" button
102 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
103
104 // variables definition
105 this.rnSession = rnSession;
106 this.vsCodeDebugSession = rnSession.vsCodeDebugSession;
107 this.isSettingsInitialized = false;
108 this.debugSessionStatus = DebugSessionStatus.FirstConnection;
109 this.cancellationTokenSource = new vscode.CancellationTokenSource();
110 this.nodeSession = null;
111 }
112
113 protected initializeRequest(
114 response: DebugProtocol.InitializeResponse,
115 // eslint-disable-next-line @typescript-eslint/no-unused-vars
116 args: DebugProtocol.InitializeRequestArguments,
117 ): void {
118 response.body = response.body || {};
119
120 response.body.supportsConfigurationDoneRequest = true;
121 response.body.supportsEvaluateForHovers = true;
122 response.body.supportTerminateDebuggee = true;
123 response.body.supportsCancelRequest = true;
124
125 this.sendResponse(response);
126 }
127
128 protected abstract establishDebugSession(
129 attachArgs: IAttachRequestArgs,
130 resolve?: (value?: void | PromiseLike<void> | undefined) => void,
131 ): void;
132
133 protected async initializeSettings(args: any): Promise<void> {
134 if (!this.isSettingsInitialized) {
135 let chromeDebugCoreLogs = getLoggingDirectory();
136 if (chromeDebugCoreLogs) {
137 chromeDebugCoreLogs = path.join(chromeDebugCoreLogs, "DebugSessionLogs.txt");
138 }
139 let logLevel: string = args.trace;
140 if (logLevel) {
141 logLevel = logLevel.replace(logLevel[0], logLevel[0].toUpperCase());
142 logger.setup(Logger.LogLevel[logLevel], chromeDebugCoreLogs || false);
143 this.cdpProxyLogLevel =
144 LogLevel[logLevel] === LogLevel.Verbose ? LogLevel.Custom : LogLevel.None;
145 } else {
146 logger.setup(Logger.LogLevel.Log, chromeDebugCoreLogs || false);
147 this.cdpProxyLogLevel =
148 LogHelper.LOG_LEVEL === LogLevel.Trace ? LogLevel.Custom : LogLevel.None;
149 }
150
151 if (typeof args.sourceMaps !== "boolean") {
152 args.sourceMaps = true;
153 }
154
155 if (typeof args.enableDebug !== "boolean") {
156 args.enableDebug = true;
157 }
158
159 // Now there is a problem with processing time of 'createFromSourceMap' function of js-debug
160 // So we disable this functionality by default https://github.com/microsoft/vscode-js-debug/issues/1033
161 if (typeof args.sourceMapRenames !== "boolean") {
162 args.sourceMapRenames = false;
163 }
164
165 const projectRootPath = SettingsHelper.getReactNativeProjectRoot(args.cwd);
166 const isReactProject = await ReactNativeProjectHelper.isReactNativeProject(
167 projectRootPath,
168 );
169 if (!isReactProject) {
170 throw ErrorHelper.getInternalError(InternalErrorCode.NotInReactNativeFolderError);
171 }
172
173 const appLauncher = await AppLauncher.getOrCreateAppLauncherByProjectRootPath(
174 projectRootPath,
175 );
176 this.appLauncher = appLauncher;
177 this.projectRootPath = projectRootPath;
178 this.isSettingsInitialized = true;
179 this.appLauncher.getOrUpdateNodeModulesRoot(true);
180 if (this.vsCodeDebugSession.workspaceFolder) {
181 this.appLauncher.updateDebugConfigurationRoot(
182 this.vsCodeDebugSession.workspaceFolder.uri.fsPath,
183 );
184 }
185 }
186 }
187
188 protected async disconnectRequest(
189 response: DebugProtocol.DisconnectResponse,
190 args: DebugProtocol.DisconnectArguments,
191 // eslint-disable-next-line @typescript-eslint/no-unused-vars
192 request?: DebugProtocol.Request,
193 ): Promise<void> {
194 if (this.appLauncher) {
195 await this.appLauncher.getRnCdpProxy().stopServer();
196 }
197
198 this.cancellationTokenSource.cancel();
199 this.cancellationTokenSource.dispose();
200
201 // Then we tell the extension to stop monitoring the logcat, and then we disconnect the debugging session
202 if (this.previousAttachArgs && this.previousAttachArgs.platform === PlatformType.Android) {
203 try {
204 this.appLauncher.getMobilePlatform().dispose();
205 } catch (err) {
206 logger.warn(
207 localize(
208 "CouldNotStopMonitoringLogcat",
209 "Couldn't stop monitoring logcat: {0}",
210 err.message || err,
211 ),
212 );
213 }
214 }
215
216 this.debugSessionStatus = DebugSessionStatus.Stopped;
217 await logger.dispose();
218
219 DebugSessionBase.rootSessionTerminatedEventEmitter.fire({
220 debugSession: this.vsCodeDebugSession,
221 args: {
222 forcedStop: !!(<any>args).forcedStop,
223 },
224 });
225
226 this.sendResponse(response);
227 }
228
229 protected terminateWithErrorResponse(error: Error, response: DebugProtocol.Response): void {
230 // We can't print error messages after the debugging session is stopped. This could break the extension work.
231 if (
232 (error instanceof InternalError || error instanceof NestedError) &&
233 error.errorCode === InternalErrorCode.CancellationTokenTriggered
234 ) {
235 return;
236 }
237
238 logger.error(error.message);
239
240 this.sendErrorResponse(
241 response,
242 { format: error.message, id: 1 },
243 undefined,
244 undefined,
245 ErrorDestination.User,
246 );
247 }
248
249 protected async preparePackagerBeforeAttach(
250 args: IAttachRequestArgs,
251 reactNativeVersions: RNPackageVersions,
252 ): Promise<void> {
253 if (!(await this.appLauncher.getPackager().isRunning())) {
254 const runOptions: ILaunchArgs = Object.assign(
255 { reactNativeVersions },
256 this.appLauncher.prepareBaseRunOptions(args),
257 );
258 this.appLauncher.getPackager().setRunOptions(runOptions);
259 await this.appLauncher.getPackager().start();
260 }
261 }
262
263 protected showError(error: Error): void {
264 void vscode.window.showErrorMessage(error.message, {
265 modal: true,
266 });
267 // We can't print error messages via debug session logger after the session is stopped. This could break the extension work.
268 if (this.debugSessionStatus === DebugSessionStatus.Stopped) {
269 OutputChannelLogger.getMainChannel().error(error.message);
270 return;
271 }
272 logger.error(error.message);
273 }
274
275 protected async terminate(): Promise<void> {
276 await vscode.commands.executeCommand(this.stopCommand, undefined, {
277 sessionId: this.vsCodeDebugSession.id,
278 });
279 }
280}
281