eng/scripts/helper.js (75 lines of code) (raw):
import { spawn, spawnSync } from "child_process";
import { readFileSync } from "fs";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";
function read(filename) {
const txt = readFileSync(filename, "utf8")
.replace(/\r/gm, "")
.replace(/\n/gm, "«")
.replace(/\/\*.*?\*\//gm, "")
.replace(/«/gm, "\n")
.replace(/\s+\/\/.*/g, "");
return JSON.parse(txt);
}
export const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
const rush = read(`${repoRoot}/rush.json`);
export function forEachProject(onEach) {
// load all the projects
for (const each of rush.projects) {
const packageName = each.packageName;
const projectFolder = resolve(`${repoRoot}/${each.projectFolder}`);
const project = JSON.parse(readFileSync(`${projectFolder}/package.json`));
onEach(packageName, projectFolder, project, each);
}
}
export function npmForEach(cmd, options) {
forEachProject((name, location, project) => {
if (project.scripts[cmd] || cmd === "pack") {
const args = cmd === "pack" ? [cmd] : ["run", cmd];
run("npm", args, { cwd: location, ...options });
}
});
}
// We could use { shell: true } to let Windows find .cmd, but that causes other issues.
// It breaks ENOENT checking for command-not-found and also handles command/args with spaces
// poorly.
const isCmdOnWindows = ["rush", "npm", "code"];
export class CommandFailedError extends Error {
constructor(msg, proc) {
super(msg);
this.proc = proc;
}
}
export function run(command, args, options) {
if (!options || !options.silent) {
console.log();
console.log(`> ${command} ${args.join(" ")}`);
}
options = {
stdio: "inherit",
sync: true,
throwOnNonZeroExit: true,
...options,
};
if (process.platform === "win32" && isCmdOnWindows.includes(command)) {
command += ".cmd";
// Required in latest versions of Node when calling batch files on Windows
options.shell = true;
}
const proc = (options.sync ? spawnSync : spawn)(command, args, options);
if (proc.error) {
if (options.ignoreCommandNotFound && proc.error.code === "ENOENT") {
console.log(`Skipped: Command \`${command}\` not found.`);
} else {
throw proc.error;
}
} else if (options.throwOnNonZeroExit && proc.status !== undefined && proc.status !== 0) {
throw new CommandFailedError(
`Command \`${command} ${args.join(" ")}\` failed with exit code ${proc.status}`,
proc
);
}
return proc;
}
export function clearScreen() {
process.stdout.write("\x1bc");
}
export function logWithTime(msg) {
const time = new Date().toLocaleTimeString();
console.log(`[${time}] ${msg}`);
}