argument.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. const { InvalidArgumentError } = require('./error.js');
  2. // @ts-check
  3. class Argument {
  4. /**
  5. * Initialize a new command argument with the given name and description.
  6. * The default is that the argument is required, and you can explicitly
  7. * indicate this with <> around the name. Put [] around the name for an optional argument.
  8. *
  9. * @param {string} name
  10. * @param {string} [description]
  11. */
  12. constructor(name, description) {
  13. this.description = description || '';
  14. this.variadic = false;
  15. this.parseArg = undefined;
  16. this.defaultValue = undefined;
  17. this.defaultValueDescription = undefined;
  18. this.argChoices = undefined;
  19. switch (name[0]) {
  20. case '<': // e.g. <required>
  21. this.required = true;
  22. this._name = name.slice(1, -1);
  23. break;
  24. case '[': // e.g. [optional]
  25. this.required = false;
  26. this._name = name.slice(1, -1);
  27. break;
  28. default:
  29. this.required = true;
  30. this._name = name;
  31. break;
  32. }
  33. if (this._name.length > 3 && this._name.slice(-3) === '...') {
  34. this.variadic = true;
  35. this._name = this._name.slice(0, -3);
  36. }
  37. }
  38. /**
  39. * Return argument name.
  40. *
  41. * @return {string}
  42. */
  43. name() {
  44. return this._name;
  45. };
  46. /**
  47. * @api private
  48. */
  49. _concatValue(value, previous) {
  50. if (previous === this.defaultValue || !Array.isArray(previous)) {
  51. return [value];
  52. }
  53. return previous.concat(value);
  54. }
  55. /**
  56. * Set the default value, and optionally supply the description to be displayed in the help.
  57. *
  58. * @param {any} value
  59. * @param {string} [description]
  60. * @return {Argument}
  61. */
  62. default(value, description) {
  63. this.defaultValue = value;
  64. this.defaultValueDescription = description;
  65. return this;
  66. };
  67. /**
  68. * Set the custom handler for processing CLI command arguments into argument values.
  69. *
  70. * @param {Function} [fn]
  71. * @return {Argument}
  72. */
  73. argParser(fn) {
  74. this.parseArg = fn;
  75. return this;
  76. };
  77. /**
  78. * Only allow option value to be one of choices.
  79. *
  80. * @param {string[]} values
  81. * @return {Argument}
  82. */
  83. choices(values) {
  84. this.argChoices = values;
  85. this.parseArg = (arg, previous) => {
  86. if (!values.includes(arg)) {
  87. throw new InvalidArgumentError(`Allowed choices are ${values.join(', ')}.`);
  88. }
  89. if (this.variadic) {
  90. return this._concatValue(arg, previous);
  91. }
  92. return arg;
  93. };
  94. return this;
  95. };
  96. /**
  97. * Make option-argument required.
  98. */
  99. argRequired() {
  100. this.required = true;
  101. return this;
  102. }
  103. /**
  104. * Make option-argument optional.
  105. */
  106. argOptional() {
  107. this.required = false;
  108. return this;
  109. }
  110. }
  111. /**
  112. * Takes an argument and returns its human readable equivalent for help usage.
  113. *
  114. * @param {Argument} arg
  115. * @return {string}
  116. * @api private
  117. */
  118. function humanReadableArgName(arg) {
  119. const nameOutput = arg.name() + (arg.variadic === true ? '...' : '');
  120. return arg.required
  121. ? '<' + nameOutput + '>'
  122. : '[' + nameOutput + ']';
  123. }
  124. exports.Argument = Argument;
  125. exports.humanReadableArgName = humanReadableArgName;