lib_parse-args-argv.js

import yargs from 'yargs'
import { Parser } from 'yargs/helpers'
import { unflatten } from 'flat'
import dotenvParseVariables from 'dotenv-parse-variables'
import lodash from 'lodash'
import fs from 'fs-extra'
import path from 'path'

const { find, each, set, camelCase, forOwn } = lodash
const delimiter = '-'
const splitter = ':'

const parseItem = (data, delimiter) => {
  return unflatten(data, {
    delimiter,
    safe: true,
    overwrite: true
  })
}

async function parseWithParser () {
  return Parser(process.argv.slice(2), {
    configuration: {
      'camel-case-expansion': false
    }
  })
}

async function parseWithYargs () {
  const pkg = fs.readJSONSync(`${this.dir}/package.json`)
  let name = `node ${pkg.main}`
  if (pkg.bin) name = path.basename(pkg.bin, '.js')
  const cli = yargs(process.argv.slice(2))
    .usage('Usage: $0 [args...]')
    .scriptName(name)
    .positional('args', {
      describe: 'Optional one or more arguments'
    })
    .parserConfiguration({
      'camel-case-expansion': false
    })
    .version(pkg.version).alias('version', 'v')
    .help().alias('help', 'h')
    .alias('applet', 'a')
  if (pkg.homepage) cli.epilog(`For more information please visit ${pkg.homepage}`)
  return cli.argv
}

/**
 * Parse program arguments (args) & options (argv). See {@link App#args|args} & {@link App#argv|argv} for examples
 *
 * @method
 * @async
 * @memberof module:Lib
 * @param {boolean} [useParser] - If ```true```, skip {@link https://github.com/yargs/yargs|yargs}
 * @returns {{args: Array, argv: Object}} An object containing ```args``` and ```argv```
 * @see App#args
 * @see App#argv
 */
async function parseArgsArgv (useParser) {
  if (!useParser) useParser = find(process.argv, a => a.startsWith('--spawn'))
  let argv = useParser ? await parseWithParser.call(this) : await parseWithYargs.call(this)
  const args = argv._
  delete argv._
  delete argv.$0
  argv = dotenvParseVariables(argv)
  const all = { _: {} }
  each(argv, (v, k) => {
    const parts = k.split(splitter)
    if (!parts[1]) all._[parts[0]] = v
    else set(all, `${camelCase(parts[0])}.${parts[1]}`, v)
  })
  const result = {}
  forOwn(all, (v, k) => {
    result[k] = parseItem(v, delimiter)
  })
  return { args, argv: this.bajo.parseObject(result, { parseValue: true }) }
}

export default parseArgsArgv