Home Manual Reference Source

src/abstracts/cliSubCommand.js

/**
 * A helper class for creating sub commands for the CLI. A sub command works like a
 * {@link CLICommand} inside a regular {@link CLICommand}.
 * @abstract
 * @version 1.0
 */
class CLISubCommand {
  /**
   * Class constructor.
   * @throws {TypeError} If instantiated directly.
   * @abstract
   */
  constructor() {
    if (new.target === CLISubCommand) {
      throw new TypeError(
        'CLISubCommand is an abstract class, it can\'t be instantiated directly'
      );
    }
    /**
     * The name of the sub command for the help interface.
     * @type {string}
     */
    this.name = '';
    /**
     * A short description for what the generator does.
     * @type {string}
     */
    this.description = '';
    /**
     * A list with the name of the options the generator supports. New options can be added using
     * the `addOption` method.
     * @type {Array}
     */
    this.options = [];
    /**
     * A dictionary of options settings by their option name. New options can be added using
     * the `addOption` method.
     * @type {Object}
     */
    this.optionsByName = {};
  }
  /**
   * Add a new option for the command.
   * @example
   * // To capture an option
   * this.addOption(
   *   'type',
   *   '-t, --type [type]',
   *   'The type of thingy you want to use?',
   * );
   *
   * // As a simple flag
   * this.addOption(
   *   'ready',
   *   '-r, --ready',
   *   'Is it read?',
   *   false
   * );
   *
   * @param {string} name              The option name.
   * @param {string} instruction       The option instruction, for example: `-t, --type [type]`.
   * @param {string} [description='']  The option description.
   * @param {string} [defaultValue=''] The option default value, in case is not used on execution.
   */
  addOption(name, instruction, description = '', defaultValue = '') {
    this.optionsByName[name] = {
      name,
      instruction,
      description,
      defaultValue,
    };

    this.options.push(name);
  }
  /**
   * Generates a complete description of the sub command and its options in order to be used on the
   * help interface of the {@link CLICommand} that implements it.
   * @return {string}
   */
  getHelpInformation() {
    // Define the basic description line.
    let description = ` - '${this.name}': ${this.description}`;
    // If the generator has options...
    if (this.options.length) {
      // ...add the options subtitle.
      description += '\n   Options:\n';
      /**
       * Loop all the options and find the one with the longest instruction to calculate the
       * necessary padding in order to show all instructions on a _"fixed width column"_ style.
       */
      let longest = '';
      this.options.forEach((optionName) => {
        const option = this.optionsByName[optionName];
        if (option.instruction.length > longest.length) {
          longest = option.instruction;
        }
      });
      // Once the longest instruction is saved, loop all the options again...
      this.options.forEach((optionName) => {
        const option = this.optionsByName[optionName];
        // Set the instruction as the initial value of the description.
        let line = option.instruction;
        // If the instruction is shorter than the longest, add the necessary padding.
        if (line.length < longest.length) {
          line += (new Array(longest.length - line.length)).fill(' ').join('');
        }
        // Add the option description after the instruction, and possibly the padding.
        line += `  ${option.description}`;
        // Add the description to the generator description.
        description += `\n   ${line}`;
      });
    }

    return description;
  }
  /**
   * The method called by the {@link CLICommand} that implements the sub command.
   * It receives a dicitionary with the parsed options the command received.
   * @throws {Error} if not overwritten.
   * @abstract
   */
  handle() {
    throw new Error('This method must to be overwritten');
  }
}

module.exports = CLISubCommand;