microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
c0b32993595df760282a903082cf37d59de18ea8

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