microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
b46c1dea2f408007fb3c3ee6cc537d5f85fb0b2c

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/common/commandExecutor.ts

228lines · 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 Q from "q";
5import {ChildProcess} from "child_process";
6import {ILogger} from "../extension/log/LogHelper";
7import { NullLogger } from "../extension/log/NullLogger";
8import {Node} from "./node/node";
9import {ISpawnResult} from "./node/childProcess";
10import {HostPlatform, HostPlatformId} from "./hostPlatform";
11import {ErrorHelper} from "./error/errorHelper";
12import {InternalErrorCode} from "./error/internalErrorCode";
13import * as nls from "vscode-nls";
14const localize = nls.loadMessageBundle();
15
16export enum CommandVerbosity {
17 OUTPUT,
18 SILENT,
19 PROGRESS,
20}
21
22interface EnvironmentOptions {
23 REACT_DEBUGGER?: string;
24}
25
26interface Options {
27 env?: EnvironmentOptions;
28 verbosity?: CommandVerbosity;
29 cwd?: string;
30}
31
32export enum CommandStatus {
33 Start = 0,
34 End = 1,
35}
36
37export class CommandExecutor {
38
39 private static ReactNativeCommand = "react-native";
40 private static ReactNativeVersionCommand = "--version";
41 private childProcess = new Node.ChildProcess();
42
43 constructor(
44 private currentWorkingDirectory: string = process.cwd(),
45 private logger: ILogger = new NullLogger()
46 ) { }
47
48 public execute(command: string, options: Options = {}): Q.Promise<void> {
49 this.logger.debug(CommandExecutor.getCommandStatusString(command, CommandStatus.Start));
50 return this.childProcess.execToString(command, { cwd: this.currentWorkingDirectory, env: options.env })
51 .then(stdout => {
52 this.logger.info(stdout);
53 this.logger.debug(CommandExecutor.getCommandStatusString(command, CommandStatus.End));
54 },
55 (reason: Error) =>
56 this.generateRejectionForCommand(command, reason));
57 }
58
59 /**
60 * Spawns a child process with the params passed
61 * This method waits until the spawned process finishes execution
62 * {command} - The command to be invoked in the child process
63 * {args} - Arguments to be passed to the command
64 * {options} - additional options with which the child process needs to be spawned
65 */
66 public spawn(command: string, args: string[], options: Options = {}): Q.Promise<any> {
67 return this.spawnChildProcess(command, args, options).outcome;
68 }
69
70 /**
71 * Spawns the React Native packager in a child process.
72 */
73 public spawnReactPackager(args: string[], options: Options = {}): ISpawnResult {
74 return this.spawnReactCommand("start", args, options);
75 }
76
77 /**
78 * Uses the `react-native --version` command to get the version used on the project.
79 * Returns null if the workspace is not a react native project
80 */
81 public getReactNativeVersion(): Q.Promise<string> {
82 let deferred = Q.defer<string>();
83 const reactCommand = HostPlatform.getNpmCliCommand(CommandExecutor.ReactNativeCommand);
84 let output = "";
85
86 const result = this.childProcess.spawn(reactCommand,
87 [CommandExecutor.ReactNativeVersionCommand],
88 { cwd: this.currentWorkingDirectory });
89
90 result.stdout.on("data", (data: Buffer) => {
91 output += data.toString();
92 });
93
94 result.stdout.on("end", () => {
95 const match = output.match(/react-native: ([\d\.]+)/);
96 deferred.resolve(match && match[1] || "");
97 });
98
99 return deferred.promise;
100 }
101
102 /**
103 * Kills the React Native packager in a child process.
104 */
105 public killReactPackager(packagerProcess?: ChildProcess): Q.Promise<void> {
106 if (packagerProcess) {
107 return Q({}).then(() => {
108 if (HostPlatform.getPlatformId() === HostPlatformId.WINDOWS) {
109 return this.childProcess.exec("taskkill /pid " + packagerProcess.pid + " /T /F").outcome;
110 } else {
111 packagerProcess.kill();
112 return Q.resolve(void 0);
113 }
114 }).then(() => {
115 this.logger.info(localize("PackagerStopped", "Packager stopped"));
116 });
117
118 } else {
119 this.logger.warning(localize("PackagerNotFound", "Packager not found"));
120 return Q.resolve<void>(void 0);
121 }
122 }
123
124 /**
125 * Executes a react native command and waits for its completion.
126 */
127 public spawnReactCommand(command: string, args: string[] = [], options: Options = {}): ISpawnResult {
128 const reactCommand = HostPlatform.getNpmCliCommand(CommandExecutor.ReactNativeCommand);
129 return this.spawnChildProcess(reactCommand, [command, ...args], options);
130 }
131
132 /**
133 * Spawns a child process with the params passed
134 * This method has logic to do while the command is executing
135 * {command} - The command to be invoked in the child process
136 * {args} - Arguments to be passed to the command
137 * {options} - additional options with which the child process needs to be spawned
138 */
139 public spawnWithProgress(command: string, args: string[], options: Options = { verbosity: CommandVerbosity.OUTPUT }): Q.Promise<void> {
140 let deferred = Q.defer<void>();
141 const spawnOptions = Object.assign({}, { cwd: this.currentWorkingDirectory }, options);
142 const commandWithArgs = command + " " + args.join(" ");
143 const timeBetweenDots = 1500;
144 let lastDotTime = 0;
145
146 const printDot = () => {
147 const now = Date.now();
148 if (now - lastDotTime > timeBetweenDots) {
149 lastDotTime = now;
150 this.logger.logStream(".", process.stdout);
151 }
152 };
153
154 if (options.verbosity === CommandVerbosity.OUTPUT) {
155 this.logger.debug(CommandExecutor.getCommandStatusString(commandWithArgs, CommandStatus.Start));
156 }
157
158 const result = this.childProcess.spawn(command, args, spawnOptions);
159
160 result.stdout.on("data", (data: Buffer) => {
161 if (options.verbosity === CommandVerbosity.OUTPUT) {
162 this.logger.logStream(data, process.stdout);
163 } else if (options.verbosity === CommandVerbosity.PROGRESS) {
164 printDot();
165 }
166 });
167
168 result.stderr.on("data", (data: Buffer) => {
169 if (options.verbosity === CommandVerbosity.OUTPUT) {
170 this.logger.logStream(data, process.stderr);
171 } else if (options.verbosity === CommandVerbosity.PROGRESS) {
172 printDot();
173 }
174 });
175
176 result.outcome = result.outcome.then(
177 () => {
178 if (options.verbosity === CommandVerbosity.OUTPUT) {
179 this.logger.debug(CommandExecutor.getCommandStatusString(commandWithArgs, CommandStatus.End));
180 }
181 this.logger.logStream("\n", process.stdout);
182 deferred.resolve(void 0);
183 },
184 reason => {
185 deferred.reject(reason);
186 return this.generateRejectionForCommand(commandWithArgs, reason);
187 });
188 return deferred.promise;
189 }
190
191 private spawnChildProcess(command: string, args: string[], options: Options = {}): ISpawnResult {
192 const spawnOptions = Object.assign({}, { cwd: this.currentWorkingDirectory }, options);
193 const commandWithArgs = command + " " + args.join(" ");
194
195 this.logger.debug(CommandExecutor.getCommandStatusString(commandWithArgs, CommandStatus.Start));
196 const result = this.childProcess.spawn(command, args, spawnOptions);
197
198 result.stderr.on("data", (data: Buffer) => {
199 this.logger.logStream(data, process.stderr);
200 });
201
202 result.stdout.on("data", (data: Buffer) => {
203 this.logger.logStream(data, process.stdout);
204 });
205
206 result.outcome = result.outcome.then(
207 () =>
208 this.logger.debug(CommandExecutor.getCommandStatusString(commandWithArgs, CommandStatus.End)),
209 reason =>
210 this.generateRejectionForCommand(commandWithArgs, reason));
211 return result;
212 }
213
214 private generateRejectionForCommand(command: string, reason: any): Q.Promise<void> {
215 return Q.reject<void>(ErrorHelper.getNestedError(reason, InternalErrorCode.CommandFailed, command));
216 }
217
218 private static getCommandStatusString(command: string, status: CommandStatus) {
219 switch (status) {
220 case CommandStatus.Start:
221 return `Executing command: ${command}`;
222 case CommandStatus.End:
223 return `Finished executing: ${command}`;
224 default:
225 throw ErrorHelper.getInternalError(InternalErrorCode.UnsupportedCommandStatus);
226 }
227 }
228}
229