checkInvalidCLIOptions.cjs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // NOTICE: This file is generated by Rollup. To modify it,
  2. // please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
  3. 'use strict';
  4. const node_os = require('node:os');
  5. const fastestLevenshtein = require('fastest-levenshtein');
  6. const picocolors = require('picocolors');
  7. const { cyan, red } = picocolors;
  8. /**
  9. * @param {{ [key: string]: { alias?: string } }} allowedOptions
  10. * @returns {string[]}
  11. */
  12. const buildAllowedOptions = (allowedOptions) => {
  13. const options = Object.keys(allowedOptions);
  14. for (const { alias } of Object.values(allowedOptions)) {
  15. if (alias) {
  16. options.push(alias);
  17. }
  18. }
  19. options.sort();
  20. return options;
  21. };
  22. /**
  23. * @param {string[]} all
  24. * @param {string} invalid
  25. * @returns {null|string}
  26. */
  27. const suggest = (all, invalid) => {
  28. const maxThreshold = 10;
  29. for (let threshold = 1; threshold <= maxThreshold; threshold++) {
  30. const suggestion = all.find((option) => fastestLevenshtein.distance(option, invalid) <= threshold);
  31. if (suggestion) {
  32. return suggestion;
  33. }
  34. }
  35. return null;
  36. };
  37. /**
  38. * Converts a string to kebab case.
  39. * For example, `kebabCase('oneTwoThree') === 'one-two-three'`.
  40. * @param {string} opt
  41. * @returns {string}
  42. */
  43. const kebabCase = (opt) => {
  44. const matches = opt.match(/[A-Z]?[a-z]+|[A-Z]|[0-9]+/g);
  45. if (matches) {
  46. return matches.map((s) => s.toLowerCase()).join('-');
  47. }
  48. return '';
  49. };
  50. /**
  51. * @param {string} opt
  52. * @returns {string}
  53. */
  54. const cliOption = (opt) => {
  55. if (opt.length === 1) {
  56. return `"-${opt}"`;
  57. }
  58. return `"--${kebabCase(opt)}"`;
  59. };
  60. /**
  61. * @param {string} invalid
  62. * @param {string|null} suggestion
  63. * @returns {string}
  64. */
  65. const buildMessageLine = (invalid, suggestion) => {
  66. let line = `Invalid option ${red(cliOption(invalid))}.`;
  67. if (suggestion) {
  68. line += ` Did you mean ${cyan(cliOption(suggestion))}?`;
  69. }
  70. return line + node_os.EOL;
  71. };
  72. /**
  73. * @param {{ [key: string]: any }} allowedOptions
  74. * @param {{ [key: string]: any }} inputOptions
  75. * @returns {string}
  76. */
  77. function checkInvalidCLIOptions(allowedOptions, inputOptions) {
  78. const allOptions = buildAllowedOptions(allowedOptions);
  79. return Object.keys(inputOptions)
  80. .filter((opt) => !allOptions.includes(opt))
  81. .map((opt) => kebabCase(opt))
  82. .reduce((msg, invalid) => {
  83. // NOTE: No suggestion for shortcut options because it's too difficult
  84. const suggestion = invalid.length >= 2 ? suggest(allOptions, invalid) : null;
  85. return msg + buildMessageLine(invalid, suggestion);
  86. }, '');
  87. }
  88. module.exports = checkInvalidCLIOptions;