validate.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import process from 'node:process';
  2. import { decamelizeFlagKey } from './utils.js';
  3. const validateFlags = (flags, options) => {
  4. for (const [flagKey, flagValue] of Object.entries(options.flags)) {
  5. if (flagKey !== '--' && !flagValue.isMultiple && Array.isArray(flags[flagKey])) {
  6. throw new Error(`The flag --${flagKey} can only be set once.`);
  7. }
  8. }
  9. };
  10. const validateChoicesByFlag = (flagKey, flagValue, receivedInput) => {
  11. const {choices, isRequired} = flagValue;
  12. if (!choices) {
  13. return;
  14. }
  15. const valueMustBeOneOf = `Value must be one of: [\`${choices.join('`, `')}\`]`;
  16. if (!receivedInput) {
  17. if (isRequired) {
  18. return `Flag \`${decamelizeFlagKey(flagKey)}\` has no value. ${valueMustBeOneOf}`;
  19. }
  20. return;
  21. }
  22. if (Array.isArray(receivedInput)) {
  23. const unknownValues = receivedInput.filter(index => !choices.includes(index));
  24. if (unknownValues.length > 0) {
  25. const valuesText = unknownValues.length > 1 ? 'values' : 'value';
  26. return `Unknown ${valuesText} for flag \`${decamelizeFlagKey(flagKey)}\`: \`${unknownValues.join('`, `')}\`. ${valueMustBeOneOf}`;
  27. }
  28. } else if (!choices.includes(receivedInput)) {
  29. return `Unknown value for flag \`${decamelizeFlagKey(flagKey)}\`: \`${receivedInput}\`. ${valueMustBeOneOf}`;
  30. }
  31. };
  32. const validateChoices = (flags, receivedFlags) => {
  33. const errors = [];
  34. for (const [flagKey, flagValue] of Object.entries(flags)) {
  35. const receivedInput = receivedFlags[flagKey];
  36. const errorMessage = validateChoicesByFlag(flagKey, flagValue, receivedInput);
  37. if (errorMessage) {
  38. errors.push(errorMessage);
  39. }
  40. }
  41. if (errors.length > 0) {
  42. throw new Error(`${errors.join('\n')}`);
  43. }
  44. };
  45. const validate = (flags, options) => {
  46. validateFlags(flags, options);
  47. validateChoices(options.flags, flags);
  48. };
  49. const reportUnknownFlags = unknownFlags => {
  50. console.error([
  51. `Unknown flag${unknownFlags.length > 1 ? 's' : ''}`,
  52. ...unknownFlags,
  53. ].join('\n'));
  54. };
  55. const checkUnknownFlags = input => {
  56. const unknownFlags = input.filter(item => typeof item === 'string' && item.startsWith('-'));
  57. if (unknownFlags.length > 0) {
  58. reportUnknownFlags(unknownFlags);
  59. process.exit(2);
  60. }
  61. };
  62. const isFlagMissing = (flagName, definedFlags, receivedFlags, input) => {
  63. const flag = definedFlags[flagName];
  64. let isFlagRequired = true;
  65. if (typeof flag.isRequired === 'function') {
  66. isFlagRequired = flag.isRequired(receivedFlags, input);
  67. if (typeof isFlagRequired !== 'boolean') {
  68. throw new TypeError(`Return value for isRequired callback should be of type boolean, but ${typeof isFlagRequired} was returned.`);
  69. }
  70. }
  71. if (receivedFlags[flagName] === undefined) {
  72. return isFlagRequired;
  73. }
  74. return flag.isMultiple && receivedFlags[flagName].length === 0 && isFlagRequired;
  75. };
  76. const reportMissingRequiredFlags = missingRequiredFlags => {
  77. console.error(`Missing required flag${missingRequiredFlags.length > 1 ? 's' : ''}`);
  78. for (const flag of missingRequiredFlags) {
  79. console.error(`\t${decamelizeFlagKey(flag.key)}${flag.shortFlag ? `, -${flag.shortFlag}` : ''}`);
  80. }
  81. };
  82. const checkMissingRequiredFlags = (flags, receivedFlags, input) => {
  83. const missingRequiredFlags = [];
  84. if (flags === undefined) {
  85. return [];
  86. }
  87. for (const flagName of Object.keys(flags)) {
  88. if (flags[flagName].isRequired && isFlagMissing(flagName, flags, receivedFlags, input)) {
  89. missingRequiredFlags.push({key: flagName, ...flags[flagName]});
  90. }
  91. }
  92. if (missingRequiredFlags.length > 0) {
  93. reportMissingRequiredFlags(missingRequiredFlags);
  94. process.exit(2);
  95. }
  96. };
  97. export { checkMissingRequiredFlags, checkUnknownFlags, validate };