@guanghechen/helper-string
Version:
Utilities for processing strings or stringify other type data.
191 lines (187 loc) • 6.19 kB
JavaScript
const SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu;
const SPLIT_UPPER_UPPER_RE = /(\p{Lu})([\p{Lu}][\p{Ll}])/gu;
const SPLIT_NUMBER_LOWER_RE = /(\d)(\p{Ll})/gu;
const SPLIT_LETTER_NUMBER_RE = /(\p{L})(\d)/gu;
const DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu;
const SPLIT_REPLACE_VALUE = '$1\0$2';
function split(input, options = {}) {
let result = input
.replace(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE)
.replace(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE);
if (options.separateNumbers) {
result = result
.replace(SPLIT_NUMBER_LOWER_RE, SPLIT_REPLACE_VALUE)
.replace(SPLIT_LETTER_NUMBER_RE, SPLIT_REPLACE_VALUE);
}
result = result.replace(DEFAULT_STRIP_REGEXP, '\0');
let start = 0;
let end = result.length;
while (result.charAt(start) === '\0')
start += 1;
if (start === end)
return [];
while (result.charAt(end - 1) === '\0')
end -= 1;
return result.slice(start, end).split(/\0/g);
}
function camelCase(input, options) {
const lower = lowerFactory(options?.locale);
const upper = upperFactory(options?.locale);
const transform = pascalCaseTransformFactory(lower, upper);
return split(input, options)
.map((word, index) => {
if (index === 0)
return lower(word);
return transform(word, index);
})
.join('');
}
function pascalCase(input, options) {
const lower = lowerFactory(options?.locale);
const upper = upperFactory(options?.locale);
return split(input, options).map(pascalCaseTransformFactory(lower, upper)).join('');
}
function capitalCase(input, options) {
const lower = lowerFactory(options?.locale);
const upper = upperFactory(options?.locale);
return split(input, options).map(capitalCaseTransformFactory(lower, upper)).join(' ');
}
function constantCase(input, options) {
const upper = upperFactory(options?.locale);
return split(input, options).map(upper).join('_');
}
function dotCase(input, options) {
const lower = lowerFactory(options?.locale);
return split(input, options).map(lower).join('.');
}
function kebabCase(input, options) {
const lower = lowerFactory(options?.locale);
return split(input, options).map(lower).join('-');
}
function pathCase(input, options) {
const lower = lowerFactory(options?.locale);
return split(input, options).map(lower).join('/');
}
function sentenceCase(input, options) {
const lower = lowerFactory(options?.locale);
const upper = upperFactory(options?.locale);
const transform = capitalCaseTransformFactory(lower, upper);
return split(input, options)
.map((word, index) => {
if (index === 0)
return transform(word, index);
return lower(word);
})
.join(' ');
}
function snakeCase(input, options) {
const lower = lowerFactory(options?.locale);
return split(input, options).map(lower).join('_');
}
function lowerFactory(locale) {
return locale === false
? (input) => input.toLowerCase()
: (input) => input.toLocaleLowerCase(locale);
}
function upperFactory(locale) {
return locale === false
? (input) => input.toUpperCase()
: (input) => input.toLocaleUpperCase(locale);
}
const capitalCaseTransformFactory = (lower, upper) => {
return (word) => `${upper(word[0])}${lower(word.slice(1))}`;
};
const pascalCaseTransformFactory = (lower, upper) => {
return (word, index) => {
const char0 = word[0];
const initial = index > 0 && char0 >= '0' && char0 <= '9' ? '_' + char0 : upper(char0);
return initial + lower(word.slice(1));
};
};
const TOKENS = /\S+|./g;
const IS_MANUAL_CASE = /\p{Ll}(?=[\p{Lu}])|\.\p{L}/u;
const ALPHANUMERIC_PATTERN = /[\p{L}\d]+/gu;
const WORD_SEPARATORS = new Set(['—', '–', '-', '―', '/']);
const SMALL_WORDS = new Set([
'an',
'and',
'as',
'at',
'because',
'but',
'by',
'en',
'for',
'if',
'in',
'neither',
'nor',
'of',
'on',
'or',
'only',
'over',
'per',
'so',
'some',
'that',
'than',
'the',
'to',
'up',
'upon',
'v',
'vs',
'versus',
'via',
'when',
'with',
'without',
'yet',
]);
function titleCase(input, options = {}) {
let result = '';
let m;
const { smallWords = SMALL_WORDS, locale } = typeof options === 'string' || Array.isArray(options) ? { locale: options } : options;
while ((m = TOKENS.exec(input)) !== null) {
const { 0: token, index } = m;
if (IS_MANUAL_CASE.test(token)) {
result += token;
}
else {
result += token.replace(ALPHANUMERIC_PATTERN, (m, i) => {
if (index > 0 && index + token.length < input.length && smallWords.has(m)) {
return m;
}
if (i > 1 && !WORD_SEPARATORS.has(input.charAt(index + i - 1))) {
return m;
}
return m.charAt(0).toLocaleUpperCase(locale) + m.slice(1);
});
}
}
return result;
}
function composeTextTransformers(...transformers) {
return text => {
let result = text;
for (const transformer of transformers) {
result = transformer(result);
}
return result;
};
}
const toCamelCase = text => camelCase(text);
const toCapitalCase = text => capitalCase(text);
const toConstantCase = text => constantCase(text);
const toDotCase = text => dotCase(text);
const toKebabCase = text => kebabCase(text);
const toLowerCase = text => text.toLocaleLowerCase();
const toPascalCase = text => pascalCase(text);
const toPathCase = text => pathCase(text);
const toSentenceCase = text => sentenceCase(text);
const toSnakeCase = text => snakeCase(text);
const toTitleCase = text => titleCase(text);
const toTrim = text => text.trim();
const toUpperCase = text => text.toLocaleUpperCase();
export { composeTextTransformers, toCamelCase, toCapitalCase, toConstantCase, toDotCase, toKebabCase, toLowerCase, toPascalCase, toPathCase, toSentenceCase, toSnakeCase, toTitleCase, toTrim, toUpperCase };