All files / src parser2.js

76.92% Statements 60/78
42.11% Branches 24/57
56.52% Functions 13/23
82.81% Lines 53/64

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 1461x 1x                                               1x                                         18x   18x 18x 18x   18x 18x   18x 30x   30x 10x 10x   10x 10x   20x   20x 7x 7x   7x 7x 12x 12x 12x     12x 7x 7x   7x 7x 13x 3x 3x   3x 3x   10x   10x 8x   9x 8x   8x 8x   3x 2x   2x 2x           18x         600x       30x   20x   1x                     20x       13x          
const {pipe} = require('./pipe')
const {then} = require('./then')
 
function parser (stages = {}, options = {}) {
  const {checks = {}, parsers = {}} = options
 
  const checksAndStages = {
    argv: [...(checks.argv || []), ...(stages.argv || [])],
    toOpts: stages.toOpts,
    opts: [...(checks.opts || []), ...(stages.opts || [])],
    toArgs: stages.toArgs,
    args: [...(checks.args || []), ...(stages.args || [])]
  }
 
  const {argv, toOpts = TO_OPTS, opts, toArgs, args} = checksAndStages
 
  return ({opts: OPTS = []} = {}) => (ARGV, ERRS) => pipe(
    ...argv,
    toOpts(OPTS),
    ...opts,
    toArgs || TO_ARGS({_: parser(stages), ...parsers, __: parser(stages)}),
    ...args
  )({errs: ERRS, argv: ARGV})
}
 
module.exports = {
  parser,
  argvToOpts
}
 
function TO_OPTS (opts = []) {
  const options = opts.filter(opt => typeof opt === 'object' && opt.key !== 'undefined')
  return pipe2(
    argvToOpts
  )(opts)(options)
}
 
function pipe2 (f, ...fs) {
  return a => b => {
    let res = f(a)(b)
    for (let i = 0; i < fs.length; i++) res = fs[i](a)(res)
    return res
  }
}
 
function argvToOpts (opts = []) {
  const forArg = optsForArg(opts)
 
  return ({errs = [], argv = []} = {}) => {
    const errs2 = []
    let opts2   = []
 
    let at  = 0
    let arg = argv[at]
 
    while (arg) {
      const opts3 = withSameTypesLength(forArg(arg))
 
      if (opts3.length === 0) {
        const opt = {values: [arg]}
        opts2.push(opt)
 
        at++
        arg = argv[at]
      } else {
        const opt = opts3[0]
 
        if (isVariadic(opt)) {
          let j    = at + 1
          let arg2 = argv[j] || '--'
 
          const values = []
          while (arg2 !== '--') {
            values.push(arg2)
            j++
            arg2 = argv[j] || '--'
          }
 
          const types2 = Array.from({length: values.length}, () => 'string')
          const opt2   = {...opt, types: types2, values}
          opts2.push(opt2)
 
          at  = j
          arg = argv[at]
        } else if (isFlag(opt)) {
          const opts4 = opts3.map(opt => ({...opt, values: [1]}))
          opts2 = [...opts2, ...opts4]
 
          at  = at + 1
          arg = argv[at]
        } else {
          const at2 = at + 1 + opt.types.length
 
          if (typeof argv[at2 - 1] !== 'undefined') {
            const values = argv.slice(at + 1, at2)
 
            const opts4 = opts3.map(opt => ({...opt, values}))
            opts2 = [...opts2, ...opts4]
 
            at  = at2
            arg = argv[at]
          } else {
            const rest = argv.slice(at).map(value => ({values: [value]}))
            opts2 = [...opts2, ...rest]
 
            at  = at2
            arg = argv[at]
          }
        }
      }
    }
 
    return {errs: [...errs, ...errs2], opts: opts2}
  }
}
 
function optsForArg (opts) {
  return arg => opts.filter(({args}) => typeof args !== 'undefined' && args.indexOf(arg) > -1)
}
 
function withSameTypesLength (opts) {
  if (opts.length === 0) return opts
  else {
    return opts.slice(1).reduce(
      ([head, ...tail], opt) => (
        (typeof head.types === 'undefined' && typeof opt.types === 'undefined') ||
        (Array.isArray(head.types) && Array.isArray(opt.types) && head.types.length === opt.types.length)
          ? [head, ...tail, opt]
          : [head, ...tail]
      ),
      opts.slice(0, 1)
    )
  }
}
 
function isVariadic ({types}) {
  return typeof types === 'undefined'
}
 
function isFlag ({types}) {
  return Array.isArray(types) && types.length === 0
}
 
function TO_ARGS (parsers = {}) {
  return ({errs = [], opts = []} = {}) => ({errs, args: {}})
}