microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
8a67e140aee58da792b1e9c1c17358a98d5a3704

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/common/commandExecutor.ts

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