microsoft/typespec

Public

mirrored from https://github.com/microsoft/typespecAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
4199f83575db84feb69e28f57d78c400aec1eab4

Branches

Tags

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

Clone

HTTPS

Download ZIP

packages/internal-build-utils/src/common.ts

140lines · modecode

1import { ChildProcess, spawn, SpawnOptions } from "child_process";
2
3export class CommandFailedError extends Error {
4 constructor(
5 msg: string,
6 public proc: ChildProcess
7 ) {
8 super(msg);
9 }
10}
11
12/**
13 * Return the correct executable name if on unix or windows(with .cmd extension)
14 * @param cmd to run
15 * @returns
16 */
17export function xplatCmd(cmd: string) {
18 return process.platform === "win32" ? `${cmd}.cmd` : cmd;
19}
20
21export interface RunOptions extends SpawnOptions {
22 silent?: boolean;
23 encoding?: string;
24 ignoreCommandNotFound?: boolean;
25 throwOnNonZeroExit?: boolean;
26}
27
28/** Run the given command and exit if command return non zero exit code. */
29export async function runOrExit(
30 command: string,
31 args: string[],
32 options?: RunOptions
33): Promise<ExecResult> {
34 return exitOnFailedCommand(() => run(command, args, options));
35}
36
37export async function exitOnFailedCommand<T>(cb: () => Promise<T>): Promise<T> {
38 try {
39 return await cb();
40 } catch (e: any) {
41 if (e instanceof CommandFailedError) {
42 // eslint-disable-next-line no-console
43 console.error(e.message);
44 process.exit(e.proc.exitCode ?? -1);
45 } else {
46 throw e;
47 }
48 }
49}
50
51const isCmdOnWindows = ["pnpm", "npm", "code", "code-insiders", "docusaurus", "tsc", "prettier"];
52
53/** Run the given command or throw CommandFailedError if the command returns non zero exit code. */
54export async function run(
55 command: string,
56 args: string[],
57 options?: RunOptions
58): Promise<ExecResult> {
59 if (!options?.silent) {
60 // eslint-disable-next-line no-console
61 console.log();
62 // eslint-disable-next-line no-console
63 console.log(`> ${command} ${args.join(" ")}`);
64 }
65
66 options = {
67 stdio: "inherit",
68 throwOnNonZeroExit: true,
69 ...options,
70 };
71
72 if (
73 process.platform === "win32" &&
74 (isCmdOnWindows.includes(command) || isCmdOnWindows.some((x) => command.endsWith(`/${x}`)))
75 ) {
76 command += ".cmd";
77 }
78
79 try {
80 const result = await execAsync(command, args, options);
81 if (options.throwOnNonZeroExit && result.exitCode !== undefined && result.exitCode !== 0) {
82 throw new CommandFailedError(
83 `Command \`${command} ${args.join(" ")}\` failed with exit code ${result.exitCode}`,
84 result.proc
85 );
86 }
87 return result;
88 } catch (e: any) {
89 if (options.ignoreCommandNotFound && e.code === "ENOENT") {
90 // eslint-disable-next-line no-console
91 console.log(`Skipped: Command \`${command}\` not found.`);
92 return { exitCode: 0, stdout: "", stderr: "" } as any;
93 } else {
94 throw e;
95 }
96 }
97}
98
99export interface ExecResult {
100 exitCode: number;
101 stdout: string;
102 stderr: string;
103 proc: ChildProcess;
104}
105export async function execAsync(
106 command: string,
107 args: string[],
108 options: SpawnOptions
109): Promise<ExecResult> {
110 const child = spawn(command, args, options);
111
112 return new Promise((resolve, reject) => {
113 child.on("error", (error) => {
114 reject(error);
115 });
116 const stdout: Buffer[] = [];
117 const stderr: Buffer[] = [];
118 child.stdout?.on("data", (data) => stdout.push(data));
119 child.stderr?.on("data", (data) => stderr.push(data));
120
121 child.on("exit", (exitCode) => {
122 resolve({
123 exitCode: exitCode ?? -1,
124 stdout: Buffer.concat(stdout).toString(),
125 stderr: Buffer.concat(stderr).toString(),
126 proc: child,
127 });
128 });
129 });
130}
131
132export function clearScreen() {
133 process.stdout.write("\x1bc");
134}
135
136export function logWithTime(msg: string) {
137 const time = new Date().toLocaleTimeString();
138 // eslint-disable-next-line no-console
139 console.log(`[${time}] ${msg}`);
140}
141