All files / libs/fs findup.js

100% Statements 37/37
100% Branches 19/19
100% Functions 7/7
100% Lines 36/36
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130                1x 1x 1x                                         1x 22x 22x   22x 21x 21x 68x       22x 22x 71x 1x   70x 70x 50x   2x     1x     15x 1x 1x   15x 15x                           1x 11x                           1x 2x                         1x 10x                         1x 2x   2x 2x 1x   1x      
/**
 * @module      libs/fs/findup
 * @createdAt   2016-06-30
 *
 * @copyright   Copyright (c) 2016 Zhonglei Qiu
 * @license     Licensed under the MIT license.
 */
 
var fs = require('fs')
var path = require('path')
var exists = require('./exists')
 
/**
 *
 * 递归向上查找满足条件的文件夹
 *
 * @param  {String}           dir          起始查找目录
 * @param  {String|Function}  iteratorSync 要查找到文件名,或者返回 Boolean 值的函数,
 *                                         如果此参数是函数,那么它会接收到参数是当前递归所在的目录
 * @param  {Object}           [options]    配置选项
 *
 *   - allowTypes:  {String|Array<String>} 要查找的文件所属类型, 参考 {@link module:libs/fs/exists} 函数的第二个参数
 *   - maxdepth:    {Number}               递归的深度,1:表示只会查找当前文件夹下的文件,2:表示查找当前文件夹和上一个文件夹 ...
 *
 * @throws {Error}            如果没有找到任何满足条件的文件夹
 * @return {String}           查找到的满足条件的文件夹路径
 *
 * @author    Zhongle Qiu
 * @since     2.0.0
 *
 */
module.exports = function(dir, iteratorSync, options) {
  dir = path.normalize(dir)
  options = options || {}
 
  if (typeof iteratorSync === 'string') {
    var file = iteratorSync
    iteratorSync = function(dir) {
      return exists(path.join(dir, file), options.allowTypes)
    }
  }
 
  var currentDepth = 0
  while (dir !== path.resolve(dir, '..')) {
    if (typeof options.maxdepth === 'number' && options.maxdepth >= 0 && currentDepth >= options.maxdepth) {
      break
    }
    currentDepth++
    if (iteratorSync(dir)) return dir
    dir = path.resolve(dir, '..')
  }
  throw new Error('Not found')
}
 
exports = module.exports
 
function _find(root, file, allowTypes) {
  if (file == null) {
    file = root
    root = process.cwd()
  }
  var dir = exports(root, file, {allowTypes: allowTypes})
  return path.resolve(path.join(dir, file))
}
 
/**
 * 递归向上查找指定的文件
 *
 * @param  {String} [root]  起始查找目录(如果没有指定,则从当前路径开始)
 * @param  {String} file    要查找的文件名
 * @throws {Error}          如果没有找到满足条件的文件
 * @return {String}         返回查到的文件路径
 *
 * @author    Zhongle Qiu
 * @since     1.5.0
 */
exports.file = function(root, file) {
  return _find(root, file, 'File')
}
 
/**
 * 递归向上查找指定的文件夹
 *
 * @param  {String} [root]  起始查找目录(如果没有指定,则从当前路径开始)
 * @param  {String} dir     要查找的文件夹名
 * @throws {Error}          如果没有找到满足条件的文件夹
 * @return {String}         返回查到的文件夹路径
 *
 * @author    Zhongle Qiu
 * @since     1.5.0
 */
exports.dir = function(root, dir) {
  return _find(root, dir, 'Directory')
}
 
/**
 * 递归向上查找 package.json 文件
 *
 * @param  {String} [root]  起始查找目录(如果没有指定,则从当前路径开始)
 * @throws {Error}          如果没有找到 package.json 文件
 * @return {String}         返回查到的 package.json 的路径
 *
 * @author    Zhongle Qiu
 * @since     1.5.0
 */
exports.pkg = function(root) {
  return exports.file(root || process.cwd(), 'package.json')
}
 
/**
 * 递归向上查找 .git 文件夹
 *
 * @param  {String} [root]  起始查找目录(如果没有指定,则从当前路径开始)
 * @throws {Error}          如果没有找到 .git 文件夹
 * @return {String}         返回查到的 .git 文件夹的路径
 *
 * @author    Zhongle Qiu
 * @since     1.5.0
 */
exports.git = function(root) {
  var dir = _find(root || process.cwd(), '.git', ['File', 'Directory'])
 
  var stats = fs.statSync(dir)
  if (!stats.isDirectory()) { // .git 可能是一个文件,而不是文件夹
    return fs.readFileSync(dir, 'utf8').substring('gitdir: '.length).trim()
  }
  return dir
}