UNPKG

@push.rocks/smartfile

Version:

Provides comprehensive tools for efficient file management in Node.js using TypeScript, including handling streams, virtual directories, and various file operations.

568 lines 39.6 kB
import * as plugins from './plugins.js'; import * as interpreter from './interpreter.js'; import { SmartFile } from './classes.smartfile.js'; import * as memory from './memory.js'; /*=============================================================== ============================ Checks ============================= ===============================================================*/ /** * * @param filePath * @returns {boolean} */ export const fileExistsSync = (filePath) => { let fileExistsBool = false; try { plugins.fsExtra.readFileSync(filePath); fileExistsBool = true; } catch (err) { fileExistsBool = false; } return fileExistsBool; }; /** * * @param filePath * @returns {any} */ export const fileExists = async (filePath) => { const done = plugins.smartpromise.defer(); plugins.fs.access(filePath, 4, (err) => { err ? done.resolve(false) : done.resolve(true); }); return done.promise; }; /** * Checks if given path points to an existing directory */ export const isDirectory = (pathArg) => { try { return plugins.fsExtra.statSync(pathArg).isDirectory(); } catch (err) { return false; } }; /** * Checks if given path points to an existing directory */ export const isDirectorySync = (pathArg) => { try { return plugins.fsExtra.statSync(pathArg).isDirectory(); } catch (err) { return false; } }; /** * Checks if a given path points to an existing file */ export const isFile = (pathArg) => { return plugins.fsExtra.statSync(pathArg).isFile(); }; /*=============================================================== ============================ FS ACTIONS ========================= ===============================================================*/ /** * copies a file or directory from A to B on the local disk */ export const copy = async (fromArg, toArg, optionsArg) => { if (optionsArg?.replaceTargetDir && isDirectory(fromArg) && isDirectory(toArg)) { await remove(toArg); } return await plugins.fsExtra.copy(fromArg, toArg, optionsArg); }; /** * copies a file or directory SYNCHRONOUSLY from A to B on the local disk */ export const copySync = (fromArg, toArg, optionsArg) => { if (optionsArg?.replaceTargetDir && isDirectory(fromArg) && isDirectory(toArg)) { removeSync(toArg); } return plugins.fsExtra.copySync(fromArg, toArg, optionsArg); }; /** * ensures that a directory is in place */ export const ensureDir = async (dirPathArg) => { await plugins.fsExtra.ensureDir(dirPathArg); }; /** * ensures that a directory is in place */ export const ensureDirSync = (dirPathArg) => { plugins.fsExtra.ensureDirSync(dirPathArg); }; /** * ensure an empty directory * @executes ASYNC */ export const ensureEmptyDir = async (dirPathArg) => { await plugins.fsExtra.ensureDir(dirPathArg); await plugins.fsExtra.emptyDir(dirPathArg); }; /** * ensure an empty directory * @executes SYNC */ export const ensureEmptyDirSync = (dirPathArg) => { plugins.fsExtra.ensureDirSync(dirPathArg); plugins.fsExtra.emptyDirSync(dirPathArg); }; /** * ensures that a file is on disk * @param filePath the filePath to ensureDir * @param the fileContent to place into a new file in case it doesn't exist yet * @returns Promise<void> * @exec ASYNC */ export const ensureFile = async (filePathArg, initFileStringArg) => { ensureFileSync(filePathArg, initFileStringArg); }; /** * ensures that a file is on disk * @param filePath the filePath to ensureDir * @param the fileContent to place into a new file in case it doesn't exist yet * @returns Promise<void> * @exec SYNC */ export const ensureFileSync = (filePathArg, initFileStringArg) => { if (fileExistsSync(filePathArg)) { return null; } else { memory.toFsSync(initFileStringArg, filePathArg); } }; /** * removes a file or folder from local disk */ export const remove = async (pathArg) => { await plugins.fsExtra.remove(pathArg); }; /** * removes a file SYNCHRONOUSLY from local disk */ export const removeSync = (pathArg) => { plugins.fsExtra.removeSync(pathArg); }; /** * removes an array of filePaths from disk */ export const removeMany = async (filePathArrayArg) => { const promiseArray = []; for (const filePath of filePathArrayArg) { promiseArray.push(remove(filePath)); } await Promise.all(promiseArray); }; /** * like removeFilePathArray but SYNCHRONOUSLY */ export const removeManySync = (filePathArrayArg) => { for (const filePath of filePathArrayArg) { removeSync(filePath); } }; /*=============================================================== ============================ Write/Read ========================= ===============================================================*/ /** * reads a file content to an object * good for JSON, YAML, TOML, etc. * @param filePathArg * @param fileTypeArg * @returns {any} */ export const toObjectSync = (filePathArg, fileTypeArg) => { const fileString = plugins.fsExtra.readFileSync(filePathArg, 'utf8'); let fileType; fileTypeArg ? (fileType = fileTypeArg) : (fileType = interpreter.filetype(filePathArg)); try { return interpreter.objectFile(fileString, fileType); } catch (err) { err.message = `Failed to read file at ${filePathArg}` + err.message; throw err; } }; /** * reads a file content to a String */ export const toStringSync = (filePath) => { const encoding = plugins.smartmime.getEncodingForPathSync(filePath); let fileString = plugins.fsExtra.readFileSync(filePath, encoding); if (Buffer.isBuffer(fileString)) { fileString = fileString.toString('binary'); } return fileString; }; export const toBuffer = async (filePath) => { return plugins.fsExtra.readFile(filePath); }; export const toBufferSync = (filePath) => { return plugins.fsExtra.readFileSync(filePath); }; /** * Creates a Readable Stream from a file path. * @param filePath The path to the file. * @returns {fs.ReadStream} */ export const toReadStream = (filePath) => { if (!fileExistsSync(filePath)) { throw new Error(`File does not exist at path: ${filePath}`); } return plugins.fsExtra.createReadStream(filePath); }; export const fileTreeToHash = async (dirPathArg, miniMatchFilter) => { const fileTreeObject = await fileTreeToObject(dirPathArg, miniMatchFilter); let combinedString = ''; for (const smartfile of fileTreeObject) { combinedString += await smartfile.getHash(); } const hash = await plugins.smarthash.sha256FromString(combinedString); return hash; }; /** * creates a smartfile array from a directory * @param dirPathArg the directory to start from * @param miniMatchFilter a minimatch filter of what files to include */ export const fileTreeToObject = async (dirPathArg, miniMatchFilter) => { // handle absolute miniMatchFilter let dirPath; if (plugins.path.isAbsolute(miniMatchFilter)) { dirPath = '/'; } else { dirPath = plugins.smartpath.transform.toAbsolute(dirPathArg); } const fileTree = await listFileTree(dirPath, miniMatchFilter); const smartfileArray = []; for (const filePath of fileTree) { const readPath = (() => { if (!plugins.path.isAbsolute(filePath)) { return plugins.path.join(dirPath, filePath); } else { return filePath; } })(); const fileBuffer = plugins.fs.readFileSync(readPath); // push a read file as Smartfile smartfileArray.push(new SmartFile({ contentBuffer: fileBuffer, base: dirPath, path: filePath, })); } return smartfileArray; }; /** * lists Folders in a directory on local disk * @returns Promise with an array that contains the folder names */ export const listFolders = async (pathArg, regexFilter) => { return listFoldersSync(pathArg, regexFilter); }; /** * lists Folders SYNCHRONOUSLY in a directory on local disk * @returns an array with the folder names as strings */ export const listFoldersSync = (pathArg, regexFilter) => { let folderArray = plugins.fsExtra.readdirSync(pathArg).filter((file) => { return plugins.fsExtra.statSync(plugins.path.join(pathArg, file)).isDirectory(); }); if (regexFilter) { folderArray = folderArray.filter((fileItem) => { return regexFilter.test(fileItem); }); } return folderArray; }; /** * lists Files in a directory on local disk * @returns Promise */ export const listFiles = async (pathArg, regexFilter) => { return listFilesSync(pathArg, regexFilter); }; /** * lists Files SYNCHRONOUSLY in a directory on local disk * @returns an array with the folder names as strings */ export const listFilesSync = (pathArg, regexFilter) => { let fileArray = plugins.fsExtra.readdirSync(pathArg).filter((file) => { return plugins.fsExtra.statSync(plugins.path.join(pathArg, file)).isFile(); }); if (regexFilter) { fileArray = fileArray.filter((fileItem) => { return regexFilter.test(fileItem); }); } return fileArray; }; /** * lists all items (folders AND files) in a directory on local disk * @returns Promise<string[]> */ export const listAllItems = async (pathArg, regexFilter) => { return listAllItemsSync(pathArg, regexFilter); }; /** * lists all items (folders AND files) in a directory on local disk * @returns an array with the folder names as strings * @executes SYNC */ export const listAllItemsSync = (pathArg, regexFilter) => { let allItmesArray = plugins.fsExtra.readdirSync(pathArg).filter((file) => { return plugins.fsExtra.statSync(plugins.path.join(pathArg, file)).isFile(); }); if (regexFilter) { allItmesArray = allItmesArray.filter((fileItem) => { return regexFilter.test(fileItem); }); } return allItmesArray; }; /** * lists a file tree using a miniMatch filter * note: if the miniMatch Filter is an absolute path, the cwdArg will be omitted * @returns Promise<string[]> string array with the absolute paths of all matching files */ export const listFileTree = async (dirPathArg, miniMatchFilter, absolutePathsBool = false) => { // handle absolute miniMatchFilter let dirPath; if (plugins.path.isAbsolute(miniMatchFilter)) { dirPath = '/'; } else { dirPath = dirPathArg; } const options = { cwd: dirPath, nodir: true, dot: true, }; // Fix inconsistent **/* glob behavior across systems // Some glob implementations don't include root-level files when using **/* // To ensure consistent behavior, we expand **/* patterns to include both root and nested files let patterns; if (miniMatchFilter.startsWith('**/')) { // Extract the part after **/ (e.g., "*.ts" from "**/*.ts") const rootPattern = miniMatchFilter.substring(3); // Use both the root pattern and the original pattern to ensure we catch everything patterns = [rootPattern, miniMatchFilter]; } else { patterns = [miniMatchFilter]; } // Collect results from all patterns const allFiles = new Set(); for (const pattern of patterns) { const files = await plugins.glob.glob(pattern, options); files.forEach(file => allFiles.add(file)); } let fileList = Array.from(allFiles).sort(); if (absolutePathsBool) { fileList = fileList.map((filePath) => { return plugins.path.resolve(plugins.path.join(dirPath, filePath)); }); } return fileList; }; /** * Watches for file stability before resolving the promise. * Ensures that the directory/file exists before setting up the watcher. * * **New behavior**: If the given path is a directory, this function will: * 1. Wait for that directory to exist (creating a timeout if needed). * 2. Watch the directory until at least one file appears. * 3. Then wait for the first file in the directory to stabilize before resolving. * * @param fileOrDirPathArg The path of the file or directory to monitor. * @param timeoutMs The maximum time to wait for the file to stabilize (in milliseconds). Default is 60 seconds. * @returns A promise that resolves when the target is stable or rejects on timeout/error. */ export const waitForFileToBeReady = async (fileOrDirPathArg, timeoutMs = 60000) => { const startTime = Date.now(); /** * Ensure that a path (file or directory) exists. If it doesn't yet exist, * wait until it does (or time out). * @param pathToCheck The file or directory path to check. */ const ensurePathExists = async (pathToCheck) => { while (true) { try { await plugins.smartpromise.fromCallback((cb) => plugins.fs.access(pathToCheck, plugins.fs.constants.F_OK, cb)); return; } catch (err) { if (err.code !== 'ENOENT') { throw err; // Propagate unexpected errors } if (Date.now() - startTime > timeoutMs) { throw new Error(`Timeout waiting for path to exist: ${pathToCheck}`); } await plugins.smartdelay.delayFor(500); } } }; /** * Checks if a file (not directory) is stable by comparing sizes * across successive checks. * @param filePathArg The path of the file to check. * @returns A promise that resolves once the file stops changing. */ const waitForSingleFileToBeStable = async (filePathArg) => { let lastFileSize = -1; let fileIsStable = false; // We'll create a helper for repeated stats-checking logic const checkFileStability = async () => { try { const stats = await plugins.smartpromise.fromCallback((cb) => plugins.fs.stat(filePathArg, cb)); if (stats.isDirectory()) { // If it unexpectedly turns out to be a directory here, throw throw new Error(`Expected a file but found a directory: ${filePathArg}`); } if (stats.size === lastFileSize) { fileIsStable = true; } else { lastFileSize = stats.size; fileIsStable = false; } } catch (err) { // Ignore only if file not found if (err.code !== 'ENOENT') { throw err; } } }; // Ensure file exists first await ensurePathExists(filePathArg); // Set up a watcher on the file itself const fileWatcher = plugins.fs.watch(filePathArg, { persistent: true }, async () => { if (!fileIsStable) { await checkFileStability(); } }); try { // Poll until stable or timeout while (!fileIsStable) { if (Date.now() - startTime > timeoutMs) { throw new Error(`Timeout waiting for file to stabilize: ${filePathArg}`); } await checkFileStability(); if (!fileIsStable) { await plugins.smartdelay.delayFor(1000); } } } finally { fileWatcher.close(); } }; /** * Main logic: check if we have a directory or file at fileOrDirPathArg. * If directory, wait for first file in the directory to appear and stabilize. * If file, do the old single-file wait logic. */ const statsForGivenPath = await (async () => { try { await ensurePathExists(fileOrDirPathArg); return await plugins.smartpromise.fromCallback((cb) => plugins.fs.stat(fileOrDirPathArg, cb)); } catch (err) { // If there's an error (including timeout), just rethrow throw err; } })(); if (!statsForGivenPath.isDirectory()) { // It's a file – just do the single-file stability wait await waitForSingleFileToBeStable(fileOrDirPathArg); return; } // Otherwise, it's a directory. Wait for the first file inside to appear and be stable const dirPath = fileOrDirPathArg; // Helper to find the first file in the directory if it exists const getFirstFileInDirectory = async () => { const entries = await plugins.smartpromise.fromCallback((cb) => plugins.fs.readdir(dirPath, cb)); // We only want actual files, not subdirectories for (const entry of entries) { const entryPath = plugins.path.join(dirPath, entry); const entryStats = await plugins.smartpromise.fromCallback((cb) => plugins.fs.stat(entryPath, cb)); if (entryStats.isFile()) { return entryPath; } } return null; }; // Wait for a file to appear in this directory let firstFilePath = await getFirstFileInDirectory(); if (!firstFilePath) { // Set up a watcher on the directory to see if a file appears const directoryWatcher = plugins.fs.watch(dirPath, { persistent: true }); try { // We'll poll for the existence of a file in that directory while (!firstFilePath) { if (Date.now() - startTime > timeoutMs) { throw new Error(`Timeout waiting for a file to appear in directory: ${dirPath}`); } firstFilePath = await getFirstFileInDirectory(); if (!firstFilePath) { await plugins.smartdelay.delayFor(1000); } } } finally { directoryWatcher.close(); } } // Now that we have a file path, wait for that file to stabilize await waitForSingleFileToBeStable(firstFilePath); }; /** * writes string or Smartfile to disk. * @param fileArg * @param fileNameArg * @param fileBaseArg */ export let toFs = async (fileContentArg, filePathArg, optionsArg = {}) => { const done = plugins.smartpromise.defer(); // check args if (!fileContentArg || !filePathArg) { throw new Error('expected valid arguments'); } // prepare actual write action let fileContent; let fileEncoding = 'utf8'; let filePath = filePathArg; // handle Smartfile if (fileContentArg instanceof SmartFile) { fileContent = fileContentArg.contentBuffer; // handle options if (optionsArg.respectRelative) { filePath = plugins.path.join(filePath, fileContentArg.path); } } else if (Buffer.isBuffer(fileContentArg)) { fileContent = fileContentArg; fileEncoding = 'binary'; } else if (typeof fileContentArg === 'string') { fileContent = fileContentArg; } else { throw new Error('fileContent is neither string nor Smartfile'); } await ensureDir(plugins.path.parse(filePath).dir); plugins.fsExtra.writeFile(filePath, fileContent, { encoding: fileEncoding }, done.resolve); return await done.promise; }; export const stat = async (filePathArg) => { return plugins.fsPromises.stat(filePathArg); }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9mcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssV0FBVyxNQUFNLGtCQUFrQixDQUFDO0FBRWhELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUVuRCxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUV0Qzs7aUVBRWlFO0FBRWpFOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsQ0FBQyxRQUFRLEVBQVcsRUFBRTtJQUNsRCxJQUFJLGNBQWMsR0FBWSxLQUFLLENBQUM7SUFDcEMsSUFBSSxDQUFDO1FBQ0gsT0FBTyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdkMsY0FBYyxHQUFHLElBQUksQ0FBQztJQUN4QixDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLGNBQWMsR0FBRyxLQUFLLENBQUM7SUFDekIsQ0FBQztJQUNELE9BQU8sY0FBYyxDQUFDO0FBQ3hCLENBQUMsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsS0FBSyxFQUFFLFFBQVEsRUFBb0IsRUFBRTtJQUM3RCxNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBVyxDQUFDO0lBQ25ELE9BQU8sQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRTtRQUNyQyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDakQsQ0FBQyxDQUFDLENBQUM7SUFDSCxPQUFPLElBQUksQ0FB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