microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
supports-bunx-as-a-react-native-package-manager

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/common/commandExecutor.ts

291lines · modeblame

a31b007cunknown10 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
af1474acRedMickey6 years ago4import * as path from "path";
ce5e88eeYuri Skorokhodov6 years ago5import * as cp from "child_process";
09f6024fHeniker4 years ago6import * as nls from "vscode-nls";
34472878RedMickey5 years ago7import { ILogger } from "../extension/log/LogHelper";
8import { NullLogger } from "../extension/log/NullLogger";
09f6024fHeniker4 years ago9import { ProjectVersionHelper } from "./projectVersionHelper";
34472878RedMickey5 years ago10import { ISpawnResult } from "./node/childProcess";
11import { HostPlatform, HostPlatformId } from "./hostPlatform";
12import { ErrorHelper } from "./error/errorHelper";
13import { InternalErrorCode } from "./error/internalErrorCode";
ce5e88eeYuri Skorokhodov6 years ago14import { Node } from "./node/node";
09f6024fHeniker4 years ago15
34472878RedMickey5 years ago16nls.config({
17messageFormat: nls.MessageFormat.bundle,
18bundleFormat: nls.BundleFormat.standalone,
19})();
e2644f37Yuri Skorokhodov7 years ago20const localize = nls.loadMessageBundle();
3fb37ad5unknown10 years ago21
831f4a85Patricio Beltran10 years ago22export enum CommandVerbosity {
23OUTPUT,
24SILENT,
25PROGRESS,
26}
27
3fb37ad5unknown10 years ago28interface EnvironmentOptions {
29REACT_DEBUGGER?: string;
30}
31
32interface Options {
33env?: EnvironmentOptions;
831f4a85Patricio Beltran10 years ago34verbosity?: CommandVerbosity;
0d827d9bJimmy Thomson10 years ago35cwd?: string;
3fb37ad5unknown10 years ago36}
37
17161993Meena Kunnathur Balakrishnan10 years ago38export enum CommandStatus {
39Start = 0,
27710197Vladimir Kotikov9 years ago40End = 1,
17161993Meena Kunnathur Balakrishnan10 years ago41}
42
3fb37ad5unknown10 years ago43export class CommandExecutor {
af1474acRedMickey6 years ago44public static ReactNativeCommand: string | null;
842deeafEmmaYuan10154 months ago45/** Set externally (e.g. from appLauncher) to the active package manager (npm/pnpm/bun). */
46public static PackageManager: string | null;
9596aa53digeff10 years ago47private childProcess = new Node.ChildProcess();
3fb37ad5unknown10 years ago48
0a68f8dbArtem Egorov8 years ago49constructor(
4dfb1c4cetatanova5 years ago50private nodeModulesRoot: string,
0a68f8dbArtem Egorov8 years ago51private currentWorkingDirectory: string = process.cwd(),
34472878RedMickey5 years ago52private logger: ILogger = new NullLogger(),
53) {}
3fb37ad5unknown10 years ago54
0d77292aJiglioNero4 years ago55public async execute(command: string, options: Options = {}): Promise<void> {
0a68f8dbArtem Egorov8 years ago56this.logger.debug(CommandExecutor.getCommandStatusString(command, CommandStatus.Start));
0d77292aJiglioNero4 years ago57try {
58const stdout = await this.childProcess.execToString(command, {
59cwd: this.currentWorkingDirectory,
60env: options.env,
61});
62this.logger.info(stdout);
63this.logger.debug(CommandExecutor.getCommandStatusString(command, CommandStatus.End));
64} catch (reason) {
65return this.generateRejectionForCommand(command, reason);
66}
3fb37ad5unknown10 years ago67}
68
57fee98eEzio Li3 years ago69public async executeToString(command: string, options: Options = {}): Promise<string> {
70try {
71const stdout = await this.childProcess.execToString(command, {
72cwd: this.currentWorkingDirectory,
73env: options.env,
74});
75return stdout;
76} catch (reason) {
176f99c8ConnorQi015 months ago77return reason as string;
57fee98eEzio Li3 years ago78}
79}
80
45944d15Meena Kunnathur Balakrishnan10 years ago81/**
82* Spawns a child process with the params passed
83* This method waits until the spawned process finishes execution
84* {command} - The command to be invoked in the child process
85* {args} - Arguments to be passed to the command
86* {options} - additional options with which the child process needs to be spawned
87*/
ce5e88eeYuri Skorokhodov6 years ago88public spawn(command: string, args: string[], options: Options = {}): Promise<any> {
9596aa53digeff10 years ago89return this.spawnChildProcess(command, args, options).outcome;
45944d15Meena Kunnathur Balakrishnan10 years ago90}
91
6126d899Meena Kunnathur Balakrishnan10 years ago92/**
93* Spawns the React Native packager in a child process.
94*/
9596aa53digeff10 years ago95public spawnReactPackager(args: string[], options: Options = {}): ISpawnResult {
96return this.spawnReactCommand("start", args, options);
6126d899Meena Kunnathur Balakrishnan10 years ago97}
98
efb84653Ezio Li3 years ago99/**
100* Spawns the React Native packager in a child process.
101*/
102public spawnExpoPackager(args: string[], options: Options = {}): ISpawnResult {
103return this.spawnExpoCommand("start", args, options);
104}
105
0d77292aJiglioNero4 years ago106public async getReactNativeVersion(): Promise<string> {
107const versions = await ProjectVersionHelper.getReactNativeVersions(
108this.currentWorkingDirectory,
34472878RedMickey5 years ago109);
0d77292aJiglioNero4 years ago110return versions.reactNativeVersion;
b8a56999Patricio Beltran10 years ago111}
112
c3a987a7Meena Kunnathur Balakrishnan10 years ago113/**
114* Kills the React Native packager in a child process.
115*/
0d77292aJiglioNero4 years ago116public async killReactPackager(packagerProcess?: cp.ChildProcess): Promise<void> {
c9b4fa6cMeena Kunnathur Balakrishnan10 years ago117if (packagerProcess) {
0d77292aJiglioNero4 years ago118if (HostPlatform.getPlatformId() === HostPlatformId.WINDOWS) {
119const res = await this.childProcess.exec(
09f6024fHeniker4 years ago120`taskkill /pid ${packagerProcess.pid} /T /F`,
0d77292aJiglioNero4 years ago121);
122await res.outcome;
123} else {
124packagerProcess.kill();
125}
126this.logger.info(localize("PackagerStopped", "Packager stopped"));
c3a987a7Meena Kunnathur Balakrishnan10 years ago127} else {
e2644f37Yuri Skorokhodov7 years ago128this.logger.warning(localize("PackagerNotFound", "Packager not found"));
c3a987a7Meena Kunnathur Balakrishnan10 years ago129}
130}
131
f8d32439dlebu10 years ago132/**
842deeafEmmaYuan10154 months ago133* Spawns the React Native packager in a child process.
f8d32439dlebu10 years ago134*/
34472878RedMickey5 years ago135public spawnReactCommand(
136command: string,
137args: string[] = [],
138options: Options = {},
139): ISpawnResult {
842deeafEmmaYuan10154 months ago140if (CommandExecutor.PackageManager === "bun") {
141// bun uses `bunx` (equivalent of npx) to run CLIs.
142return this.spawnChildProcess("bunx", ["react-native", command, ...args], options);
143}
af1474acRedMickey6 years ago144const reactCommand = HostPlatform.getNpmCliCommand(this.selectReactNativeCLI());
94cd5149Artem Egorov9 years ago145return this.spawnChildProcess(reactCommand, [command, ...args], options);
5b0582f3digeff10 years ago146}
147
efb84653Ezio Li3 years ago148/**
842deeafEmmaYuan10154 months ago149* Spawns the Expo CLI in a child process.
efb84653Ezio Li3 years ago150*/
151public spawnExpoCommand(
152command: string,
153args: string[] = [],
154options: Options = {},
155): ISpawnResult {
842deeafEmmaYuan10154 months ago156if (CommandExecutor.PackageManager === "bun") {
157// bun uses `bunx expo` instead of the local .bin/expo shim.
158return this.spawnChildProcess("bunx", ["expo", command, ...args], options);
159}
efb84653Ezio Li3 years ago160const expoCommand = HostPlatform.getNpmCliCommand(this.selectExpoCLI());
161return this.spawnChildProcess(expoCommand, [command, ...args], options);
162}
163
831f4a85Patricio Beltran10 years ago164/**
165* Spawns a child process with the params passed
166* This method has logic to do while the command is executing
167* {command} - The command to be invoked in the child process
168* {args} - Arguments to be passed to the command
169* {options} - additional options with which the child process needs to be spawned
170*/
0d77292aJiglioNero4 years ago171public async spawnWithProgress(
34472878RedMickey5 years ago172command: string,
173args: string[],
174options: Options = { verbosity: CommandVerbosity.OUTPUT },
175): Promise<void> {
831f4a85Patricio Beltran10 years ago176const spawnOptions = Object.assign({}, { cwd: this.currentWorkingDirectory }, options);
09f6024fHeniker4 years ago177const commandWithArgs = `${command} ${args.join(" ")}`;
831f4a85Patricio Beltran10 years ago178const timeBetweenDots = 1500;
b0af599cJimmy Thomson10 years ago179let lastDotTime = 0;
831f4a85Patricio Beltran10 years ago180
181const printDot = () => {
b0af599cJimmy Thomson10 years ago182const now = Date.now();
183if (now - lastDotTime > timeBetweenDots) {
184lastDotTime = now;
0a68f8dbArtem Egorov8 years ago185this.logger.logStream(".", process.stdout);
b0af599cJimmy Thomson10 years ago186}
831f4a85Patricio Beltran10 years ago187};
188
189if (options.verbosity === CommandVerbosity.OUTPUT) {
34472878RedMickey5 years ago190this.logger.debug(
191CommandExecutor.getCommandStatusString(commandWithArgs, CommandStatus.Start),
192);
831f4a85Patricio Beltran10 years ago193}
194
195const result = this.childProcess.spawn(command, args, spawnOptions);
196
197result.stdout.on("data", (data: Buffer) => {
198if (options.verbosity === CommandVerbosity.OUTPUT) {
0a68f8dbArtem Egorov8 years ago199this.logger.logStream(data, process.stdout);
b0af599cJimmy Thomson10 years ago200} else if (options.verbosity === CommandVerbosity.PROGRESS) {
831f4a85Patricio Beltran10 years ago201printDot();
202}
203});
204
205result.stderr.on("data", (data: Buffer) => {
206if (options.verbosity === CommandVerbosity.OUTPUT) {
0a68f8dbArtem Egorov8 years ago207this.logger.logStream(data, process.stderr);
b0af599cJimmy Thomson10 years ago208} else if (options.verbosity === CommandVerbosity.PROGRESS) {
831f4a85Patricio Beltran10 years ago209printDot();
210}
211});
212
0d77292aJiglioNero4 years ago213try {
214await result.outcome;
215if (options.verbosity === CommandVerbosity.OUTPUT) {
216this.logger.debug(
217CommandExecutor.getCommandStatusString(commandWithArgs, CommandStatus.End),
218);
219}
220this.logger.logStream("\n", process.stdout);
221} catch (reason) {
222return this.generateRejectionForCommand(commandWithArgs, reason);
223}
831f4a85Patricio Beltran10 years ago224}
225
af1474acRedMickey6 years ago226public selectReactNativeCLI(): string {
34472878RedMickey5 years ago227return (
228CommandExecutor.ReactNativeCommand ||
4dfb1c4cetatanova5 years ago229path.resolve(this.nodeModulesRoot, "node_modules", ".bin", "react-native")
34472878RedMickey5 years ago230);
af1474acRedMickey6 years ago231}
232
efb84653Ezio Li3 years ago233public selectExpoCLI(): string {
234return (
235CommandExecutor.ReactNativeCommand ||
236path.resolve(this.nodeModulesRoot, "node_modules", ".bin", "expo")
237);
238}
239
34472878RedMickey5 years ago240private spawnChildProcess(
241command: string,
242args: string[],
243options: Options = {},
244): ISpawnResult {
77e86943lexie0112 years ago245const spawnOptions = Object.assign({}, { cwd: this.currentWorkingDirectory }, options, {
246shell: true,
247});
09f6024fHeniker4 years ago248const commandWithArgs = `${command} ${args.join(" ")}`;
3fb37ad5unknown10 years ago249
34472878RedMickey5 years ago250this.logger.debug(
251CommandExecutor.getCommandStatusString(commandWithArgs, CommandStatus.Start),
252);
9596aa53digeff10 years ago253const result = this.childProcess.spawn(command, args, spawnOptions);
3fb37ad5unknown10 years ago254
255result.stderr.on("data", (data: Buffer) => {
0a68f8dbArtem Egorov8 years ago256this.logger.logStream(data, process.stderr);
3fb37ad5unknown10 years ago257});
258
259result.stdout.on("data", (data: Buffer) => {
0a68f8dbArtem Egorov8 years ago260this.logger.logStream(data, process.stdout);
3fb37ad5unknown10 years ago261});
262
10873e11digeff10 years ago263result.outcome = result.outcome.then(
9596aa53digeff10 years ago264() =>
34472878RedMickey5 years ago265this.logger.debug(
266CommandExecutor.getCommandStatusString(commandWithArgs, CommandStatus.End),
267),
268reason => this.generateRejectionForCommand(commandWithArgs, reason),
269);
efa076b0Meena Kunnathur Balakrishnan10 years ago270return result;
3fb37ad5unknown10 years ago271}
10873e11digeff10 years ago272
ce5e88eeYuri Skorokhodov6 years ago273private generateRejectionForCommand(command: string, reason: any): Promise<void> {
34472878RedMickey5 years ago274return Promise.reject<void>(
60ad4ec0JiglioNero5 years ago275reason.errorCode === InternalErrorCode.CommandFailed
276? reason
277: ErrorHelper.getNestedError(reason, InternalErrorCode.CommandFailed, command),
34472878RedMickey5 years ago278);
10873e11digeff10 years ago279}
0a68f8dbArtem Egorov8 years ago280
281private static getCommandStatusString(command: string, status: CommandStatus) {
282switch (status) {
283case CommandStatus.Start:
fc602bb6Yuri Skorokhodov7 years ago284return `Executing command: ${command}`;
0a68f8dbArtem Egorov8 years ago285case CommandStatus.End:
fc602bb6Yuri Skorokhodov7 years ago286return `Finished executing: ${command}`;
0a68f8dbArtem Egorov8 years ago287default:
fc602bb6Yuri Skorokhodov7 years ago288throw ErrorHelper.getInternalError(InternalErrorCode.UnsupportedCommandStatus);
0a68f8dbArtem Egorov8 years ago289}
290}
3fb37ad5unknown10 years ago291}