import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises' import { dirname, join as joinPath, normalize } from 'node:path' import process from 'node:process' import { toUint8Array } from '../common/data/bin' import { isUint8Array } from '../common/data/is' import { jsonStringifySorted } from '../common/data/json' /** Try to use `~` for HOME folder if possible */ export function toHumanReadableFilePath(path: string) { const p = normalize(path) const h = process.env.HOME if (h && p.startsWith(h)) return `~${p.slice(h.length)}` return p } /** * Tests whether a value is a valid file path. Attention! Also folders are valid file paths. * @deprecated */ export async function exists(path: string): Promise { try { await stat(path) } catch (err) { return false } return true } export async function isFile(path: string) { try { const s = await stat(path) return s.isFile() } catch (err) { return false } } export async function isFolder(path: string) { try { const s = await stat(path) return s.isDirectory() } catch (err) { return false } } export function isHiddenPath(path: string): boolean { return path.startsWith('.') || path.includes('/.') } /** Create missing folders e.g. `/a/b/c` will create folders `/a/b/c` */ export async function ensureFolder(...parts: string[]): Promise { const path = joinPath(...parts) if (!(await isFolder(path))) await mkdir(path, { recursive: true }) return path } /** Create missing folder to file location e.g. `/a/b/c/s.txt` will create folders `/a/b/c` */ export async function ensureFolderForFile(...parts: string[]): Promise { const path = dirname(joinPath(...parts)) if (!(await isFolder(path))) await mkdir(path, { recursive: true }) return path } export async function removeFolder(...parts: string[]): Promise { const path = joinPath(...parts) if (await isFolder(path)) await rm(path, { recursive: true }) return path } export async function readText(...parts: string[]): Promise { const path = joinPath(...parts) if (await isFile(path)) return await readFile(path, 'utf-8') } export async function readJson(...parts: string[]): Promise { const content = await readText(...parts) if (content != null) { try { return JSON.parse(content) } catch (err) { } } } export async function readBin(...parts: string[]): Promise { const path = joinPath(...parts) if (await isFile(path)) return toUint8Array(await readFile(path)) } /** @deprecated use readJson or readBin */ export async function readData(...parts: string[]): Promise { const path = joinPath(...parts) if (await isFile(path)) return await readFile(path) } export async function writeText(path: string, content: string, createFolders = false): Promise { if (createFolders) await ensureFolderForFile(path) await writeFile(path, content, 'utf-8') } /** @deprecated use writeBin or writeJson */ export async function writeData(path: string, content: object | Uint8Array, createFolders = false): Promise { if (createFolders) await ensureFolderForFile(path) const data = isUint8Array(content) ? content : JSON.stringify(content) await writeFile(path, data) } export async function writeBin(path: string, content: Uint8Array, info: { createFolders?: boolean } = {}): Promise { const { createFolders = false } = info if (createFolders) await ensureFolderForFile(path) await writeFile(path, content) } export async function writeJson(path: string, content: T, info: { createFolders?: boolean pretty?: boolean } = {}): Promise { const { createFolders = false, pretty = false } = info await writeText(path, jsonStringifySorted(content, pretty ? 2 : undefined), createFolders) }