@supercharge/fs
Version:
Async filesystem methods for Node.js
483 lines (482 loc) • 14.8 kB
JavaScript
;
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;
}
}
});