UNPKG

@catbee/utils

Version:

A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.

330 lines 9.97 kB
import fs from "fs/promises"; import { createReadStream, createWriteStream } from "fs"; import { tmpdir } from "os"; import path from "path"; import { pipeline } from "stream/promises"; import { randomUUID } from "crypto"; /** * Checks whether a file or directory exists at the given path. * * @param {string} path - The file or directory path to check. * @returns {Promise<boolean>} Resolves to `true` if the path exists, `false` otherwise. */ export async function fileExists(path) { try { await fs.access(path); return true; } catch (_a) { return false; } } /** * Reads and parses a JSON file from the specified path. * * @typeParam T - The expected type of the parsed JSON object. * @param {string} path - The path to the JSON file. * @returns {Promise<T | null>} The parsed object, or `null` if reading or parsing fails. */ export async function readJsonFile(path) { try { const data = await fs.readFile(path, "utf-8"); return JSON.parse(data); } catch (_a) { return null; } } /** * Writes a JavaScript object to a file as formatted JSON. * * @param {string} path - The destination file path. * @param {any} data - The object to serialize and write. * @param {number} [space=2] - Number of spaces for JSON formatting. * @returns {Promise<void>} Resolves when writing is complete. */ export async function writeJsonFile(path, data, space = 2) { const json = JSON.stringify(data, null, space); await fs.writeFile(path, json, "utf-8"); } /** * Deletes a file if it exists. * * @param {string} path - The file path to delete. * @returns {Promise<boolean>} Resolves to `true` if the file was deleted or didn't exist, `false` if deletion failed. */ export async function deleteFileIfExists(path) { try { await fs.unlink(path); return true; } catch (err) { if (err.code === "ENOENT") return true; return false; } } /** * Reads a text file from the specified path. * * @param {string} path - The path to the text file. * @param {BufferEncoding} [encoding="utf-8"] - The encoding to use. * @returns {Promise<string | null>} The file contents, or `null` if reading fails. */ export async function readTextFile(path, encoding = "utf-8") { try { return await fs.readFile(path, encoding); } catch (_a) { return null; } } /** * Writes text content to a file. * * @param {string} path - The destination file path. * @param {string} content - The text content to write. * @param {BufferEncoding} [encoding="utf-8"] - The encoding to use. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export async function writeTextFile(path, content, encoding = "utf-8") { try { await fs.writeFile(path, content, encoding); return true; } catch (_a) { return false; } } /** * Appends text content to a file. * * @param {string} path - The file path to append to. * @param {string} content - The text content to append. * @param {BufferEncoding} [encoding="utf-8"] - The encoding to use. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export async function appendTextFile(path, content, encoding = "utf-8") { try { await fs.appendFile(path, content, encoding); return true; } catch (_a) { return false; } } /** * Copies a file from source to destination. * * @param {string} source - Source file path. * @param {string} destination - Destination file path. * @param {boolean} [overwrite=false] - Whether to overwrite if destination exists. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export async function copyFile(source, destination, overwrite = false) { try { const flags = overwrite ? 0 : fs.constants.COPYFILE_EXCL; await fs.copyFile(source, destination, flags); return true; } catch (_a) { return false; } } /** * Renames/moves a file. * * @param {string} oldPath - Current file path. * @param {string} newPath - New file path. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export async function moveFile(oldPath, newPath) { try { await fs.rename(oldPath, newPath); return true; } catch (_a) { // If rename fails (e.g., across devices), try copy+delete try { await fs.copyFile(oldPath, newPath); await fs.unlink(oldPath); return true; } catch (_b) { return false; } } } /** * Gets file stats if the file exists. * * @param {string} path - Path to the file. * @returns {Promise<fs.Stats | null>} File stats object or null if file doesn't exist. */ export async function getFileStats(path) { try { return await fs.stat(path); } catch (_a) { return null; } } /** * Creates a temporary file with optional content. * * @param {object} [options] - Options for creating the temp file. * @param {string} [options.prefix="tmp-"] - Filename prefix. * @param {string} [options.extension=""] - File extension. * @param {string} [options.dir] - Directory to create the file in (defaults to OS temp dir). * @param {string | Buffer} [options.content] - Optional content to write to the file. * @returns {Promise<string>} Path to the created temporary file. */ export async function createTempFile({ prefix = "tmp-", extension = "", dir = tmpdir(), content, } = {}) { const ext = extension ? extension.startsWith(".") ? extension : `.${extension}` : ""; const filename = `${prefix}${randomUUID()}${ext}`; const filepath = path.join(dir, filename); if (content) { await fs.writeFile(filepath, content); } else { await fs.writeFile(filepath, ""); } return filepath; } /** * Streams a file from source to destination. * Useful for large files to avoid loading the entire file into memory. * * @param {string} source - Source file path. * @param {string} destination - Destination file path. * @returns {Promise<void>} Resolves when streaming completes. * @throws {Error} If streaming fails. */ export async function streamFile(source, destination) { const readStream = createReadStream(source); const writeStream = createWriteStream(destination); try { await pipeline(readStream, writeStream); } catch (error) { // Ensure both streams are closed in case of error readStream.destroy(); writeStream.destroy(); throw error; } } /** * Reads a directory and returns file names. * * @param {string} dirPath - Path to the directory. * @param {object} [options] - Options for reading the directory. * @param {boolean} [options.fullPaths=false] - Whether to return full paths. * @param {RegExp} [options.filter] - Optional regex to filter files. * @returns {Promise<string[]>} Array of file names or paths. * @throws {Error} If directory cannot be read. */ export async function readDirectory(dirPath, options = {}) { const files = await fs.readdir(dirPath); let result = files; if (options.filter) { result = result.filter((file) => options.filter.test(file)); } if (options.fullPaths) { result = result.map((file) => path.join(dirPath, file)); } return result; } /** * Creates a directory if it doesn't exist. * * @param {string} dirPath - Path to the directory. * @param {boolean} [recursive=true] - Whether to create parent directories. * @returns {Promise<boolean>} Resolves to `true` if successful, `false` otherwise. */ export async function createDirectory(dirPath, recursive = true) { try { await fs.mkdir(dirPath, { recursive }); return true; } catch (err) { // Consider it success if directory already exists return err.code === "EEXIST"; } } /** * Safely reads and parses a JSON file with error details. * * @typeParam T - The expected type of the parsed JSON object. * @param {string} path - The path to the JSON file. * @returns {Promise<{ data: T | null; error: Error | null }>} Object with data and error properties. */ export async function safeReadJsonFile(path) { try { const content = await fs.readFile(path, "utf-8"); try { const data = JSON.parse(content); return { data, error: null }; } catch (error) { return { data: null, error: new Error(`Invalid JSON in file ${path}: ${error.message}`), }; } } catch (error) { return { data: null, error: new Error(`Failed to read file ${path}: ${error.message}`), }; } } /** * Checks if a path points to a file (not a directory). * * @param {string} path - Path to check. * @returns {Promise<boolean>} True if the path is a file, false otherwise. */ export async function isFile(path) { try { const stat = await fs.stat(path); return stat.isFile(); } catch (_a) { return false; } } /** * Gets the size of a file in bytes. * * @param {string} path - Path to the file. * @returns {Promise<number>} Size in bytes or -1 if file doesn't exist. */ export async function getFileSize(path) { try { const stat = await fs.stat(path); return stat.size; } catch (_a) { return -1; } } /** * Reads a file as a Buffer. * * @param {string} path - Path to the file. * @returns {Promise<Buffer | null>} File contents as Buffer or null if reading fails. */ export async function readFileBuffer(path) { try { return await fs.readFile(path); } catch (_a) { return null; } } //# sourceMappingURL=fs.utils.js.map