# 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;