microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.10.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commandPaletteHandler.ts

409lines · modeblame

bef522ffMeena Kunnathur Balakrishnan10 years ago1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
6e179583digeff10 years ago4import * as vscode from "vscode";
5import * as Q from "q";
0a68f8dbArtem Egorov8 years ago6import * as XDL from "./exponent/xdlInterface";
df4bce40digeff10 years ago7import {SettingsHelper} from "./settingsHelper";
0a68f8dbArtem Egorov8 years ago8import {OutputChannelLogger} from "./log/OutputChannelLogger";
4787ec09Artem Egorov7 years ago9import {Packager} from "../common/packager";
db6fd42aRuslan Bikkinin7 years ago10import {TargetType, GeneralMobilePlatform} from "./generalMobilePlatform";
0a68f8dbArtem Egorov8 years ago11import {AndroidPlatform} from "./android/androidPlatform";
12import {IOSPlatform} from "./ios/iOSPlatform";
b0061ac6Meena Kunnathur Balakrishnan10 years ago13import {ReactNativeProjectHelper} from "../common/reactNativeProjectHelper";
aeaf46bcMeena Kunnathur Balakrishnan10 years ago14import {TargetPlatformHelper} from "../common/targetPlatformHelper";
d976d077Meena Kunnathur Balakrishnan10 years ago15import {TelemetryHelper} from "../common/telemetryHelper";
0a68f8dbArtem Egorov8 years ago16import {ExponentHelper} from "./exponent/exponentHelper";
4edcda70Artem Egorov8 years ago17import {ReactDirManager} from "./reactDirManager";
18import {ExtensionServer} from "./extensionServer";
d1fc7f8aArtem Egorov8 years ago19import {IAndroidRunOptions, IIOSRunOptions} from "./launchArgs";
d8b69cd3Yuri Skorokhodov7 years ago20import {ExponentPlatform} from "./exponent/exponentPlatform";
6277aa08Yuri Skorokhodov7 years ago21import {spawn, ChildProcess} from "child_process";
22import {HostPlatform} from "../common/hostPlatform";
d7d405aeYuri Skorokhodov7 years ago23import * as nls from "vscode-nls";
24import { ErrorHelper } from "../common/error/errorHelper";
25import { InternalErrorCode } from "../common/error/internalErrorCode";
26const localize = nls.loadMessageBundle();
4b37483dmax-mironov8 years ago27
4edcda70Artem Egorov8 years ago28interface IReactNativeStuff {
29packager: Packager;
2e432a9eArtem Egorov8 years ago30exponentHelper: ExponentHelper;
4edcda70Artem Egorov8 years ago31reactDirManager: ReactDirManager;
32extensionServer: ExtensionServer;
33}
640e6e98max-mironov8 years ago34
4edcda70Artem Egorov8 years ago35interface IReactNativeProject extends IReactNativeStuff {
2e432a9eArtem Egorov8 years ago36workspaceFolder: vscode.WorkspaceFolder;
37}
38
e1c05e69dlebu10 years ago39export class CommandPaletteHandler {
a740b79aYuri Skorokhodov7 years ago40public static elementInspector: ChildProcess | null;
4edcda70Artem Egorov8 years ago41private static projectsCache: {[key: string]: IReactNativeProject} = {};
2e432a9eArtem Egorov8 years ago42private static logger: OutputChannelLogger = OutputChannelLogger.getMainChannel();
6a465861max-mironov8 years ago43
4edcda70Artem Egorov8 years ago44public static addFolder(workspaceFolder: vscode.WorkspaceFolder, stuff: IReactNativeStuff): void {
db6fd42aRuslan Bikkinin7 years ago45this.logger.debug(`Command palette: added folder ${workspaceFolder.uri.fsPath}`);
4edcda70Artem Egorov8 years ago46this.projectsCache[workspaceFolder.uri.fsPath] = {
47...stuff,
2e432a9eArtem Egorov8 years ago48workspaceFolder,
49};
50}
bef522ffMeena Kunnathur Balakrishnan10 years ago51
4edcda70Artem Egorov8 years ago52public static getFolder(workspaceFolder: vscode.WorkspaceFolder): IReactNativeProject {
53return this.projectsCache[workspaceFolder.uri.fsPath];
2e432a9eArtem Egorov8 years ago54}
55
4edcda70Artem Egorov8 years ago56public static delFolder(workspaceFolder: vscode.WorkspaceFolder): void {
57delete this.projectsCache[workspaceFolder.uri.fsPath];
bef522ffMeena Kunnathur Balakrishnan10 years ago58}
59
0904a0d7Meena Kunnathur Balakrishnan10 years ago60/**
61* Starts the React Native packager
62*/
2e432a9eArtem Egorov8 years ago63public static startPackager(): Q.Promise<void> {
64return this.selectProject()
65.then((project: IReactNativeProject) => {
4787ec09Artem Egorov7 years ago66return this.executeCommandInContext("startPackager", project.workspaceFolder, () => {
67return project.packager.isRunning()
2e432a9eArtem Egorov8 years ago68.then((running) => {
4edcda70Artem Egorov8 years ago69return running ? project.packager.stop() : Q.resolve(void 0);
4787ec09Artem Egorov7 years ago70});
71})
72.then(() => project.packager.start());
2e432a9eArtem Egorov8 years ago73});
bef522ffMeena Kunnathur Balakrishnan10 years ago74}
75
0904a0d7Meena Kunnathur Balakrishnan10 years ago76/**
77* Kills the React Native packager invoked by the extension's packager
78*/
2e432a9eArtem Egorov8 years ago79public static stopPackager(): Q.Promise<void> {
80return this.selectProject()
81.then((project: IReactNativeProject) => {
4787ec09Artem Egorov7 years ago82return this.executeCommandInContext("stopPackager", project.workspaceFolder, () => project.packager.stop());
2e432a9eArtem Egorov8 years ago83});
3194e9afMeena Kunnathur Balakrishnan10 years ago84}
85
4edcda70Artem Egorov8 years ago86public static stopAllPackagers(): Q.Promise<void> {
87let keys = Object.keys(this.projectsCache);
88let promises: Q.Promise<void>[] = [];
89keys.forEach((key) => {
90let project = this.projectsCache[key];
4787ec09Artem Egorov7 years ago91promises.push(this.executeCommandInContext("stopPackager", project.workspaceFolder, () => project.packager.stop()));
4edcda70Artem Egorov8 years ago92});
93
94return Q.all(promises).then(() => {});
95}
96
f2a58eefBret Johnson9 years ago97/**
98* Restarts the React Native packager
99*/
2e432a9eArtem Egorov8 years ago100public static restartPackager(): Q.Promise<void> {
101return this.selectProject()
102.then((project: IReactNativeProject) => {
103return this.executeCommandInContext("restartPackager", project.workspaceFolder, () =>
104this.runRestartPackagerCommandAndUpdateStatus(project));
105});
f2a58eefBret Johnson9 years ago106}
107
7893fb7eJimmy Thomson9 years ago108/**
109* Execute command to publish to exponent host.
110*/
2e432a9eArtem Egorov8 years ago111public static publishToExpHost(): Q.Promise<void> {
112return this.selectProject()
113.then((project: IReactNativeProject) => {
114return this.executeCommandInContext("publishToExpHost", project.workspaceFolder, () => {
115return this.executePublishToExpHost(project).then((didPublish) => {
116if (!didPublish) {
d7d405aeYuri Skorokhodov7 years ago117CommandPaletteHandler.logger.warning(localize("ExponentPublishingWasUnsuccessfulMakeSureYoureLoggedInToExpo", "Publishing was unsuccessful. Please make sure you are logged in Expo and your project is a valid Expo project"));
2e432a9eArtem Egorov8 years ago118}
119});
120});
7893fb7eJimmy Thomson9 years ago121});
122}
123
3194e9afMeena Kunnathur Balakrishnan10 years ago124/**
125* Executes the 'react-native run-android' command
126*/
d1fc7f8aArtem Egorov8 years ago127public static runAndroid(target: TargetType = "simulator"): Q.Promise<void> {
2e432a9eArtem Egorov8 years ago128return this.selectProject()
129.then((project: IReactNativeProject) => {
0611729dRuslan Bikkinin7 years ago130TargetPlatformHelper.checkTargetPlatformSupport("android");
4787ec09Artem Egorov7 years ago131return this.executeCommandInContext("runAndroid", project.workspaceFolder, () => {
db6fd42aRuslan Bikkinin7 years ago132const platform = <AndroidPlatform>this.createPlatform(project, "android", AndroidPlatform, target);
4787ec09Artem Egorov7 years ago133return platform.beforeStartPackager()
134.then(() => {
135return platform.startPackager();
136})
137.then(() => {
138return platform.runApp(/*shouldLaunchInAllDevices*/true);
139})
2e432a9eArtem Egorov8 years ago140.then(() => {
141return platform.disableJSDebuggingMode();
142});
4787ec09Artem Egorov7 years ago143});
0a68f8dbArtem Egorov8 years ago144});
3194e9afMeena Kunnathur Balakrishnan10 years ago145}
146
147/**
148* Executes the 'react-native run-ios' command
149*/
d1fc7f8aArtem Egorov8 years ago150public static runIos(target: TargetType = "simulator"): Q.Promise<void> {
2e432a9eArtem Egorov8 years ago151return this.selectProject()
152.then((project: IReactNativeProject) => {
0611729dRuslan Bikkinin7 years ago153TargetPlatformHelper.checkTargetPlatformSupport("ios");
4787ec09Artem Egorov7 years ago154return this.executeCommandInContext("runIos", project.workspaceFolder, () => {
db6fd42aRuslan Bikkinin7 years ago155const platform = <IOSPlatform>this.createPlatform(project, "ios", IOSPlatform, target);
4787ec09Artem Egorov7 years ago156return platform.beforeStartPackager()
157.then(() => {
158return platform.startPackager();
159})
160.then(() => {
161// Set the Debugging setting to disabled, because in iOS it's persisted across runs of the app
162return platform.disableJSDebuggingMode();
163})
2e432a9eArtem Egorov8 years ago164.catch(() => { }) // If setting the debugging mode fails, we ignore the error and we run the run ios command anyways
165.then(() => {
166return platform.runApp();
167});
4787ec09Artem Egorov7 years ago168});
169});
170}
171
172/**
173* Starts the Exponent packager
174*/
175public static runExponent(): Q.Promise<void> {
176return this.selectProject()
177.then((project: IReactNativeProject) => {
178return this.loginToExponent(project)
179.then(() => {
031832ffArtem Egorov7 years ago180return this.executeCommandInContext("runExponent", project.workspaceFolder, () => {
db6fd42aRuslan Bikkinin7 years ago181const platform = <ExponentPlatform>this.createPlatform(project, "exponent", ExponentPlatform);
4787ec09Artem Egorov7 years ago182return platform.beforeStartPackager()
183.then(() => {
184return platform.startPackager();
185})
186.then(() => {
187return platform.runApp();
188});
189});
190});
2e432a9eArtem Egorov8 years ago191});
3194e9afMeena Kunnathur Balakrishnan10 years ago192}
193
2e432a9eArtem Egorov8 years ago194public static showDevMenu(): Q.Promise<void> {
a41f5c68Artem Egorov8 years ago195return this.selectProject()
196.then((project: IReactNativeProject) => {
db6fd42aRuslan Bikkinin7 years ago197const androidPlatform = <AndroidPlatform>this.createPlatform(project, "android", AndroidPlatform);
198androidPlatform.showDevMenu()
a41f5c68Artem Egorov8 years ago199.catch(() => { }); // Ignore any errors
2ecfbd20Yuri Skorokhodov7 years ago200
201if (process.platform === "darwin") {
202const iosPlatform = <IOSPlatform>this.createPlatform(project, "ios", IOSPlatform);
203iosPlatform.showDevMenu()
204.catch(() => { }); // Ignore any errors
205}
a41f5c68Artem Egorov8 years ago206return Q.resolve(void 0);
207});
7daed3fcArtem Egorov8 years ago208}
209
2e432a9eArtem Egorov8 years ago210public static reloadApp(): Q.Promise<void> {
a41f5c68Artem Egorov8 years ago211return this.selectProject()
212.then((project: IReactNativeProject) => {
db6fd42aRuslan Bikkinin7 years ago213const androidPlatform = <AndroidPlatform>this.createPlatform(project, "android", AndroidPlatform);
214androidPlatform.reloadApp()
a41f5c68Artem Egorov8 years ago215.catch(() => { }); // Ignore any errors
2ecfbd20Yuri Skorokhodov7 years ago216
217if (process.platform === "darwin") {
218const iosPlatform = <IOSPlatform>this.createPlatform(project, "ios", IOSPlatform);
219iosPlatform.reloadApp()
220.catch(() => { }); // Ignore any errors
221}
a41f5c68Artem Egorov8 years ago222return Q.resolve(void 0);
223});
7daed3fcArtem Egorov8 years ago224}
225
14fc3172Artem Egorov7 years ago226public static runElementInspector(): Q.Promise<void> {
227if (!CommandPaletteHandler.elementInspector) {
6277aa08Yuri Skorokhodov7 years ago228// Remove the following env variables to prevent running electron app in node mode.
229// https://github.com/Microsoft/vscode/issues/3011#issuecomment-184577502
230let env = Object.assign({}, process.env);
231delete env.ATOM_SHELL_INTERNAL_RUN_AS_NODE;
232delete env.ELECTRON_RUN_AS_NODE;
233let command = HostPlatform.getNpmCliCommand("react-devtools");
234CommandPaletteHandler.elementInspector = spawn(command, [], {
235env,
236});
237if (!CommandPaletteHandler.elementInspector.pid) {
238CommandPaletteHandler.elementInspector = null;
d7d405aeYuri Skorokhodov7 years ago239return Q.reject(ErrorHelper.getInternalError(InternalErrorCode.ReactDevtoolsIsNotInstalled));
fd2c4686Yuri Skorokhodov7 years ago240}
6277aa08Yuri Skorokhodov7 years ago241CommandPaletteHandler.elementInspector.stdout.on("data", (data: string) => {
242this.logger.info(data);
243});
244CommandPaletteHandler.elementInspector.stderr.on("data", (data: string) => {
245this.logger.error(data);
246});
247CommandPaletteHandler.elementInspector.once("exit", () => {
248CommandPaletteHandler.elementInspector = null;
249});
62e2a78aYuri Skorokhodov7 years ago250} else {
d7d405aeYuri Skorokhodov7 years ago251this.logger.info(localize("AnotherElementInspectorAlreadyRun", "Another element inspector already run"));
62e2a78aYuri Skorokhodov7 years ago252}
14fc3172Artem Egorov7 years ago253return Q(void 0);
d8b69cd3Yuri Skorokhodov7 years ago254}
14fc3172Artem Egorov7 years ago255
256public static stopElementInspector(): void {
257return CommandPaletteHandler.elementInspector ? CommandPaletteHandler.elementInspector.kill() : void 0;
258}
259
031832ffArtem Egorov7 years ago260public static getPlatformByCommandName(commandName: string): string {
261commandName = commandName.toLocaleLowerCase();
262
263if (commandName.indexOf("android") > -1) {
264return "android";
265}
266
267if (commandName.indexOf("ios") > -1) {
268return "ios";
269}
270
271if (commandName.indexOf("exponent") > -1) {
272return "exponent";
273}
274
275return "";
276}
277
db6fd42aRuslan Bikkinin7 years ago278private static createPlatform(project: IReactNativeProject, platform: "ios" | "android" | "exponent", platformClass: typeof GeneralMobilePlatform, target?: TargetType): GeneralMobilePlatform {
279const runOptions = CommandPaletteHandler.getRunOptions(project, platform, target);
280return new platformClass(runOptions, {
281packager: project.packager,
282});
283}
284
2e432a9eArtem Egorov8 years ago285private static runRestartPackagerCommandAndUpdateStatus(project: IReactNativeProject): Q.Promise<void> {
4787ec09Artem Egorov7 years ago286return project.packager.restart(SettingsHelper.getPackagerPort(project.workspaceFolder.uri.fsPath));
3194e9afMeena Kunnathur Balakrishnan10 years ago287}
b3a793eeNisheet Jain10 years ago288
289/**
290* Ensures that we are in a React Native project and then executes the operation
291* Otherwise, displays an error message banner
292* {operation} - a function that performs the expected operation
293*/
2e432a9eArtem Egorov8 years ago294private static executeCommandInContext(rnCommand: string, workspaceFolder: vscode.WorkspaceFolder, operation: () => Q.Promise<void>): Q.Promise<void> {
031832ffArtem Egorov7 years ago295const extProps = {
296platform: {
297value: CommandPaletteHandler.getPlatformByCommandName(rnCommand),
298isPii: false,
299},
300};
301
302return TelemetryHelper.generate("RNCommand", extProps, (generator) => {
8512ccfeMeena Kunnathur Balakrishnan10 years ago303generator.add("command", rnCommand, false);
e4dd9aa4Serge Svekolnikov8 years ago304const projectRoot = SettingsHelper.getReactNativeProjectRoot(workspaceFolder.uri.fsPath);
db6fd42aRuslan Bikkinin7 years ago305this.logger.debug(`Command palette: run project ${projectRoot} in context`);
306return ReactNativeProjectHelper.isReactNativeProject(projectRoot)
307.then(isRNProject => {
308generator.add("isRNProject", isRNProject, false);
309if (isRNProject) {
310// Bring the log channel to focus
311this.logger.setFocusOnLogChannel();
312
313// Execute the operation
314return operation();
315} else {
316vscode.window.showErrorMessage(`${projectRoot} workspace is not a React Native project.`);
317return;
318}
319});
10873e11digeff10 years ago320});
b3a793eeNisheet Jain10 years ago321}
7893fb7eJimmy Thomson9 years ago322
323/**
324* Publish project to exponent server. In order to do this we need to make sure the user is logged in exponent and the packager is running.
325*/
2e432a9eArtem Egorov8 years ago326private static executePublishToExpHost(project: IReactNativeProject): Q.Promise<boolean> {
d7d405aeYuri Skorokhodov7 years ago327CommandPaletteHandler.logger.info(localize("PublishingAppToExponentServer", "Publishing app to Expo server. This might take a moment."));
2e432a9eArtem Egorov8 years ago328return this.loginToExponent(project)
7059d307Patricio Beltran9 years ago329.then(user => {
2e432a9eArtem Egorov8 years ago330CommandPaletteHandler.logger.debug(`Publishing as ${user.username}...`);
4787ec09Artem Egorov7 years ago331return this.runExponent()
7059d307Patricio Beltran9 years ago332.then(() =>
a41f5c68Artem Egorov8 years ago333XDL.publish(project.workspaceFolder.uri.fsPath))
7059d307Patricio Beltran9 years ago334.then(response => {
335if (response.err || !response.url) {
336return false;
337}
d7d405aeYuri Skorokhodov7 years ago338const publishedOutput = localize("ExpoAppSuccessfullyPublishedTo", "Expo app successfully published to {0}", response.url);
2e432a9eArtem Egorov8 years ago339CommandPaletteHandler.logger.info(publishedOutput);
7059d307Patricio Beltran9 years ago340vscode.window.showInformationMessage(publishedOutput);
341return true;
342});
343});
344}
345
2e432a9eArtem Egorov8 years ago346private static loginToExponent(project: IReactNativeProject): Q.Promise<XDL.IUser> {
347return project.exponentHelper.loginToExponent(
5c8365a6Artem Egorov8 years ago348(message, password) => {
349return Q.Promise((resolve, reject) => {
350vscode.window.showInputBox({ placeHolder: message, password: password })
2e432a9eArtem Egorov8 years ago351.then(login => {
352resolve(login || "");
353}, reject);
5c8365a6Artem Egorov8 years ago354});
355},
356(message) => {
357return Q.Promise((resolve, reject) => {
358vscode.window.showInformationMessage(message)
2e432a9eArtem Egorov8 years ago359.then(password => {
360resolve(password || "");
361}, reject);
5c8365a6Artem Egorov8 years ago362});
363}
4787ec09Artem Egorov7 years ago364)
365.catch((err) => {
d7d405aeYuri Skorokhodov7 years ago366CommandPaletteHandler.logger.warning(localize("ExpoErrorOccuredMakeSureYouAreLoggedIn", "An error has occured. Please make sure you are logged in to Expo, your project is setup correctly for publishing and your packager is running as Expo."));
4787ec09Artem Egorov7 years ago367throw err;
368});
7893fb7eJimmy Thomson9 years ago369}
2e432a9eArtem Egorov8 years ago370
371private static selectProject(): Q.Promise<IReactNativeProject> {
372let keys = Object.keys(this.projectsCache);
373if (keys.length > 1) {
374return Q.Promise((resolve, reject) => {
375vscode.window.showQuickPick(keys)
376.then((selected) => {
377if (selected) {
db6fd42aRuslan Bikkinin7 years ago378this.logger.debug(`Command palette: selected project ${selected}`);
2e432a9eArtem Egorov8 years ago379resolve(this.projectsCache[selected]);
380}
381}, reject);
382});
383} else if (keys.length === 1) {
db6fd42aRuslan Bikkinin7 years ago384this.logger.debug(`Command palette: once project ${keys[0]}`);
2e432a9eArtem Egorov8 years ago385return Q.resolve(this.projectsCache[keys[0]]);
386} else {
d7d405aeYuri Skorokhodov7 years ago387return Q.reject(ErrorHelper.getInternalError(InternalErrorCode.WorkspaceNotFound, "Current workspace does not contain React Native projects."));
2e432a9eArtem Egorov8 years ago388}
389}
d1fc7f8aArtem Egorov8 years ago390
4787ec09Artem Egorov7 years ago391private static getRunOptions(project: IReactNativeProject, platform: "ios" | "android" | "exponent", target: TargetType = "simulator"): IAndroidRunOptions | IIOSRunOptions {
d1fc7f8aArtem Egorov8 years ago392const packagerPort = SettingsHelper.getPackagerPort(project.workspaceFolder.uri.fsPath);
393const runArgs = SettingsHelper.getRunArgs(platform, target, project.workspaceFolder.uri);
394const envArgs = SettingsHelper.getEnvArgs(platform, target, project.workspaceFolder.uri);
395const envFile = SettingsHelper.getEnvFile(platform, target, project.workspaceFolder.uri);
396const projectRoot = SettingsHelper.getReactNativeProjectRoot(project.workspaceFolder.uri.fsPath);
397const runOptions: IAndroidRunOptions | IIOSRunOptions = {
398platform: platform,
399workspaceRoot: project.workspaceFolder.uri.fsPath,
400projectRoot: projectRoot,
401packagerPort: packagerPort,
402runArguments: runArgs,
403env: envArgs,
404envFile: envFile,
405};
406
407return runOptions;
408}
d7d405aeYuri Skorokhodov7 years ago409}