Home Reference Source Test

src/bin/cli.js

import Path from 'path'
import WpXml2Md from '../lib/index.js'

/**
 * Help text.
 * @type {String}
 */
const HelpText = `
Usage: wpxml2md [OPTIONS]

  Convert the WordPress XML file to Markdown files.

  Options:
    -h, --help            Display this text.
    -v, --version         Display the version number.
    -i, --input           Path of the XML file exported from WordPress.
    -o, --output          Path of the output directory.
    -r, --report          Output process reports.
    --no-gfm              Disable the GitHub Flavored Markdown.
    --no-melink           Disable the Markdown Extra link on header.
    --with-metadata       Enable output article metadata.
    --with-image-download Enable download and replace link syntaxes a linked images from article.
    --with-comment        Enable comment output from article.
    --replace-link-prefix Replace the link URL prefix with the specified word, format is "target=placeholder".
                          If "--replace-link http://example.com/=/" then "http://example.com/" will be replaced with "/".

  Examples:
    $ wpxml2md -i wordpress.xml -o ./dist -r
    $ wpxml2md -i wordpress.xml -o ./dist -r --with-metadata --with-image-download --with-comment --replace-link-prefix http://akabeko.me/=/

  See also:
    https://github.com/akabekobeko/npm-wpxml2md
`

/**
 * CLI options.
 * @type {Object}
 */
const Options = {
  help: { name: '--help', shortName: '-h' },
  version: { name: '--version', shortName: '-v' },
  input: { name: '--input', shortName: '-i' },
  output: { name: '--output', shortName: '-o' },
  report: { name: '--report', shortName: '-r' },
  noGFM: { name: '--no-gfm' },
  noMELink: { name: '--no-melink' },
  withMetadata: { name: '--with-metadata' },
  withImageDownload: { name: '--with-image-download' },
  withComment: { name: '--with-comment' },
  replaceLinkPrefix: { name: '--replace-link-prefix' }
}

/**
 * Check that it is an option value.
 *
 * @param {String} value Value.
 *
 * @return {Boolean} If the option of the value "true".
 */
const isValue = (value) => {
  const keys = Object.keys(Options)
  return !(keys.some((key) => value === Options[key].name || value === Options[key].shortName))
}

/**
 * Parse for option value.
 *
 * @param {String[]} argv Arguments of the command line.
 * @param {Number} index Index of argumens.
 *
 * @return {String} Its contents if the option value, otherwise null.
 */
const parseArgValue =  (argv, index) => {
  if (!(index + 1 < argv.length)) {
    return null
  }

  const value = argv[index + 1]
  return (isValue(value) ? value : null)
}

/**
 * Parse for the link option.
 *
 * @param {String} value Option value.
 *
 * @return {Object} Replace targets.
 */
const parseReplaceLinkURL = (value) => {
  const units = value.split('=')
  if (units.length < 2) {
    return { old: '', new: '' }
  }

  return { old: units[0], new: units[1] }
}

/**
 * Parse for the command line argumens.
 *
 * @param {String[]} argv Arguments of the command line.
 *
 * @return {CLIOptions} Parse results.
 */
const parseArgv = (argv) => {
  const options = {}
  let   value   = null

  argv.forEach((arg, index) => {
    switch (arg) {
      case Options.input.name:
      case Options.input.shortName:
        value = parseArgValue(argv, index)
        if (value) {
          options.input = Path.resolve(value)
        }
        break

      case Options.output.name:
      case Options.output.shortName:
        value = parseArgValue(argv, index)
        if (value) {
          options.output = Path.resolve(value)
        }
        break

      case Options.report.name:
      case Options.report.shortName:
        options.report = true
        break

      case Options.noGFM.name:
        options.noGFM = true
        break

      case Options.noMELink.name:
        options.noMELink = true
        break

      case Options.withMetadata.name:
        options.withMetadata = true
        break

      case Options.withImageDownload.name:
        options.withImageDownload = true
        break

      case Options.withComment.name:
        options.withComment = true
        break

      case Options.replaceLinkPrefix.name:
        value = parseArgValue(argv, index)
        if (value) {
          options.replaceLinkPrefix = parseReplaceLinkURL(value)
        }
        break

      default:
        break
    }
  })

  return options
}

/**
 * Parse for the command line arguments.
 *
 * @param {String[]} argv Arguments of the command line.
 *
 * @return {CLIOptions} Parse results.
 */
const parseOption = (argv) => {
  if (!(argv && 0 < argv.length)) {
    return { help: true }
  }

  switch (argv[0]) {
    case Options.help.name:
    case Options.help.shortName:
      return { help: true }

    case Options.version.name:
    case Options.version.shortName:
      return { version: true }

    default:
      return parseArgv(argv)
  }
}

/**
 * Print a help text.
 *
 * @param {WritableStream} stdout Standard output.
 */
const printHelp = (stdout) => {
  stdout.write(HelpText)
}

/**
 * Print a version number.
 *
 * @param {WritableStream} stdout Standard output.
 */
const printVersion = (stdout) => {
  const read = (path) => {
    try {
      return require(path).version
    } catch (err) {
      return null
    }
  }

  const version = read('../package.json') || read('../../package.json')
  stdout.write('v' + version + '\n')
}

/**
 * Entry point of the CLI.
 *
 * @param {String[]} argv Arguments of the command line.
 * @param {WritableStream} stdout Standard output.
 *
 * @return {Promise} Asynchronous task.
 */
const CLI = (argv, stdout) => {
  return new Promise((resolve, reject) => {
    const options = parseOption(argv)
    if (options.help) {
      printHelp(stdout)
      return resolve()
    }

    if (options.version) {
      printVersion(stdout)
      return resolve()
    }

    if (!(options.input)) {
      return reject(new Error('"-i" or "--input" has not been specified. This parameter is required.'))
    }

    if (!(options.output)) {
      return reject(new Error('"-o" or "--output" has not been specified. This parameter is required.'))
    }

    return WpXml2Md(options.input, options.output, options)
  })
}

export default CLI