You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
3.7 KiB

11 months ago
  1. const UPPERCASE = /[\p{Lu}]/u;
  2. const LOWERCASE = /[\p{Ll}]/u;
  3. const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
  4. const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
  5. const SEPARATORS = /[_.\- ]+/;
  6. const LEADING_SEPARATORS = new RegExp("^" + SEPARATORS.source);
  7. const SEPARATORS_AND_IDENTIFIER = new RegExp(
  8. SEPARATORS.source + IDENTIFIER.source,
  9. "gu"
  10. );
  11. const NUMBERS_AND_IDENTIFIER = new RegExp("\\d+" + IDENTIFIER.source, "gu");
  12. const preserveCamelCase = (
  13. string,
  14. toLowerCase,
  15. toUpperCase,
  16. preserveConsecutiveUppercase
  17. ) => {
  18. let isLastCharLower = false;
  19. let isLastCharUpper = false;
  20. let isLastLastCharUpper = false;
  21. let isLastLastCharPreserved = false;
  22. for (let index = 0; index < string.length; index++) {
  23. const character = string[index];
  24. isLastLastCharPreserved = index > 2 ? string[index - 3] === "-" : true;
  25. if (isLastCharLower && UPPERCASE.test(character)) {
  26. string = string.slice(0, index) + "-" + string.slice(index);
  27. isLastCharLower = false;
  28. isLastLastCharUpper = isLastCharUpper;
  29. isLastCharUpper = true;
  30. index++;
  31. } else if (
  32. isLastCharUpper &&
  33. isLastLastCharUpper &&
  34. LOWERCASE.test(character) &&
  35. (!isLastLastCharPreserved || preserveConsecutiveUppercase)
  36. ) {
  37. string = string.slice(0, index - 1) + "-" + string.slice(index - 1);
  38. isLastLastCharUpper = isLastCharUpper;
  39. isLastCharUpper = false;
  40. isLastCharLower = true;
  41. } else {
  42. isLastCharLower =
  43. toLowerCase(character) === character &&
  44. toUpperCase(character) !== character;
  45. isLastLastCharUpper = isLastCharUpper;
  46. isLastCharUpper =
  47. toUpperCase(character) === character &&
  48. toLowerCase(character) !== character;
  49. }
  50. }
  51. return string;
  52. };
  53. const preserveConsecutiveUppercase = (input, toLowerCase) => {
  54. LEADING_CAPITAL.lastIndex = 0;
  55. return input.replace(LEADING_CAPITAL, (m1) => toLowerCase(m1));
  56. };
  57. const postProcess = (input, toUpperCase) => {
  58. SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
  59. NUMBERS_AND_IDENTIFIER.lastIndex = 0;
  60. return input
  61. .replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) =>
  62. toUpperCase(identifier)
  63. )
  64. .replace(NUMBERS_AND_IDENTIFIER, (m) => toUpperCase(m));
  65. };
  66. function camelCase(input, options) {
  67. if (!(typeof input === "string" || Array.isArray(input))) {
  68. throw new TypeError("Expected the input to be `string | string[]`");
  69. }
  70. options = {
  71. pascalCase: true,
  72. preserveConsecutiveUppercase: false,
  73. ...options,
  74. };
  75. if (Array.isArray(input)) {
  76. input = input
  77. .map((x) => x.trim())
  78. .filter((x) => x.length)
  79. .join("-");
  80. } else {
  81. input = input.trim();
  82. }
  83. if (input.length === 0) {
  84. return "";
  85. }
  86. const toLowerCase =
  87. options.locale === false
  88. ? (string) => string.toLowerCase()
  89. : (string) => string.toLocaleLowerCase(options.locale);
  90. const toUpperCase =
  91. options.locale === false
  92. ? (string) => string.toUpperCase()
  93. : (string) => string.toLocaleUpperCase(options.locale);
  94. if (input.length === 1) {
  95. if (SEPARATORS.test(input)) {
  96. return "";
  97. }
  98. return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
  99. }
  100. const hasUpperCase = input !== toLowerCase(input);
  101. if (hasUpperCase) {
  102. input = preserveCamelCase(
  103. input,
  104. toLowerCase,
  105. toUpperCase,
  106. options.preserveConsecutiveUppercase
  107. );
  108. }
  109. input = input.replace(LEADING_SEPARATORS, "");
  110. input = options.preserveConsecutiveUppercase
  111. ? preserveConsecutiveUppercase(input, toLowerCase)
  112. : toLowerCase(input);
  113. if (options.pascalCase) {
  114. input = toUpperCase(input.charAt(0)) + input.slice(1);
  115. }
  116. return postProcess(input, toUpperCase);
  117. }
  118. module.exports = {
  119. camelCase,
  120. };