structures/Command.js

const path = require("path");
/** A command that can be run */
class Command {
  /**
   * @typedef {Object} CommandInfo
   * @property {string} name - The name of the command (must be lowercase)
   * @property {string[]} [aliases] - Alternative names for the command (all must be lowercase)
   * @property {string} group - The ID of the group the command belongs to (must be lowercase)
   * @property {string} description - A short description of the command
   */

  /**
   * @param {ModzClient} client - The client the command is for
   * @param {CommandInfo} info - The command information
   */
  constructor(client, info) {
    this.constructor.validateInfo(client, info);

    /**
     * The client this command is for
     * @name Command#client
     */
    Object.defineProperty(this, "client", { value: client });

    // Command name
    /**
     * Name of this command
     * @type {string}
     */
    this.name = info.name;

    // Command aliases
    /**
     * Aliases for the command
     * @type {string[]}
     */
    this.aliases = info.aliases;

    // Command group
    /**
     * Group this command belongs to
     * @type {string}
     */
    this.group = info.group;

    // Command description
    /**
     * Description for the command
     * @type {string}
     */
    this.description = info.description;
  }
  // eslint-disable-next-line valid-jsdoc
  /**
   * Runs the command
   * @param {Message} message - The message the command is being run for
   * @param {string[]} args - The arguments for the command
   * @return {Promise<?Message|?Array<Message>>}
   * @abstract
   */
  async run(message, args) { // eslint-disable-line no-unused-vars, require-await
    throw new Error(`${this.constructor.name} doesn't have a run() method.`);
  }
  /**
   * Validates the constructor parameters
   * @param {ModzClient} client - Client to validate
   * @param {CommandInfo} info - Info to validate
   * @private
   */
  static validateInfo(client, info) {
    if (!client) throw new Error("A client must be specified.");
    if (typeof info !== "object") throw new TypeError("Command info must be an Object.");
    if (typeof info.name !== "string") throw new TypeError("Command name must be a string.");
    if (info.name !== info.name.toLowerCase()) throw new Error("Command name must be lowercase.");
    if (typeof info.description !== "string") throw new TypeError("Command description must be a string.");
    if (info.aliases && (!Array.isArray(info.aliases) || info.aliases.some(ali => typeof ali !== "string"))) {
      throw new TypeError("Command aliases must be an Array of strings.");
    }
    if (info.aliases && info.aliases.some(ali => ali !== ali.toLowerCase())) {
      throw new Error("Command aliases must be lowercase.");
    }
    if (typeof info.group !== "string") throw new TypeError("group name must be a string.");
  }
  /**
   * Reloads the command
   */
  reload() {
    let cmdPath, cached, newCmd;
    try {
      cmdPath = this.client.loader.resolveCommandPath(this.group, this.name);
      cached = require.cache[cmdPath];
      delete require.cache[cmdPath];
      newCmd = require(cmdPath);
    } catch (err) {
      if (cached) require.cache[cmdPath] = cached;
      try {
        cmdPath = path.join(__dirname, this.group, `${this.name}.js`);
        cached = require.cache[cmdPath];
        delete require.cache[cmdPath];
        newCmd = require(cmdPath);
      } catch (err2) {
        if (cached) require.cache[cmdPath] = cached;
        if (err2.message.includes("Cannot find module")) throw err; else throw err2;
      }
    }

    this.client.loader.reloadCommand(newCmd, this);
  }
}

module.exports = Command;