UNPKG

@nasriya/atomix

Version:

Composable helper functions for building reliable systems

177 lines (176 loc) 7.16 kB
import path from 'path'; import runtime from '../runtime/runtime.js'; import mimes from '../http/mimes/mimes.js'; import valueIs from '../../valueIs.js'; class PathUtils { /** * Normalizes the given path by resolving it to an absolute path and converting * it to lowercase if the current platform is Windows. * @param path_ The path to normalize. * @returns The normalized path. * @since v1.0.0 */ normalizePath(path_) { const resolved = path.resolve(path.normalize(path_)); return runtime.platform.isWindows() ? resolved.toLowerCase() : resolved; } /** * Returns a sanitized and safe file or folder name by replacing or removing illegal characters. * Illegal characters differ between platforms, so this method handles Windows and POSIX systems. * * @param name - The file or folder name to sanitize. * @param replacement - The character(s) to replace illegal characters with. Defaults to '_'. * @returns The sanitized safe name. * @since v1.0.0 * @example * const safeName = sanitizeName('my*illegal:file?.txt'); * // safeName === 'my_illegal_file_.txt' */ sanitizeName(name, replacement = '_') { if (typeof name !== 'string') throw new TypeError('Name must be a string'); // Characters illegal on Windows filenames: // < > : " / \ | ? * and also control chars 0-31 // POSIX forbids '/' in names const illegalRe = runtime.platform.isWindows() ? /[<>:"/\\|?*\x00-\x1F]/g : /[/\x00]/g; // Also forbid trailing dots or spaces on Windows let sanitized = name.replace(illegalRe, replacement); if (runtime.platform.isWindows()) { sanitized = sanitized.replace(/[. ]+$/, ''); // remove trailing dots/spaces } // Prevent empty names if (sanitized.length === 0) sanitized = '_'; return sanitized; } /** * Determines if the given child path is a sub-path of the given parent path. * * @param childPath - The path to check as a sub-path. * @param parentPath - The path to check as the parent. * @returns True if the child path is a sub-path of the parent path, false otherwise. * @since v1.0.0 * @example * const isSub = isSubPath('/path/to/child', '/path/to'); * isSub is true */ isSubPath(childPath, parentPath) { const normalizedChild = this.normalizePath(childPath); const normalizedParent = this.normalizePath(parentPath); const relative = path.relative(normalizedParent, normalizedChild); return relative && !relative.startsWith('..') && !path.isAbsolute(relative) ? true : false; } /** * Gets the filename of the given path without the file extension. * @param filePath - The path to get the filename from. * @returns The filename without extension. * @since v1.0.0 * @example * const filename = getFileNameWithoutExtension('/path/to/file.txt'); * filename is 'file' */ getFileNameWithoutExtension(filePath) { filePath = this.normalizePath(filePath); return path.basename(filePath, path.extname(filePath)); } /** * Changes the file extension of the given path to the specified new extension. * @param filePath - The path to change the extension of. * @param newExt - The new file extension to set. Must be a valid file extension. * @returns The modified path with the new extension. * @throws Error if the provided new extension is not a valid file extension. * @since v1.0.0 * @example * const newFilePath = changeExtension('/path/to/file.txt', '.json'); * newFilePath is '/path/to/file.json' */ changeExtension(filePath, newExt) { if (!mimes.isValid.extension(newExt)) throw new Error(`Invalid file extension: ${newExt}`); filePath = this.normalizePath(filePath); return filePath.replace(path.extname(filePath), newExt); } /** * Checks if the given string is a valid file path. * * On Windows, this checks that the path does not contain any of the following characters: * - `<>:"|?*` * - ASCII control characters (characters with code points less than 32) * * On Unix-like platforms, this only checks that the path is not empty and does not contain the null byte (`\0`). * * @param path_ - The path to check. * @returns True if the path is valid, false otherwise. * @since v1.0.0 */ isValidPath(path_) { if (!path_ || typeof path_ !== 'string') return false; // Basic invalid Windows path characters (adjust as needed) const invalidChars = /[<>:"|?*\x00-\x1F]/; if (runtime.platform.isWindows()) { return !invalidChars.test(path_); } // On Unix-like, just check if it's not empty and no null byte return !path_.includes('\0'); } /** * Gets the relative path from the current working directory to the given path. * * @param path_ - The path to get the relative path from the current working directory. * @returns The relative path from the current working directory to the given path. * @since v1.0.0 * @example * const relativePath = pathUtils.relativeToCwd('/Users/username/Documents/file.txt'); * relativePath is 'Users/username/Documents/file.txt' */ relativeToCwd(path_) { const cwd = process.cwd(); const relative = path.relative(cwd, path_); const normalized = path.normalize(relative); return runtime.platform.isWindows() ? normalized.toLowerCase() : normalized; } /** * Heuristically determines if the given string is likely a file path. * * @param str - The string to check. * @returns True if the string is likely a file path, false otherwise. * @since v1.0.17 * @example * const isLikelyPath = atomix.path.isLikelyPath('./foo/bar.txt'); * console.log(isLikelyPath); // true */ isLikelyPath(str) { // Must not be empty or only whitespace if (!valueIs.string(str) || valueIs.emptyString(str)) { return false; } // Skipping URI schemes if (/^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(str)) { return false; } // Absolute path check (e.g., /foo/bar or C:\foo\bar) if (path.isAbsolute(str)) { return true; } // Has OS path separators (e.g., / or \) if (str.includes(path.sep)) return true; // Has typical file extensions if (mimes.extensions.some(ext => str.endsWith(ext))) { return true; } // Dot-prefixed (relative paths) like ./ or ../ if (str.startsWith(`.${path.sep}`) || str.startsWith(`..${path.sep}`)) { return true; } if (runtime.platform.isWindows() && /^[a-zA-Z]:\\/.test(str)) { return true; } return false; } } const pathUtils = new PathUtils; export default pathUtils;