// @ts-check import {exec} from 'node:child_process'; import {accessSync} from 'node:fs'; import fs from 'node:fs/promises'; import https from 'node:https'; import path from 'node:path'; /** @type {{[color: string]: (text: string) => string}} */ const colors = Object.entries({ gray: '\x1b[90m', green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', }).reduce((map, [key, value]) => Object.assign(map, {[key]: (/** @type {string} */text) => `${value}${text}\x1b[0m`}), {}); /** * @param {string} command * @returns {Promise} */ export async function execute(command) { return new Promise((resolve, reject) => exec(command, (error, stdout) => { if (error) { reject(`Failed to execute command ${command}`); } else { resolve(stdout); } })); } /** * @param {string} text * @returns */ export function logWithTime(text) { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); const seconds = now.getSeconds(); const leftpad = (/** @type {number} */n) => String(n).padStart(2, '0'); return console.log(`${colors.gray([hours, minutes, seconds].map(leftpad).join(':'))} ${text}`); } export const log = Object.assign((/** @type {string} */text) => logWithTime(text), { ok: (/** @type {string} */text) => logWithTime(colors.green(text)), warn: (/** @type {string} */text) => logWithTime(colors.yellow(text)), error: (/** @type {string} */text) => logWithTime(colors.red(text)), clearLine: () => { process.stdout.moveCursor(0, -1); process.stdout.clearLine(1); }, }); /** * @param {string} dest * @returns {Promise} */ export async function pathExists(dest) { try { await fs.access(dest); return true; } catch (err) { return false; } } /** * @param {string} dest * @returns {boolean} */ export function pathExistsSync(dest) { try { accessSync(dest); return true; } catch (err) { return false; } } /** * @param {string} dir * @returns {Promise} */ export async function removeFolder(dir) { if (await pathExists(dir)) { await fs.rm(dir, {recursive: true}); } } /** * @param {string} dest * @returns {Promise} */ export async function mkDirIfMissing(dest) { const dir = path.dirname(dest); if (!(await pathExists(dir))) { await fs.mkdir(dir, {recursive: true}); } } /** * @param {string} src * @param {string} dest * @returns {Promise} */ export async function copyFile(src, dest) { await mkDirIfMissing(dest); await fs.copyFile(src, dest); } /** * @param {string} src * @param {BufferEncoding} [encoding] * @returns {Promise} */ export async function readFile(src, encoding = 'utf8') { return await fs.readFile(src, encoding); } /** * @param {string} src * @returns {Promise} */ export async function fileExists(src) { try { await fs.access(src, fs.constants.R_OK); return true; } catch (e) { return false; } } /** * @param {string} dest * @param {string} content * @param {BufferEncoding | null | undefined} encoding * @returns {Promise} */ export async function writeFile(dest, content, encoding = 'utf8') { await mkDirIfMissing(dest); await fs.writeFile(dest, content, encoding); } /** * @param {string} path * @returns {Promise} */ export async function readJSON(path) { const file = await readFile(path); return JSON.parse(file); } /** * @param {string} dest * @param {string} content * @param {string | number | undefined} space * @returns {Promise} */ export async function writeJSON(dest, content, space = 4) { const string = JSON.stringify(content, null, space); return await writeFile(dest, string); } /** * @param {string | string[]} patterns * @returns {Promise} */ export async function getPaths(patterns) { const {globby} = await import('globby'); return await globby(patterns); } /** * @param {number} delay * @returns {Promise} */ export function timeout(delay) { return new Promise((resolve) => setTimeout(resolve, delay)); } /** * @param {string} url * @returns {Promise<{buffer(): Buffer; text(encoding?: BufferEncoding): string; type(): string}>} */ export function httpsRequest(url) { return new Promise((resolve) => { /** @type {Uint8Array[]} */ const data = []; https.get(url, (response) => { response .on('data', (chunk) => data.push(chunk)) .on('end', () => { const buffer = Buffer.concat(data); resolve({ buffer: () => buffer, text: (encoding = 'utf8') => buffer.toString(encoding), type: () => response.headers['content-type'] || '', }); }); }); }); }