microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
8152571adb22d7628c545490a38bc85153249841

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