index.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. 'use strict';
  2. const UPPERCASE = /[\p{Lu}]/u;
  3. const LOWERCASE = /[\p{Ll}]/u;
  4. const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
  5. const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
  6. const SEPARATORS = /[_.\- ]+/;
  7. const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
  8. const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
  9. const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');
  10. const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
  11. let isLastCharLower = false;
  12. let isLastCharUpper = false;
  13. let isLastLastCharUpper = false;
  14. for (let i = 0; i < string.length; i++) {
  15. const character = string[i];
  16. if (isLastCharLower && UPPERCASE.test(character)) {
  17. string = string.slice(0, i) + '-' + string.slice(i);
  18. isLastCharLower = false;
  19. isLastLastCharUpper = isLastCharUpper;
  20. isLastCharUpper = true;
  21. i++;
  22. } else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) {
  23. string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
  24. isLastLastCharUpper = isLastCharUpper;
  25. isLastCharUpper = false;
  26. isLastCharLower = true;
  27. } else {
  28. isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
  29. isLastLastCharUpper = isLastCharUpper;
  30. isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
  31. }
  32. }
  33. return string;
  34. };
  35. const preserveConsecutiveUppercase = (input, toLowerCase) => {
  36. LEADING_CAPITAL.lastIndex = 0;
  37. return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1));
  38. };
  39. const postProcess = (input, toUpperCase) => {
  40. SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
  41. NUMBERS_AND_IDENTIFIER.lastIndex = 0;
  42. return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier))
  43. .replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m));
  44. };
  45. const camelCase = (input, options) => {
  46. if (!(typeof input === 'string' || Array.isArray(input))) {
  47. throw new TypeError('Expected the input to be `string | string[]`');
  48. }
  49. options = {
  50. pascalCase: false,
  51. preserveConsecutiveUppercase: false,
  52. ...options
  53. };
  54. if (Array.isArray(input)) {
  55. input = input.map(x => x.trim())
  56. .filter(x => x.length)
  57. .join('-');
  58. } else {
  59. input = input.trim();
  60. }
  61. if (input.length === 0) {
  62. return '';
  63. }
  64. const toLowerCase = options.locale === false ?
  65. string => string.toLowerCase() :
  66. string => string.toLocaleLowerCase(options.locale);
  67. const toUpperCase = options.locale === false ?
  68. string => string.toUpperCase() :
  69. string => string.toLocaleUpperCase(options.locale);
  70. if (input.length === 1) {
  71. return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
  72. }
  73. const hasUpperCase = input !== toLowerCase(input);
  74. if (hasUpperCase) {
  75. input = preserveCamelCase(input, toLowerCase, toUpperCase);
  76. }
  77. input = input.replace(LEADING_SEPARATORS, '');
  78. if (options.preserveConsecutiveUppercase) {
  79. input = preserveConsecutiveUppercase(input, toLowerCase);
  80. } else {
  81. input = toLowerCase(input);
  82. }
  83. if (options.pascalCase) {
  84. input = toUpperCase(input.charAt(0)) + input.slice(1);
  85. }
  86. return postProcess(input, toUpperCase);
  87. };
  88. module.exports = camelCase;
  89. // TODO: Remove this for the next major release
  90. module.exports.default = camelCase;