file.js

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const fileGlob = require("minimatch");
const path = require("path");
const fsp = require("./fsp");
function joinWith(dir) {
    return (file) => {
        return path.join(dir, file);
    };
}
class File {
    constructor(pathname) {
        this.dir = process.cwd();
        this.pathname = pathname;
    }
    /**
     * Synchronously determine if pathname is a directory
     *
     * @memberOf File
     * @method
     * isDirectorySync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myDirectory');
     * if (file.isDirectorySync()) {
     *    console.log('processing directory');
     * }
     */
    isDirectorySync() {
        return this.getStatsSync().isDirectory();
    }
    /**
     * Synchronously determine if pathname is a socket
     *
     * @memberOf File
     * @method
     * isSocketSync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('mysocket');
     * if (file.isSocketSync()) {
     *    console.log('processing socket');
     * }
     */
    isSocketSync() {
        return this.getStatsSync().isSocket();
    }
    /**
     * Synchronously determine if pathname is a file
     *
     * @memberOf File
     * @method
     * isFileSync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myDirectory');
     * if (file.isFileSync()) {
     *    console.log('processing file');
     * }
     */
    isFileSync() {
        return this.getStatsSync().isFile();
    }
    /**
     * Determine if pathname is a directory
     *
     * @memberOf File
     * @method
     * isDirectory
     * @return If the Promise fulfils, the fulfilment value is
     * a boolean indicating if the pathname is a directory
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myDirectory');
     * const isDirectory = await file.isDirectory((isDirectory);
     * console.log(isDirectory);
     */
    isDirectory() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.checkAsyncStats('isDirectory');
        });
    }
    /**
     * Determine if pathname is a Socket
     *
     * @memberOf File
     * @method
     * isSocket
     * @return If the Promise fulfils, the fulfilment value is
     * a boolean indicating if the pathname is a Socket
     * @example
     * import File from 'file-js';
     *
     * const file = new File('mySocket');
     * const isSocket = await file.isSocket((isSocket));
     * console.log(isSocket);
     */
    isSocket() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.checkAsyncStats('isSocket');
        });
    }
    /**
     * Determine if pathname is a file
     *
     * @memberOf File
     * @method
     * isFile
     * @return If the Promise fulfils, the fulfilment value is
     * a boolean indicating if the pathname is a file
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myDirectory');
     * const isFile = await file.isFile();
     * console.log(isFile);
     */
    isFile() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.checkAsyncStats('isFile');
        });
    }
    /**
     * Synchronously determine if pathname is a hidden file
     *
     * @memberOf File
     * @method
     * isHiddenSync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./myHiddenFile');
     * if (file.isHiddenSync()) {
     *    console.log('processing hidden file');
     * }
     */
    isHiddenSync() {
        if (!this.isDirectorySync()) {
            return this.isHiddenFile();
        }
        return this.isHiddenDirectory();
    }
    /**
     * Determine if pathname is a file
     *
     * @memberOf File
     * @method
     * isHidden
     * @return If the Promise fulfils, the fulfilment value is
     * a boolean indicating if the pathname is a hidden file
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myDirectory');
     * const isHidden = await file.isHidden();
     * console.log(isHidden);
     */
    isHidden() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!(yield this.isDirectory())) {
                return this.isHiddenFile();
            }
            return this.isHiddenDirectory();
        });
    }
    /**
     * Synchronously get list of files, if pathname is a directory
     *
     * @memberOf File
     * @method
     * getListSync
     * @return array of files
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./myHiddenFile');
     * const files = file.getListSync();
     * console.log(files);
     */
    getListSync() {
        if (this.isDirectorySync()) {
            return fs_1.readdirSync(this.pathname)
                .map((file) => {
                return path.join(this.pathname, file);
            });
        }
        return null;
    }
    /**
     * Get list of file objects, if pathname is a directory
     *
     * @memberOf File
     * @method
     * getList
     * @return a promise. If the Promise fulfils, the fulfilment value is
     * a list of pathnames
     * @example
     * import File from 'file-js';
     *
     * // get all json files
     * const file = new File('./myDirectory');
     * const files = await file.getFiles('*.json');
     * console.log(jsonFiles);
     */
    getList(glob) {
        return __awaiter(this, void 0, void 0, function* () {
            const files = yield this.getFiles(glob);
            if (files.length > 0) {
                return files.map(pathname => pathname.getName());
            }
            return [];
        });
    }
    /**
     * Get list of file objects, if pathname is a directory
     *
     * @memberOf File
     * @method
     * getFiles
     * @return a promise. If the Promise fulfils, the fulfilment value is
     * a list of File objects
     * @example
     * import File from 'file-js';
     *
     * // get last modified time of all json files
     * const file = new File('./myDirectory');
     * const files = await file.getFiles('*.json');
     * console.log(jsonFiles.map(file => file.lastModifiedSync()));
     */
    getFiles(glob) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!(yield this.isDirectory())) {
                return [];
            }
            const files = yield fsp.readdir(this.pathname);
            const results = files
                .map(joinWith(this.pathname))
                .map(pathname => new File(pathname));
            if (results.length === 0) {
                return [];
            }
            if (glob) {
                return results.filter(file => file.isMatch(glob));
            }
            return results;
        });
    }
    /**
     * Synchronously get list of file objects, if pathname is a directory
     *
     * @memberOf File
     * @method
     * getFileSync
     * @return array of files
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./myHiddenFile');
     * const files = file.getFileSync();
     * console.log(files);
     */
    getFilesSync(glob) {
        if (this.isDirectorySync()) {
            const files = this.getListSync()
                .map((pathname) => {
                return new File(pathname);
            });
            if (glob) {
                return files.filter(file => file.isMatch(glob));
            }
            return files;
        }
        return null;
    }
    /**
     * Synchronously calculate the depth of a directory
     *
     * @memberOf File
     * @method
     * getDepthSync
     * @return boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myDirectory');
     * console.log(file.getDepthSync());
     */
    getDepthSync() {
        if (!this.isDirectorySync()) {
            return this.depth(path.dirname(this.pathname));
        }
        return this.depth(this.pathname);
    }
    /**
     * Returns the pathname as a string
     *
     * @memberOf File
     * @method
     * getName
     * @return String
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myDirectory');
     * console.log(file.getName());
     */
    getName() {
        return this.pathname;
    }
    /**
     * Returns the absolutePath
     *
     * @memberOf File
     * @method
     * getAbsolutePath
     * @return String
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myFile');
     * console.log(file.getAbsolutePath());
     */
    getAbsolutePath() {
        if (path.isAbsolute(this.pathname)) {
            return this.pathname;
        }
        return [this.dir, this.pathname].join(path.sep);
    }
    /**
     * Returns the canonical path
     *
     * @memberOf File
     * @method
     * getCanonicalPath
     * @return String
     * @example
     * import File from 'file-js';
     *
     * const file = new File('myFile');
     * console.log(file.getCanonicalPath());
     */
    getCanonicalPath() {
        return path.normalize(this.getAbsolutePath());
    }
    /**
     * Returns the file extension.
     *
     * @memberOf File
     * @method
     * getPathExtension
     * @return String
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.getPathExtension()); // sh
     */
    getPathExtension() {
        return path.extname(this.pathname).substring(1);
    }
    /**
     * Returns the last modified date.
     *
     * @memberOf File
     * @method
     * lastModifiedSync
     * @return Date
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.lastModifiedSync());
     */
    lastModifiedSync() {
        return this.getStatsSync().mtime;
    }
    /**
     * Returns the last the file was accessed.
     *
     * @memberOf File
     * @method
     * lastAccessedSync
     * @return Date
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.lastAccessedSync());
     */
    lastAccessedSync() {
        return this.getStatsSync().atime;
    }
    /**
     * Returns the last the file was changed.
     *
     * @memberOf File
     * @method
     * lastChangedSync
     * @return Date
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.lastChangedSync());
     */
    lastChangedSync() {
        return this.getStatsSync().ctime;
    }
    /**
     * Returns the size of the file.
     *
     * @memberOf File
     * @method
     * sizeSync
     * @return Date
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.sizeSync());
     */
    sizeSync() {
        return this.getStatsSync().size;
    }
    /**
     * Returns true if the file is writable
     *
     * @memberOf File
     * @method
     * isWritable
     * @return Boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.isWritable());
     */
    isWritable() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.access(fs_1.constants.W_OK);
        });
    }
    /**
     * Returns true if the file is readable
     *
     * @memberOf File
     * @method
     * isReadable
     * @return Boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.isReadable());
     */
    isReadable() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.access(fs_1.constants.R_OK);
        });
    }
    /**
     * Returns true if the file is executable
     *
     * @memberOf File
     * @method
     * isExecutable
     * @return Boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.sizeSync());
     */
    isExecutable() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.access(fs_1.constants.X_OK);
        });
    }
    /**
     * Deletes the file.
     *
     * @memberOf File
     * @method
     * delete
     * @return void
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * file.delete();
     */
    delete() {
        return __awaiter(this, void 0, void 0, function* () {
            return fsp.unlink(this.pathname);
        });
    }
    /**
     * Recursively delete the folder and contents.
     *
     * @memberOf File
     * @method
     * deleteRecursively
     * @return void
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./dir/');
     * file.deleteRecursively();
     */
    deleteRecursively(dirPath = this.pathname) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.exists()) {
                const files = yield fsp.readdir(dirPath);
                for (const file of files) {
                    const curPath = `${dirPath}/${file}`;
                    const isDirectory = (yield fsp.lstat(curPath)).isDirectory();
                    const isFile = (yield fsp.lstat(curPath)).isFile();
                    if (isDirectory) {
                        const hasFiles = (yield fsp.readdir(curPath)).length > 0;
                        if (hasFiles) {
                            yield this.deleteRecursively(curPath);
                        }
                    }
                    if (isFile) {
                        yield fsp.unlink(curPath);
                    }
                }
                yield fsp.rmdir(dirPath);
            }
        });
    }
    /**
     * Recursively copy the folder and contents.
     *
     * @memberOf File
     * @method
     * copyRecursively
     * @return : void
     * @example
     * import File from 'file-js';
     *
     * const file = new File('dir/');
     * file.copyRecursively('destination/');
     */
    copyRecursively(destination, opts = { overwrite: false }) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!opts.source) {
                opts.source = this.pathname;
            }
            // check if destination directory already exists
            const directoryExists = fs_1.existsSync(destination);
            if (directoryExists && !opts.overwrite) {
                throw new Error(`Directory: "${destination}" already exists.`);
            }
            else if (directoryExists && opts.overwrite) {
                yield this.deleteRecursively(destination);
            }
            // make destination directory
            yield fsp.mkdir(destination);
            // get source directory files
            const files = yield fsp.readdir(opts.source);
            // copy source directory contents into destination directory
            for (const i of files.keys()) {
                const current = yield fsp.stat(this.createPath(opts.source, files[i]));
                if (current.isDirectory()) {
                    const newSource = this.createPath(opts.source, files[i]);
                    const newDestination = this.createPath(destination, files[i]);
                    yield this.copyRecursively(newDestination, { overwrite: opts.overwrite, source: newSource });
                }
                else if (current.isSymbolicLink()) {
                    const symlink = yield fsp.readLink(this.createPath(opts.source, files[i]));
                    yield fsp.symLink(symlink, this.createPath(destination, files[i]));
                }
                else {
                    this.copy(opts.source, destination, files[i]);
                }
            }
        });
    }
    /**
     * Returns true if the file exists
     *
     * @memberOf File
     * @method
     * exists
     * @return Boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.exists());
     */
    exists() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.access(fs_1.constants.R_OK);
        });
    }
    /**
     * Returns true if the given file glob matches
     *
     * @memberOf File
     * @method
     * isMatch
     * @return Boolean
     * @example
     * import File from 'file-js';
     *
     * const file = new File('./tmp.sh');
     * console.log(file.isMatch());
     */
    isMatch(globPattern) {
        const glob = new fileGlob.Minimatch(globPattern, {
            matchBase: true
        });
        return glob.match(this.pathname);
    }
    getStatsSync() {
        return fs_1.statSync(this.pathname);
    }
    getStats() {
        return __awaiter(this, void 0, void 0, function* () {
            return fsp.stat(this.pathname);
        });
    }
    isHiddenFile() {
        return (/^\./).test(path.basename(this.pathname));
    }
    isHiddenDirectory() {
        return (/(^|\/)\.[^\/\.]/g).test(this.pathname);
    }
    depth(pathname) {
        return pathname.split(path.sep).length - 1;
    }
    access(permission) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                yield fsp.access(this.pathname, permission);
            }
            catch (err) {
                return false;
            }
            return true;
        });
    }
    checkAsyncStats(type) {
        return __awaiter(this, void 0, void 0, function* () {
            const stats = yield this.getStats();
            return stats[type]();
        });
    }
    copy(src, dest, files) {
        const sourcePath = this.createPath(src, files);
        const destPath = this.createPath(dest, files);
        const oldFile = fs_1.createReadStream(sourcePath);
        const newFile = fs_1.createWriteStream(destPath);
        oldFile.pipe(newFile);
    }
    createPath(directory, file) {
        return path.join(directory, file);
    }
}
exports.File = File;