microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.11.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/exponent/exponentPlatform.ts

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