(function() {
  var Module, async, fs, fsPlus, isPathValid, lstatSyncNoException, mkdirp, path, rimraf, statSyncNoException, _,
    __slice = [].slice;

  fs = require('fs');

  Module = require('module');

  path = require('path');

  _ = require('underscore-plus');

  async = require('async');

  mkdirp = require('mkdirp');

  rimraf = require('rimraf');

  fsPlus = {
    getHomeDirectory: function() {
      if (process.platform === 'win32') {
        return process.env.USERPROFILE;
      } else {
        return process.env.HOME;
      }
    },
    absolute: function(relativePath) {
      var e, homeDir;
      if (relativePath == null) {
        return null;
      }
      homeDir = fsPlus.getHomeDirectory();
      if (relativePath === '~') {
        relativePath = homeDir;
      } else if (relativePath.indexOf('~/') === 0) {
        relativePath = "" + homeDir + (relativePath.substring(1));
      }
      try {
        return fs.realpathSync(relativePath);
      } catch (_error) {
        e = _error;
        return relativePath;
      }
    },
    normalize: function(pathToNormalize) {
      var home, normalizedPath;
      if (pathToNormalize == null) {
        return null;
      }
      normalizedPath = path.normalize(pathToNormalize.toString());
      if (home = fsPlus.getHomeDirectory()) {
        if (normalizedPath === '~') {
          normalizedPath = home;
        } else if (normalizedPath.indexOf("~" + path.sep) === 0) {
          normalizedPath = "" + home + (normalizedPath.substring(1));
        }
      }
      return normalizedPath;
    },
    getAppDataDirectory: function() {
      switch (process.platform) {
        case 'darwin':
          return fsPlus.absolute('~/Library/Application Support');
        case 'linux':
          return '/var/lib';
        case 'win32':
          return process.env.APPDATA;
        default:
          return null;
      }
    },
    isAbsolute: function(pathToCheck) {
      if (pathToCheck == null) {
        pathToCheck = '';
      }
      if (process.platform === 'win32') {
        if (pathToCheck[1] === ':') {
          return true;
        }
        if (pathToCheck[0] === '\\' && pathToCheck[1] === '\\') {
          return true;
        }
      } else {
        return pathToCheck[0] === '/';
      }
      return false;
    },
    existsSync: function(pathToCheck) {
      return isPathValid(pathToCheck) && (statSyncNoException(pathToCheck) !== false);
    },
    isDirectorySync: function(directoryPath) {
      var stat;
      if (!isPathValid(directoryPath)) {
        return false;
      }
      if (stat = statSyncNoException(directoryPath)) {
        return stat.isDirectory();
      } else {
        return false;
      }
    },
    isDirectory: function(directoryPath, done) {
      if (!isPathValid(directoryPath)) {
        return done(false);
      }
      return fs.exists(directoryPath, function(exists) {
        if (exists) {
          return fs.stat(directoryPath, function(error, stat) {
            if (error != null) {
              return done(false);
            } else {
              return done(stat.isDirectory());
            }
          });
        } else {
          return done(false);
        }
      });
    },
    isFileSync: function(filePath) {
      var stat;
      if (!isPathValid(filePath)) {
        return false;
      }
      if (stat = statSyncNoException(filePath)) {
        return stat.isFile();
      } else {
        return false;
      }
    },
    isSymbolicLinkSync: function(symlinkPath) {
      var stat;
      if (!isPathValid(symlinkPath)) {
        return false;
      }
      if (stat = lstatSyncNoException(symlinkPath)) {
        return stat.isSymbolicLink();
      } else {
        return false;
      }
    },
    isSymbolicLink: function(symlinkPath, callback) {
      if (isPathValid(symlinkPath)) {
        return fs.lstat(symlinkPath, function(error, stat) {
          return typeof callback === "function" ? callback((stat != null) && stat.isSymbolicLink()) : void 0;
        });
      } else {
        return process.nextTick(function() {
          return typeof callback === "function" ? callback(false) : void 0;
        });
      }
    },
    isExecutableSync: function(pathToCheck) {
      var stat;
      if (!isPathValid(pathToCheck)) {
        return false;
      }
      if (stat = statSyncNoException(pathToCheck)) {
        return (stat.mode & 0x1ff & 1) !== 0;
      } else {
        return false;
      }
    },
    getSizeSync: function(pathToCheck) {
      var _ref;
      if (isPathValid(pathToCheck)) {
        return (_ref = statSyncNoException(pathToCheck).size) != null ? _ref : -1;
      } else {
        return -1;
      }
    },
    listSync: function(rootPath, extensions) {
      var paths;
      if (!fsPlus.isDirectorySync(rootPath)) {
        return [];
      }
      paths = fs.readdirSync(rootPath);
      if (extensions) {
        paths = fsPlus.filterExtensions(paths, extensions);
      }
      paths = paths.sort(function(a, b) {
        return a.toLowerCase().localeCompare(b.toLowerCase());
      });
      paths = paths.map(function(childPath) {
        return path.join(rootPath, childPath);
      });
      return paths;
    },
    list: function() {
      var done, extensions, rest, rootPath;
      rootPath = arguments[0], rest = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
      if (rest.length > 1) {
        extensions = rest.shift();
      }
      done = rest.shift();
      return fs.readdir(rootPath, function(error, paths) {
        if (error != null) {
          return done(error);
        } else {
          if (extensions) {
            paths = fsPlus.filterExtensions(paths, extensions);
          }
          paths = paths.sort(function(a, b) {
            return a.toLowerCase().localeCompare(b.toLowerCase());
          });
          paths = paths.map(function(childPath) {
            return path.join(rootPath, childPath);
          });
          return done(null, paths);
        }
      });
    },
    filterExtensions: function(paths, extensions) {
      extensions = extensions.map(function(ext) {
        if (ext === '') {
          return ext;
        } else {
          return '.' + ext.replace(/^\./, '');
        }
      });
      return paths.filter(function(pathToCheck) {
        return _.include(extensions, path.extname(pathToCheck));
      });
    },
    listTreeSync: function(rootPath) {
      var onPath, paths;
      paths = [];
      onPath = function(childPath) {
        paths.push(childPath);
        return true;
      };
      fsPlus.traverseTreeSync(rootPath, onPath, onPath);
      return paths;
    },
    moveSync: function(source, target) {
      return fs.renameSync(source, target);
    },
    removeSync: function(pathToRemove) {
      return rimraf.sync(pathToRemove);
    },
    writeFileSync: function(filePath, content, options) {
      mkdirp.sync(path.dirname(filePath));
      return fs.writeFileSync(filePath, content, options);
    },
    writeFile: function(filePath, content, options, callback) {
      callback = _.last(arguments);
      return mkdirp(path.dirname(filePath), function(error) {
        if (error != null) {
          return typeof callback === "function" ? callback(error) : void 0;
        } else {
          return fs.writeFile(filePath, content, options, callback);
        }
      });
    },
    copy: function(sourcePath, destinationPath, done) {
      return mkdirp(path.dirname(destinationPath), function(error) {
        var destinationStream, sourceStream;
        if (error != null) {
          if (typeof done === "function") {
            done(error);
          }
          return;
        }
        sourceStream = fs.createReadStream(sourcePath);
        sourceStream.on('error', function(error) {
          if (typeof done === "function") {
            done(error);
          }
          return done = null;
        });
        destinationStream = fs.createWriteStream(destinationPath);
        destinationStream.on('error', function(error) {
          if (typeof done === "function") {
            done(error);
          }
          return done = null;
        });
        destinationStream.on('close', function() {
          if (typeof done === "function") {
            done();
          }
          return done = null;
        });
        return sourceStream.pipe(destinationStream);
      });
    },
    copySync: function(sourcePath, destinationPath) {
      var content, destinationFilePath, source, sourceFilePath, sources, _i, _len, _results;
      sources = fs.readdirSync(sourcePath);
      mkdirp.sync(destinationPath);
      _results = [];
      for (_i = 0, _len = sources.length; _i < _len; _i++) {
        source = sources[_i];
        sourceFilePath = path.join(sourcePath, source);
        destinationFilePath = path.join(destinationPath, source);
        if (fsPlus.isDirectorySync(sourceFilePath)) {
          _results.push(fsPlus.copySync(sourceFilePath, destinationFilePath));
        } else {
          content = fs.readFileSync(sourceFilePath);
          _results.push(fs.writeFileSync(destinationFilePath, content));
        }
      }
      return _results;
    },
    makeTreeSync: function(directoryPath) {
      if (!fsPlus.existsSync(directoryPath)) {
        return mkdirp.sync(directoryPath);
      }
    },
    makeTree: function(directoryPath, callback) {
      return fs.exists(directoryPath, function(exists) {
        if (exists) {
          return typeof callback === "function" ? callback() : void 0;
        }
        return mkdirp(directoryPath, function(error) {
          return typeof callback === "function" ? callback(error) : void 0;
        });
      });
    },
    traverseTreeSync: function(rootPath, onFile, onDirectory) {
      var traverse;
      if (onDirectory == null) {
        onDirectory = onFile;
      }
      if (!fsPlus.isDirectorySync(rootPath)) {
        return;
      }
      traverse = function(directoryPath, onFile, onDirectory) {
        var childPath, file, linkStats, stats, _i, _len, _ref;
        _ref = fs.readdirSync(directoryPath);
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          file = _ref[_i];
          childPath = path.join(directoryPath, file);
          stats = fs.lstatSync(childPath);
          if (stats.isSymbolicLink()) {
            if (linkStats = statSyncNoException(childPath)) {
              stats = linkStats;
            }
          }
          if (stats.isDirectory()) {
            if (onDirectory(childPath)) {
              traverse(childPath, onFile, onDirectory);
            }
          } else if (stats.isFile()) {
            onFile(childPath);
          }
        }
        return void 0;
      };
      return traverse(rootPath, onFile, onDirectory);
    },
    traverseTree: function(rootPath, onFile, onDirectory, onDone) {
      return fs.readdir(rootPath, function(error, files) {
        var file, queue, _i, _len, _results;
        if (error) {
          return typeof onDone === "function" ? onDone() : void 0;
        } else {
          queue = async.queue(function(childPath, callback) {
            return fs.stat(childPath, function(error, stats) {
              if (error) {
                return callback(error);
              } else if (stats.isFile()) {
                onFile(childPath);
                return callback();
              } else if (stats.isDirectory()) {
                if (onDirectory(childPath)) {
                  return fs.readdir(childPath, function(error, files) {
                    var file, _i, _len;
                    if (error) {
                      return callback(error);
                    } else {
                      for (_i = 0, _len = files.length; _i < _len; _i++) {
                        file = files[_i];
                        queue.unshift(path.join(childPath, file));
                      }
                      return callback();
                    }
                  });
                } else {
                  return callback();
                }
              }
            });
          });
          queue.concurrency = 1;
          queue.drain = onDone;
          _results = [];
          for (_i = 0, _len = files.length; _i < _len; _i++) {
            file = files[_i];
            _results.push(queue.push(path.join(rootPath, file)));
          }
          return _results;
        }
      });
    },
    md5ForPath: function(pathToDigest) {
      var contents;
      contents = fs.readFileSync(pathToDigest);
      return require('crypto').createHash('md5').update(contents).digest('hex');
    },
    resolve: function() {
      var args, candidatePath, extensions, loadPath, loadPaths, pathToResolve, resolvedPath, _i, _len, _ref;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      if (_.isArray(_.last(args))) {
        extensions = args.pop();
      }
      pathToResolve = (_ref = args.pop()) != null ? _ref.toString() : void 0;
      loadPaths = args;
      if (!pathToResolve) {
        return void 0;
      }
      if (fsPlus.isAbsolute(pathToResolve)) {
        if (extensions && (resolvedPath = fsPlus.resolveExtension(pathToResolve, extensions))) {
          return resolvedPath;
        } else {
          if (fsPlus.existsSync(pathToResolve)) {
            return pathToResolve;
          }
        }
      }
      for (_i = 0, _len = loadPaths.length; _i < _len; _i++) {
        loadPath = loadPaths[_i];
        candidatePath = path.join(loadPath, pathToResolve);
        if (extensions) {
          if (resolvedPath = fsPlus.resolveExtension(candidatePath, extensions)) {
            return resolvedPath;
          }
        } else {
          if (fsPlus.existsSync(candidatePath)) {
            return fsPlus.absolute(candidatePath);
          }
        }
      }
      return void 0;
    },
    resolveOnLoadPath: function() {
      var args, loadPaths;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      loadPaths = Module.globalPaths.concat(module.paths);
      return fsPlus.resolve.apply(fsPlus, __slice.call(loadPaths).concat(__slice.call(args)));
    },
    resolveExtension: function(pathToResolve, extensions) {
      var extension, pathWithExtension, _i, _len;
      for (_i = 0, _len = extensions.length; _i < _len; _i++) {
        extension = extensions[_i];
        if (extension === "") {
          if (fsPlus.existsSync(pathToResolve)) {
            return fsPlus.absolute(pathToResolve);
          }
        } else {
          pathWithExtension = pathToResolve + "." + extension.replace(/^\./, "");
          if (fsPlus.existsSync(pathWithExtension)) {
            return fsPlus.absolute(pathWithExtension);
          }
        }
      }
      return void 0;
    },
    isCompressedExtension: function(ext) {
      return _.indexOf(['.bz2', '.epub', '.gz', '.jar', '.lz', '.lzma', '.lzo', '.tar', '.tgz', '.war', '.xz', '.z', '.zip'], ext, true) >= 0;
    },
    isImageExtension: function(ext) {
      return _.indexOf(['.gif', '.ico', '.jpeg', '.jpg', '.png', '.tiff', '.webp'], ext, true) >= 0;
    },
    isPdfExtension: function(ext) {
      return ext === '.pdf';
    },
    isBinaryExtension: function(ext) {
      return _.indexOf(['.DS_Store', '.a', '.exe', '.o', '.pyc', '.pyo', '.so', '.woff'], ext, true) >= 0;
    },
    isReadmePath: function(readmePath) {
      var base, extension;
      extension = path.extname(readmePath);
      base = path.basename(readmePath, extension).toLowerCase();
      return base === 'readme' && (extension === '' || fsPlus.isMarkdownExtension(extension));
    },
    isMarkdownExtension: function(ext) {
      return _.indexOf(['.markdown', '.md', '.mdown', '.mkd', '.mkdown', '.rmd', '.ron'], ext, true) >= 0;
    },
    isCaseInsensitive: function() {
      var lowerCaseStat, upperCaseStat;
      if (fsPlus.caseInsensitiveFs == null) {
        lowerCaseStat = statSyncNoException(__filename.toLowerCase());
        upperCaseStat = statSyncNoException(__filename.toUpperCase());
        if (lowerCaseStat && upperCaseStat) {
          fsPlus.caseInsensitiveFs = lowerCaseStat.dev === upperCaseStat.dev && lowerCaseStat.ino === upperCaseStat.ino;
        } else {
          fsPlus.caseInsensitiveFs = false;
        }
      }
      return fsPlus.caseInsensitiveFs;
    },
    isCaseSensitive: function() {
      return !fsPlus.isCaseInsensitive();
    }
  };

  statSyncNoException = fs.statSyncNoException, lstatSyncNoException = fs.lstatSyncNoException;

  if (statSyncNoException == null) {
    statSyncNoException = function() {
      var args, error;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      try {
        return fs.statSync.apply(fs, args);
      } catch (_error) {
        error = _error;
        return false;
      }
    };
  }

  if (lstatSyncNoException == null) {
    lstatSyncNoException = function() {
      var args, error;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      try {
        return fs.lstatSync.apply(fs, args);
      } catch (_error) {
        error = _error;
        return false;
      }
    };
  }

  isPathValid = function(pathToCheck) {
    return (pathToCheck != null) && typeof pathToCheck === 'string' && pathToCheck.length > 0;
  };

  module.exports = _.extend({}, fs, fsPlus);

}).call(this);
