UNPKG

@supercharge/fs

Version:

Async filesystem methods for Node.js

483 lines (482 loc) 14.8 kB
'use strict'; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const recursive_readdir_1 = __importDefault(require("recursive-readdir")); const helper_1 = require("./helper"); const goodies_1 = require("@supercharge/goodies"); const fs_extra_1 = __importDefault(require("fs-extra")); const proper_lockfile_1 = __importDefault(require("proper-lockfile")); exports.default = Object.assign({}, fs_extra_1.default, { /** * Returns the file size in bytes of the file located at `path`. * * @param {String} path * * @returns {Integer} */ async size(path) { return (0, goodies_1.upon)(await fs_extra_1.default.stat(path), (stat) => { return stat.size; }); }, /** * Retrieve the time when `file` was last modified. * * @param {String} file * * @returns {Date} */ async lastModified(file) { return (0, goodies_1.upon)(await fs_extra_1.default.stat(file), (stat) => { return stat.mtime; }); }, /** * Retrieve the time when `file` was last accessed. * * @param {String} file * * @returns {Date} */ async lastAccessed(file) { return (0, goodies_1.upon)(await fs_extra_1.default.stat(file), (stat) => { return stat.atime; }); }, /** * Change the file system timestamps of the * referenced `path`. Updates the last * accessed and last modified properties. * * @param {String} path * @param {Number} lastAccessed * @param {Number} lastModified * * @throws */ async updateTimestamps(path, lastAccessed, lastModified) { if (!(0, helper_1.isDate)(lastAccessed)) { throw new Error(`Updating the last accessed timestamp for ${path} requires an instance of "Date". Received ${typeof lastAccessed}`); } if (!(0, helper_1.isDate)(lastModified)) { throw new Error(`Updating the last modified timestamp for ${path} requires an instance of "Date". Received ${typeof lastAccessed}`); } await fs_extra_1.default.utimes(path, lastAccessed, lastModified); }, /** * Test the user's permissions for the given `path` which can * be a file or directory. The `mode` argument is an optional * integer to specify the accessibility level. * * @param {String} path - file or directory path * @param {Integer} mode - defaults to `fs.constants.F_OK` * * @returns {Boolean} * * @throws */ async canAccess(path, mode = fs_extra_1.default.constants.F_OK) { try { await fs_extra_1.default.access(path, mode); return true; } catch { return false; } }, /** * Shortcut for `pathExists` determining whether a given file or * directory exists at the given `path` on the file system. * * @param {String} path * * @returns {Boolean} */ async exists(path) { return await fs_extra_1.default.pathExists(path); }, /** * Determine wether the given `path` does not exists. * * @param {String} path * * @returns {Boolean} */ async notExists(path) { return !await this.exists(path); }, /** * Updates the access and modification times of the given `file` current * time. This method creates the `file` if it doesn’t exist. * * @param {String} file */ async touch(file) { await fs_extra_1.default.ensureFile(file); const now = new Date(); await this.updateTimestamps(file, now, now); }, /** * Read the entire content of `file`. If no `encoding` is * specified, the raw buffer is returned. If `encoding` is * an object, it allows the `encoding` and `flag` options. * * @param {String} file * @param {String|Object} encoding * * @returns {String} */ async readFile(file, encoding) { if (encoding === undefined) { console.log('"Fs.readFile(file)" to retrieve the file’s content as string is deprecated. Use "Fs.content(file)" instead.'); } return await fs_extra_1.default.readFile(file, encoding == null ? 'utf8' : encoding); }, /** * Returns the content of the given `file` as a string. * * @param {String} file * * @returns {String} */ async content(file) { return await fs_extra_1.default.readFile(file, 'utf8'); }, /** * Returns an array of file names containing the files that are available * in the given directory `path`. This method excludes the paths `.` and * `..` and does not read files recursively in available subdirectories. * * @param {String} path * * @returns {Array} */ async files(path) { return await fs_extra_1.default.readdir(path); }, /** * Returns an array of file names of all files, even recursive files in the given * directory `path`. This method excludes the paths `.`, `..`, and dotfiles. * * @param {String} path * @param {ReadFileOptions} options * * @returns {Array} */ async allFiles(path, options) { const { ignore } = options == null ? { ignore: [] } : options; return await (0, recursive_readdir_1.default)(path, [].concat(ignore || [])); }, /** * Write the given `content` to the file` and create * any parent directories if not existent. * * @param {String} path * @param {String} content * @param {WriteFileOptions} options */ async writeFile(file, content, options) { return await fs_extra_1.default.outputFile(file, content, options); }, /** * Removes a `file` from the file system. * * @param {String} file */ async removeFile(file) { return await fs_extra_1.default.remove(file); }, /** * Ensures that the directory exists. If the directory * structure does not exist, it is created. * Like `mkdir -p`. * * @param {String} dir - directory path * * @returns {String} dir - directory path */ async ensureDir(dir) { return await (0, goodies_1.tap)(dir, async () => { await fs_extra_1.default.ensureDir(dir); }); }, /** * Removes a `dir` from the file system.The directory * can have content. Content in the directory will * be removed as well, like `rm -rf`. * * @param {String} dir - directory path */ async removeDir(dir) { return await fs_extra_1.default.remove(dir); }, /** * Changes the permissions of a `file`. * The `mode` is a numeric bitmask and * can be an integer or string. * * @param {String} file * @param {String|Integer} mode */ async chmod(file, mode) { return await fs_extra_1.default.chmod(file, parseInt(mode, 8)); }, /** * Ensures that the symlink from source to * destination exists. If the directory * structure does not exist, it is created. * * @param {String} src * @param {String} dest * @param {String} type */ async ensureSymlink(src, dest, type = 'file') { return await fs_extra_1.default.ensureSymlink(src, dest, type); }, /** * Acquire a file lock on the specified `file` path with the given `options`. * If the `file` is already locked, this method won't throw an error and * instead just move on. * * @param {String} file * @param {Object} options * * @returns {Function} release function */ async lock(file, options) { if (await this.isNotLocked(file, options)) { await proper_lockfile_1.default.lock(file, options); } }, /** * Release an existent lock for the `file` and given `options`. If the `file` * isn't locked, this method won't throw an error and just move on. * * @param {String} file */ async unlock(file, options) { if (await this.isLocked(file, options)) { await proper_lockfile_1.default.unlock(file, options); } }, /** * Check if the `file` is locked and not stale. * * @param {String} file * @param {Object} options * * @returns {Boolean} */ async isLocked(file, options) { return await proper_lockfile_1.default.check(file, options); }, /** * Check if the `file` is not locked and not stale. * * @param {String} file * @param {Object} options * * @returns {Boolean} */ async isNotLocked(file, options) { return !await this.isLocked(file, options); }, /** * Create a random temporary file path you can write to. * The operating system will clean up the temporary * files automatically, probably after some days. * * @param {Object} options * * @returns {String} */ async tempFile(name) { const filename = name == null ? (0, helper_1.randomString)() : name; const file = path_1.default.resolve(await this.tempDir(), filename); return await (0, goodies_1.tap)(file, async () => { await fs_extra_1.default.ensureFile(file); }); }, /** * Create a temporary directory path which will be cleaned up by the operating system. * * @returns {String} */ async tempDir() { return await this.ensureDir(await this.tempPath()); }, /** * Returns the path to the user’s home directory. You may pass a `path` to which * the function should resolve in the user’s home directory. This method does * **not** ensure that the resolved path exists. Please do that yourself. * * @param {String} path * * @returns {String} */ homeDir(path) { return path ? path_1.default.resolve(os_1.default.homedir(), path) : os_1.default.homedir(); }, /** * Generates a random, temporary path on the filesystem. * * @returns {String} */ async tempPath() { return path_1.default.resolve(await this.realPath(os_1.default.tmpdir()), (0, helper_1.randomString)()); }, /** * Returns the fully resolve, absolute file path to the given `path`. * Resolves any relative paths, like `..` or `.`, and symbolic links. * * @param {String} path * @param {Object} cache * * @returns {String} */ async realPath(path, cache) { return await fs_extra_1.default.realpath(path, cache); }, /** * Returns the extension of `file`. For example, returns `.html` * for the HTML file located at `/path/to/index.html`. * * @param {String} file * * @returns {String} */ extension(file) { return path_1.default.extname(file); }, /** * Returns the trailing name component from a file path. For example, * returns `file.png` from the path `/home/user/file.png`. * * @param {String} path * @param {String} extension * * @returns {String} */ basename(path, extension) { return path_1.default.basename(path, extension); }, /** * Returns the file name without extension. * * @param {String} file * * @returns {String} */ filename(file) { return path_1.default.parse(file).name; }, /** * Returns the directory name of the given `path`. * For example, a file path of `foo/bar/baz/file.txt` * returns `foo/bar/baz`. * * @param {String} path * * @returns {String} */ dirname(path) { return path_1.default.dirname(path); }, /** * Determine whether the given `path` is a file. * * @param {String} path * * @returns {Boolean} */ async isFile(path) { return (0, goodies_1.upon)(await fs_extra_1.default.stat(path), (stats) => { return stats.isFile(); }); }, /** * Determine whether the given `path` is a directory. * * @param {String} path * * @returns {Boolean} */ async isDirectory(path) { return (0, goodies_1.upon)(await fs_extra_1.default.stat(path), (stats) => { return stats.isDirectory(); }); }, /** * Determine whether a the given `path` is a socket. * * @param {String} path * * @returns {Boolean} */ async isSocket(path) { return (0, goodies_1.upon)(await fs_extra_1.default.stat(path), (stats) => { return stats.isSocket(); }); }, /** * Determine whether a the given `path` is a symbolic link. * * @param {PathLike} path * * @returns {Boolean} */ async isSymLink(path) { return (0, goodies_1.upon)(await fs_extra_1.default.lstat(path), (stats) => { return stats.isSymbolicLink(); }); }, /** * Append the given `content` to a `file`. This method * creates the `file` if it does not exist yet. * * @param {PathLike} file * @param {String|Buffer} content * @param {String|Object} options */ async append(file, content, options) { await fs_extra_1.default.appendFile(file, content, options); }, /** * Append the given `content` in a new line to the given `file`. * This method creates the `file` if it does not exist yet. * * @param {PathLike} file * @param {String|Buffer} content * @param {String|Object} options */ async appendLine(file, content, options) { await this.append(file, os_1.default.EOL, options); await this.append(file, content, options); }, /** * Determine whether the given `path` points to an empty directory. In comparison to the * `Fs.emptyDir(path)` method, this `Fs.isEmptyDir(path)` method doesn’t load all files * into memory. It opens the folder as a stream and checks if at least one file exists. * * @param {String} path * * @returns {Boolean} */ async isEmptyDir(path) { try { const dirent = await fs_extra_1.default.opendir(path); const value = await dirent.read(); await dirent.close(); return value === null; } catch (error) { return false; } } });