microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fix-mocha-this-typing

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/direct/directDebugSession.ts

359lines · 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 { logger } from "@vscode/debugadapter";
6import { DebugProtocol } from "vscode-debugprotocol";
7import * as nls from "vscode-nls";
8import { ProjectVersionHelper } from "../../common/projectVersionHelper";
9import { TelemetryHelper } from "../../common/telemetryHelper";
10import { HermesCDPMessageHandler } from "../../cdp-proxy/CDPMessageHandlers/hermesCDPMessageHandler";
11import {
12 DebugSessionBase,
13 DebugSessionStatus,
14 IAttachRequestArgs,
15 ILaunchRequestArgs,
16} from "../debugSessionBase";
17import { JsDebugConfigAdapter } from "../jsDebugConfigAdapter";
18import { DebuggerEndpointHelper } from "../../cdp-proxy/debuggerEndpointHelper";
19import { ErrorHelper } from "../../common/error/errorHelper";
20import { InternalErrorCode } from "../../common/error/internalErrorCode";
21import { IOSDirectCDPMessageHandler } from "../../cdp-proxy/CDPMessageHandlers/iOSDirectCDPMessageHandler";
22import { PlatformType } from "../../extension/launchArgs";
23import { BaseCDPMessageHandler } from "../../cdp-proxy/CDPMessageHandlers/baseCDPMessageHandler";
24import { TipNotificationService } from "../../extension/services/tipsNotificationsService/tipsNotificationService";
25import { RNSession } from "../debugSessionWrapper";
26import { SettingsHelper } from "../../extension/settingsHelper";
27import { ReactNativeProjectHelper } from "../../common/reactNativeProjectHelper";
28import { IWDPHelper } from "./IWDPHelper";
29
30nls.config({
31 messageFormat: nls.MessageFormat.bundle,
32 bundleFormat: nls.BundleFormat.standalone,
33})();
34const localize = nls.loadMessageBundle();
35
36export class DirectDebugSession extends DebugSessionBase {
37 private debuggerEndpointHelper: DebuggerEndpointHelper;
38 private onDidTerminateDebugSessionHandler: vscode.Disposable;
39 private onDidStartDebugSessionHandler: vscode.Disposable;
40 private appTargetConnectionClosedHandlerDescriptor?: vscode.Disposable;
41 private attachSession: vscode.DebugSession | null;
42 private iOSWKDebugProxyHelper: IWDPHelper;
43
44 constructor(rnSession: RNSession) {
45 super(rnSession);
46 this.debuggerEndpointHelper = new DebuggerEndpointHelper();
47 this.iOSWKDebugProxyHelper = new IWDPHelper();
48 this.attachSession = null;
49
50 this.onDidTerminateDebugSessionHandler = vscode.debug.onDidTerminateDebugSession(
51 this.handleTerminateDebugSession.bind(this),
52 );
53
54 this.onDidStartDebugSessionHandler = vscode.debug.onDidStartDebugSession(
55 this.handleStartDebugSession.bind(this),
56 );
57 }
58
59 protected async launchRequest(
60 response: DebugProtocol.LaunchResponse,
61 launchArgs: ILaunchRequestArgs,
62 // eslint-disable-next-line @typescript-eslint/no-unused-vars
63 request?: DebugProtocol.Request,
64 ): Promise<void> {
65 let extProps = {
66 platform: {
67 value: launchArgs.platform,
68 isPii: false,
69 },
70 isDirect: {
71 value: true,
72 isPii: false,
73 },
74 };
75
76 void TipNotificationService.getInstance().setKnownDateForFeatureById(
77 "directDebuggingWithHermes",
78 );
79
80 try {
81 try {
82 if (launchArgs.platform != "exponent") {
83 await ReactNativeProjectHelper.verifyMetroConfigFile(launchArgs.cwd);
84 }
85 await this.initializeSettings(launchArgs);
86 logger.log("Launching the application");
87 logger.verbose(`Launching the application: ${JSON.stringify(launchArgs, null, 2)}`);
88
89 const versions = await ProjectVersionHelper.getReactNativeVersions(
90 this.projectRootPath,
91 ProjectVersionHelper.generateAdditionalPackagesToCheckByPlatform(launchArgs),
92 );
93 extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
94 launchArgs,
95 versions,
96 extProps,
97 );
98
99 // eslint-disable-next-line @typescript-eslint/no-unused-vars
100 await TelemetryHelper.generate("launch", extProps, generator =>
101 this.appLauncher.launch(launchArgs),
102 );
103
104 if (!launchArgs.enableDebug) {
105 this.sendResponse(response);
106 // if debugging is not enabled skip attach request
107 return;
108 }
109 } catch (error) {
110 throw ErrorHelper.getInternalError(
111 InternalErrorCode.ApplicationLaunchFailed,
112 (error as Error).message || error,
113 );
114 }
115 // if debugging is enabled start attach request
116 await this.vsCodeDebugSession.customRequest("attach", launchArgs);
117 this.sendResponse(response);
118 } catch (error) {
119 this.terminateWithErrorResponse(error as Error, response);
120 }
121 }
122
123 protected async attachRequest(
124 response: DebugProtocol.AttachResponse,
125 attachArgs: IAttachRequestArgs,
126 // eslint-disable-next-line @typescript-eslint/no-unused-vars
127 request?: DebugProtocol.Request,
128 ): Promise<void> {
129 let extProps = {
130 platform: {
131 value: attachArgs.platform,
132 isPii: false,
133 },
134 isDirect: {
135 value: true,
136 isPii: false,
137 },
138 };
139
140 attachArgs.webkitRangeMin = attachArgs.webkitRangeMin || 9223;
141 attachArgs.webkitRangeMax = attachArgs.webkitRangeMax || 9322;
142
143 this.previousAttachArgs = attachArgs;
144
145 try {
146 await this.initializeSettings(attachArgs);
147
148 const packager = this.appLauncher.getPackager();
149 const args: Parameters<typeof packager.forMessage> = [
150 // message indicates that another debugger has connected
151 "Already connected:",
152 {
153 type: "client_log",
154 level: "warn",
155 mode: "BRIDGE",
156 },
157 ];
158
159 void packager.forMessage(...args).then(
160 () => {
161 this.showError(
162 ErrorHelper.getInternalError(
163 InternalErrorCode.AnotherDebuggerConnectedToPackager,
164 ),
165 );
166 void this.terminate();
167 },
168 () => {},
169 );
170
171 logger.log("Attaching to the application");
172 logger.verbose(`Attaching to the application: ${JSON.stringify(attachArgs, null, 2)}`);
173
174 const versions = await ProjectVersionHelper.getReactNativeVersions(
175 this.projectRootPath,
176 ProjectVersionHelper.generateAdditionalPackagesToCheckByPlatform(attachArgs),
177 );
178
179 extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
180 attachArgs,
181 versions,
182 extProps,
183 );
184
185 // eslint-disable-next-line @typescript-eslint/no-unused-vars
186 await TelemetryHelper.generate("attach", extProps, async generator => {
187 const port = attachArgs.useHermesEngine
188 ? attachArgs.port || this.appLauncher.getPackagerPort(attachArgs.cwd)
189 : attachArgs.platform === PlatformType.iOS
190 ? attachArgs.port || IWDPHelper.iOS_WEBKIT_DEBUG_PROXY_DEFAULT_PORT
191 : null;
192 if (port === null) {
193 throw ErrorHelper.getInternalError(
194 InternalErrorCode.CouldNotDirectDebugWithoutHermesEngine,
195 attachArgs.platform,
196 );
197 }
198 attachArgs.port = port;
199 logger.log(`Connecting to ${attachArgs.port} port`);
200 await this.appLauncher.getRnCdpProxy().stopServer();
201
202 const cdpMessageHandler: BaseCDPMessageHandler | null = attachArgs.useHermesEngine
203 ? new HermesCDPMessageHandler()
204 : attachArgs.platform === PlatformType.iOS
205 ? new IOSDirectCDPMessageHandler()
206 : null;
207
208 if (!cdpMessageHandler) {
209 throw ErrorHelper.getInternalError(
210 InternalErrorCode.CouldNotDirectDebugWithoutHermesEngine,
211 attachArgs.platform,
212 );
213 }
214 await this.appLauncher
215 .getRnCdpProxy()
216 .initializeServer(
217 cdpMessageHandler,
218 this.cdpProxyLogLevel,
219 this.cancellationTokenSource.token,
220 );
221
222 if (!attachArgs.useHermesEngine && attachArgs.platform === PlatformType.iOS) {
223 await this.iOSWKDebugProxyHelper.startiOSWebkitDebugProxy(
224 attachArgs.port,
225 attachArgs.webkitRangeMin,
226 attachArgs.webkitRangeMax,
227 );
228 const results = await this.iOSWKDebugProxyHelper.getSimulatorProxyPort(
229 attachArgs,
230 );
231 attachArgs.port = results.targetPort;
232 }
233
234 if (attachArgs.request === "attach") {
235 await this.preparePackagerBeforeAttach(attachArgs, versions);
236 }
237
238 this.appTargetConnectionClosedHandlerDescriptor = this.appLauncher
239 .getRnCdpProxy()
240 .onApplicationTargetConnectionClosed(() => {
241 if (this.attachSession) {
242 if (
243 this.debugSessionStatus !== DebugSessionStatus.Stopping &&
244 this.debugSessionStatus !== DebugSessionStatus.Stopped
245 ) {
246 void this.terminate();
247 }
248 this.appTargetConnectionClosedHandlerDescriptor?.dispose();
249 }
250 });
251
252 const settingsPorts = SettingsHelper.getPackagerPort(attachArgs.cwd);
253 const browserInspectUri = await this.debuggerEndpointHelper.retryGetWSEndpoint(
254 `http://localhost:${attachArgs.port}`,
255 90,
256 this.cancellationTokenSource.token,
257 attachArgs.useHermesEngine,
258 settingsPorts,
259 );
260
261 // Make sure expo app is using correct ws endpoint url
262 const debuggerType = await this.debuggerEndpointHelper.getDebuggerTpye(
263 `http://localhost:${attachArgs.port}`,
264 );
265 if (debuggerType == "expo") {
266 const expoBrowserInspectUri = `${browserInspectUri.split("&")[0]}&page=2`;
267 this.appLauncher.getRnCdpProxy().setBrowserInspectUri(expoBrowserInspectUri);
268 } else {
269 this.appLauncher.getRnCdpProxy().setBrowserInspectUri(browserInspectUri);
270 }
271
272 await this.establishDebugSession(attachArgs);
273 });
274 this.sendResponse(response);
275 } catch (error) {
276 this.terminateWithErrorResponse(
277 ErrorHelper.getInternalError(
278 InternalErrorCode.CouldNotAttachToDebugger,
279 (error as Error).message || error,
280 ),
281 response,
282 );
283 }
284 }
285
286 protected async disconnectRequest(
287 response: DebugProtocol.DisconnectResponse,
288 args: DebugProtocol.DisconnectArguments,
289 request?: DebugProtocol.Request,
290 ): Promise<void> {
291 this.debugSessionStatus = DebugSessionStatus.Stopping;
292
293 // Stop packager when using expo-cli, to avoid launch conflicts in the next launch request
294 if (this.appLauncher.getPackager().getPlatform() == "exponent") {
295 await this.appLauncher.getPackager().stop();
296 }
297
298 this.iOSWKDebugProxyHelper.cleanUp();
299 this.onDidTerminateDebugSessionHandler.dispose();
300 this.onDidStartDebugSessionHandler.dispose();
301 this.appLauncher.getPackager().closeWsConnection();
302 this.appTargetConnectionClosedHandlerDescriptor?.dispose();
303 return super.disconnectRequest(response, args, request);
304 }
305
306 protected async establishDebugSession(attachArgs: IAttachRequestArgs): Promise<void> {
307 const attachConfiguration = await JsDebugConfigAdapter.createDebuggingConfigForRNHermes(
308 attachArgs,
309 this.appLauncher.getCdpProxyPort(),
310 this.rnSession.sessionId,
311 );
312
313 const childDebugSessionStarted = await vscode.debug.startDebugging(
314 this.appLauncher.getWorkspaceFolder(),
315 attachConfiguration,
316 {
317 parentSession: this.vsCodeDebugSession,
318 consoleMode: vscode.DebugConsoleMode.MergeWithParent,
319 },
320 );
321 if (!childDebugSessionStarted) {
322 throw new Error(
323 localize("CouldNotStartChildDebugSession", "Couldn't start child debug session"),
324 );
325 }
326 }
327
328 private handleTerminateDebugSession(debugSession: vscode.DebugSession): void {
329 if (
330 debugSession.configuration.rnDebugSessionId === this.rnSession.sessionId &&
331 debugSession.type === this.pwaNodeSessionName
332 ) {
333 void this.terminate();
334 }
335 }
336
337 private handleStartDebugSession(debugSession: vscode.DebugSession): void {
338 if (
339 this.nodeSession &&
340 (debugSession as any).parentSession &&
341 this.nodeSession.id === (debugSession as any).parentSession.id
342 ) {
343 this.attachSession = debugSession;
344 }
345 if (
346 debugSession.configuration.rnDebugSessionId === this.rnSession.sessionId &&
347 debugSession.type === this.pwaNodeSessionName
348 ) {
349 this.nodeSession = debugSession;
350 }
351 }
352
353 protected async initializeSettings(args: any): Promise<any> {
354 await super.initializeSettings(args);
355 if (args.useHermesEngine === undefined) {
356 args.useHermesEngine = true;
357 }
358 }
359}
360