microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
dev/v-peq/stabilizeExperimentServiceTests

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/exponent/exponentPlatform.ts

212lines · 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 url from "url";
5import * as vscode from "vscode";
6import * as nls from "vscode-nls";
7import * as qrcode from "qrcode-terminal";
8import { ErrorHelper } from "../../common/error/errorHelper";
9import { InternalErrorCode } from "../../common/error/internalErrorCode";
10import { ExpoHostType, IExponentRunOptions, PlatformType } from "../launchArgs";
11import { GeneralPlatform, MobilePlatformDeps } from "../generalPlatform";
12import { TelemetryHelper } from "../../common/telemetryHelper";
13import { QRCodeContentProvider } from "../qrCodeContentProvider";
14import { ExponentHelper } from "./exponentHelper";
15
16import * as XDL from "./xdlInterface";
17
18nls.config({
19 messageFormat: nls.MessageFormat.bundle,
20 bundleFormat: nls.BundleFormat.standalone,
21})();
22const localize = nls.loadMessageBundle();
23
24let QRCodeUrl = "";
25
26export class ExponentPlatform extends GeneralPlatform {
27 private exponentTunnelPath: string | null;
28 private exponentHelper: ExponentHelper;
29 private qrCodeContentProvider: QRCodeContentProvider = new QRCodeContentProvider();
30 constructor(runOptions: IExponentRunOptions, platformDeps: MobilePlatformDeps = {}) {
31 super(runOptions, platformDeps);
32 this.exponentHelper = this.packager.getExponentHelper();
33 this.exponentTunnelPath = null;
34 }
35
36 public async runApp(): Promise<void> {
37 let extProps = {
38 platform: {
39 value: PlatformType.Exponent,
40 isPii: false,
41 },
42 };
43
44 extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
45 this.runOptions,
46 this.runOptions.reactNativeVersions,
47 extProps,
48 );
49
50 await TelemetryHelper.generate("ExponentPlatform.runApp", extProps, async () => {
51 await this.loginToExponentOrSkip(this.runOptions.expoHostType);
52 const port = this.packager.getPort();
53 await XDL.setOptions(this.projectPath, { packagerPort: port });
54 await XDL.startExponentServer(this.projectPath);
55
56 // the purpose of this is to save the same sequence of handling 'adb reverse' command execution as in Expo
57 // https://github.com/expo/expo-cli/blob/1d515d21200841e181518358fd9dc4c7b24c7cd6/packages/xdl/src/Project.ts#L2226-L2370
58 // we added this to be sure that our Expo launching logic doesn't have any negative side effects
59
60 if (this.runOptions.expoHostType === "tunnel") {
61 await this.prepareExpoTunnels();
62 } else {
63 await XDL.stopAdbReverse(this.projectPath);
64 }
65
66 const isAdbReversed =
67 this.runOptions.expoHostType !== "local"
68 ? false
69 : // we need to execute 'adb reverse' command to bind ports used by Expo and RN of local machine to ports of a connected Android device or a running emulator
70 await XDL.startAdbReverse(this.projectPath);
71 let exponentUrl = "";
72 switch (this.runOptions.expoHostType) {
73 case "lan":
74 exponentUrl = await XDL.getUrl(this.projectPath, {
75 dev: true,
76 minify: false,
77 hostType: "lan",
78 });
79 break;
80 case "local":
81 if (isAdbReversed) {
82 this.logger.info(
83 localize(
84 "ExpoStartAdbReverseSuccess",
85 "A device or an emulator was found, 'adb reverse' command successfully executed.",
86 ),
87 );
88 } else {
89 this.logger.warning(
90 localize(
91 "ExpoStartAdbReverseFailure",
92 "Adb reverse command failed. Couldn't find connected over usb device or running emulator. Also please make sure that there is only one currently connected device or running emulator.",
93 ),
94 );
95 }
96
97 exponentUrl = await XDL.getUrl(this.projectPath, {
98 dev: true,
99 minify: false,
100 hostType: "localhost",
101 });
102 break;
103 case "tunnel":
104 default:
105 exponentUrl = await XDL.getUrl(this.projectPath, { dev: true, minify: false });
106 }
107
108 // Make sure expo app url port is switched to customization
109 const urlString = url.parse(exponentUrl);
110 urlString.port = port.toString();
111 exponentUrl = `exp://${String(urlString.hostname)}:${String(urlString.port)}`;
112
113 if (!exponentUrl) {
114 throw ErrorHelper.getInternalError(InternalErrorCode.ExpectedExponentTunnelPath);
115 }
116
117 this.exponentTunnelPath = QRCodeUrl = exponentUrl;
118 const outputMessage = localize(
119 "ExponentServerIsRunningOpenToSeeIt",
120 "Expo server is running. Open your Expo app at {0} to see it.",
121 this.exponentTunnelPath,
122 );
123 this.logger.info(outputMessage);
124
125 if (this.runOptions.openExpoQR) {
126 const exponentPage = vscode.window.createWebviewPanel(
127 "Expo QR Code",
128 "Expo QR Code",
129 vscode.ViewColumn.Two,
130 {},
131 );
132 exponentPage.webview.html = this.qrCodeContentProvider.provideTextDocumentContent(
133 vscode.Uri.parse(exponentUrl),
134 );
135 const outputMessage = localize(
136 "QRCodeOutputInstructions",
137 "Scan below QR code to open your app:",
138 );
139 this.logger.info(outputMessage);
140 qrcode.generate(exponentUrl, { small: true }, (qrcode: string) =>
141 this.logger.info(`\n${qrcode}`),
142 );
143 }
144
145 const copyButton = localize("CopyToClipboard", "Copy to clipboard");
146
147 void vscode.window.showInformationMessage(outputMessage, copyButton).then(selection => {
148 if (selection === copyButton) {
149 void vscode.env.clipboard.writeText(exponentUrl);
150 }
151 });
152 });
153 }
154
155 public async loginToExponentOrSkip(expoHostType?: ExpoHostType): Promise<any> {
156 if (expoHostType !== "tunnel") {
157 return;
158 }
159
160 return await this.exponentHelper.loginToExponent(
161 async (message, password) =>
162 (await vscode.window.showInputBox({
163 placeHolder: message,
164 password,
165 })) || "",
166 async message => {
167 const okButton = { title: "Ok" };
168 const cancelButton = { title: "Cancel", isCloseAffordance: true };
169 const answer = await vscode.window.showInformationMessage(
170 message,
171 { modal: true },
172 okButton,
173 cancelButton,
174 );
175 if (answer === cancelButton) {
176 throw ErrorHelper.getInternalError(InternalErrorCode.UserCancelledExpoLogin);
177 }
178 return "";
179 },
180 );
181 }
182
183 public async beforeStartPackager(): Promise<void> {
184 return this.exponentHelper.configureExponentEnvironment();
185 }
186
187 public async enableJSDebuggingMode(): Promise<void> {
188 this.logger.info(
189 localize(
190 "ApplicationIsRunningOnExponentShakeDeviceForRemoteDebugging",
191 "Application is running on Expo. Please shake device and select 'Debug JS Remotely' to enable debugging.",
192 ),
193 );
194 }
195
196 public getRunArguments(): string[] {
197 return [];
198 }
199
200 private async prepareExpoTunnels(): Promise<void> {
201 try {
202 await this.exponentHelper.findOrInstallNgrokGlobally();
203 await XDL.startTunnels(this.projectPath);
204 } finally {
205 this.exponentHelper.removeNodeModulesPathFromEnvIfWasSet();
206 }
207 }
208}
209
210export function getQRCodeUrl() {
211 return QRCodeUrl;
212}
213