import fs from "node:fs/promises" import path from "path" import os from "os" import { glob } from "glob" export async function fileExists(filePath) { try { await fs.stat(filePath) return true } catch (err) { if (err.code === "ENOENT") { return false } throw err // re-throw other errors } } export async function readDirectoryRecursively(dir, files = []) { const exists = await fileExists(dir) if (!exists) { return files } const contents = await fs.readdir(dir, { withFileTypes: true }) for (const item of contents) { const itemPath = path.join(dir, item.name) if (item.isDirectory()) { readDirectoryRecursively(itemPath, files) } else { files.push(itemPath) } } return files } // type InputConfig // { // pattern: String | String[]; // ignore: String | String[]; // } export async function readFilesByGlob(globConfigs) { const matchPromises = globConfigs.reduce( async (existingMatches, globConfig) => { const { pattern, ignore, dot } = { dot: false, ignore: [], ...globConfig, } const matches = await glob(expandTilde(pattern), { ignore, dot, }) return [...(await existingMatches), ...matches] }, [], ) const files = await matchPromises return [...new Set(files)] } export function expandTilde(path) { if (!path.startsWith("~")) return path return path.replace(/^~(?=$|\/|\\)/, os.homedir()) } export async function checkFilesExist(files, baseDir) { const filesToCheck = Array.isArray(files) ? files : [files] const fileCheckResults = await Promise.all( filesToCheck.map(async file => { const filePath = path.join(baseDir, file) const exists = await fileExists(filePath) return { filePath, exists } }), ) return fileCheckResults.reduce( (sorted, { filePath, exists }) => { return exists ? { ...sorted, present: [...sorted.present, filePath] } : { ...sorted, absent: [...sorted.absent, filePath] } }, { present: [], absent: [] }, ) } export async function writeFile(filePath, content) { const fileDir = path.dirname(filePath) await fs.mkdir(fileDir, { recursive: true }) return await fs.writeFile(filePath, content, { encoding: "utf8", }) }