@handy-common-utils/misc-utils
Version:
Miscellaneous utilities
370 lines • 16.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.unescapeXml = exports.escapeXml = exports.applyWordCasing = exports.pluralise = exports.pluralize = exports.snakeToCamel = exports.camelToSnake = exports.capitalise = exports.capitalize = exports.truncate = exports.StringUtils = void 0;
class StringUtils {
/**
* Preserve basic casing from a template single word and apply it to another word.
*
* Rules:
* - If `casingTemplate` is all upper-case, return `word` in all upper-case.
* - If `casingTemplate` is Title Case (first letter uppercase, rest lowercase), return
* `word` in Title Case.
* - Otherwise return `word` as-is.
*
* This helper focuses on single-word tokens only and intentionally does not handle
* complex multi-word or mixed-case patterns. Use for word-level casing preservation
* (for example, to preserve input casing when returning a pluralized form).
*
* @param casingTemplate A word whose casing should be copied (e.g. 'Cat' or 'CAT')
* @param word The word to apply casing to (usually a transformed/lowercased form)
* @returns The `word` adjusted to match the template's basic casing
*/
static applyWordCasing(casingTemplate, word) {
if (casingTemplate === casingTemplate.toUpperCase())
return word.toUpperCase();
if (casingTemplate[0] === casingTemplate[0].toUpperCase() && casingTemplate.slice(1) === casingTemplate.slice(1).toLowerCase()) {
return word[0].toUpperCase() + word.slice(1).toLowerCase();
}
return word;
}
/**
* Truncates a string to a specified length, optionally adding a suffix.
* @param str The string to truncate
* @param length Maximum length of the resulting string (including suffix if provided)
* @param suffix Optional suffix to add to truncated string (default: '...')
* @returns Truncated string
*/
static truncate(str, length, suffix = '...') {
if (!str || str.length <= length)
return str;
const truncatedLength = length - suffix.length;
return truncatedLength <= 0 ? suffix.slice(0, length) : str.slice(0, truncatedLength) + suffix;
}
/**
* Capitalises the first letter of a string, making the rest lowercase.
* @param str The string to capitalise
* @returns Capitalised string
*/
static capitalise(str) {
return StringUtils.capitalize(str);
}
/**
* Capitalizes the first letter of a string, making the rest lowercase.
* @param str The string to capitalize
* @returns Capitalized string
*/
static capitalize(str) {
if (!str)
return str;
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
/**
* Converts a camelCase string to snake_case.
* @param str The camelCase string to convert
* @returns snake_case string
*/
static camelToSnake(str) {
if (!str)
return str;
return str
// eslint-disable-next-line unicorn/prefer-string-replace-all
.replace(/([\da-z])([A-Z])/g, '$1_$2')
// eslint-disable-next-line unicorn/prefer-string-replace-all
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
.toLowerCase()
.replace(/^_/, '');
}
/**
* Converts a snake_case string to camelCase.
* @param str The snake_case string to convert
* @returns camelCase string
*/
static snakeToCamel(str) {
if (!str)
return str;
return str
.toLowerCase()
// eslint-disable-next-line unicorn/prefer-string-replace-all
.replace(/_+([a-z])/g, (_, letter) => letter.toUpperCase());
}
/**
* Returns the plural form of a single English word based on the supplied count.
*
* Capabilities:
* - Preserves basic input casing (using `applyWordCasing`) so e.g. "Cat" -> "Cats",
* "CAT" -> "CATS".
* - Handles common irregular plurals (person->people, child->children, mouse->mice, etc.).
* - Treats a number of nouns as uncountable (sheep, fish, species, series, news, etc.).
* - Applies common rules: f/fe -> ves (knife->knives), consonant+y -> ies (baby->babies),
* words ending with s/x/z/ch/sh -> add 'es'.
* - For words ending with 'o' there is a small exceptions list that will add 'es' (hero,
* potato, tomato, echo, torpedo); otherwise 's' is added.
*
* Limitations and notes:
* - This is a pragmatic, rule-based implementation covering the most common English cases,
* not a complete linguistic solution. It does not support locales or full irregular/exception
* lists (many English words have irregular forms not included here).
* - The function expects a single word token. It does not pluralize multi-word phrases or
* attempt to inflect verbs. Use a dedicated library (for example, the 'pluralize' npm
* package) if you need comprehensive, production-grade pluralization.
* - Casing preservation is basic (all-caps and Title Case); mixed/mid-word casing (camelCase,
* acronyms inside words) is not fully reconstructed.
*
* @param word The single English word to pluralize (may be mixed case)
* @param count The numeric count; if equal to 1 the original word is returned
* @returns The pluralized word with basic casing preserved
*/
static pluralise(word, count) {
return StringUtils.pluralize(word, count);
}
/**
* Returns the plural form of a single English word based on the supplied count.
*
* Capabilities:
* - Preserves basic input casing (using `applyWordCasing`) so e.g. "Cat" -> "Cats",
* "CAT" -> "CATS".
* - Handles common irregular plurals (person->people, child->children, mouse->mice, etc.).
* - Treats a number of nouns as uncountable (sheep, fish, species, series, news, etc.).
* - Applies common rules: f/fe -> ves (knife->knives), consonant+y -> ies (baby->babies),
* words ending with s/x/z/ch/sh -> add 'es'.
* - For words ending with 'o' there is a small exceptions list that will add 'es' (hero,
* potato, tomato, echo, torpedo); otherwise 's' is added.
*
* Limitations and notes:
* - This is a pragmatic, rule-based implementation covering the most common English cases,
* not a complete linguistic solution. It does not support locales or full irregular/exception
* lists (many English words have irregular forms not included here).
* - The function expects a single word token. It does not pluralize multi-word phrases or
* attempt to inflect verbs. Use a dedicated library (for example, the 'pluralize' npm
* package) if you need comprehensive, production-grade pluralization.
* - Casing preservation is basic (all-caps and Title Case); mixed/mid-word casing (camelCase,
* acronyms inside words) is not fully reconstructed.
*
* @param word The single English word to pluralize (may be mixed case)
* @param count The numeric count; if equal to 1 the original word is returned
* @returns The pluralized word with basic casing preserved
*/
static pluralize(word, count) {
if (!word)
return word;
if (count === 1)
return word;
const lower = word.toLowerCase();
// Uncountable nouns
const uncountables = new Set([
'bison',
'deer',
'equipment',
'fish',
'information',
'money',
'moose',
'news',
'rice',
'salmon',
'series',
'sheep',
'species',
'swine',
'trout',
]);
if (uncountables.has(lower))
return word;
// Irregular plurals
const irregulars = {
person: 'people',
man: 'men',
woman: 'women',
child: 'children',
tooth: 'teeth',
foot: 'feet',
mouse: 'mice',
goose: 'geese',
ox: 'oxen',
louse: 'lice',
cactus: 'cacti',
nucleus: 'nuclei',
syllabus: 'syllabi',
focus: 'foci',
phenomenon: 'phenomena',
datum: 'data',
radius: 'radii',
index: 'indices',
appendix: 'appendices',
criterion: 'criteria',
};
if (Object.prototype.hasOwnProperty.call(irregulars, lower)) {
return StringUtils.applyWordCasing(word, irregulars[lower]);
}
// Words that form plural by adding 'es' when ending with o
const oEsExceptions = new Set(['echo', 'hero', 'potato', 'tomato', 'torpedo']);
// Rules-based pluralization
// f/fe -> ves (knife -> knives)
if (/([a-z])(?:fe|f)$/.test(lower)) {
return StringUtils.applyWordCasing(word, lower.replace(/(?:fe|f)$/, 'ves'));
}
// words ending with 'y' preceded by consonant -> replace y with ies
if (/[b-df-hj-np-tv-z]y$/.test(lower)) {
return StringUtils.applyWordCasing(word, lower.replace(/y$/, 'ies'));
}
// words ending with s, x, z, ch, sh -> add 'es'
if (/(?:[sxz]|ch|sh)$/.test(lower)) {
return StringUtils.applyWordCasing(word, lower + 'es');
}
// words ending with 'o' -> usually add 's', but some add 'es'
if (lower.endsWith('o')) {
if (oEsExceptions.has(lower))
return StringUtils.applyWordCasing(word, lower + 'es');
return StringUtils.applyWordCasing(word, lower + 's');
}
// Default: just add 's'
return StringUtils.applyWordCasing(word, lower + 's');
}
/**
* Escapes special XML entities/characters in a string.
* Replaces &, <, >, ", and ' with their corresponding XML entities.
* Designed for performance using a single pass and lookup table.
* @param str The string to escape for XML
* @returns The escaped XML string
*/
static escapeXml(str) {
if (!str)
return str;
const xmlEntities = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
// Use replaceAll for each entity for lint compliance
// eslint-disable-next-line unicorn/prefer-string-replace-all
return str.replace(/[&<>'"]/g, ch => xmlEntities[ch]);
}
/**
* Unescapes XML entities/characters in a string.
* Converts &, <, >, ", ' back to their original characters.
* @param str The string to unescape from XML
* @returns The unescaped string
*/
static unescapeXml(str) {
if (!str)
return str;
const xmlEntities = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
''': "'",
};
// Replace all XML entities using a regex
// eslint-disable-next-line unicorn/prefer-string-replace-all
return str.replace(/&(amp|lt|gt|quot|apos);/g, entity => xmlEntities[entity]);
}
}
exports.StringUtils = StringUtils;
// Export functions as constants for convenience
/**
* Truncates a string to a specified length, optionally adding a suffix.
* @param str The string to truncate
* @param length Maximum length of the resulting string (including suffix if provided)
* @param suffix Optional suffix to add to truncated string (default: '...')
* @returns Truncated string
*/
exports.truncate = StringUtils.truncate;
/**
* Capitalizes the first letter of a string, making the rest lowercase.
* @param str The string to capitalize
* @returns Capitalized string
*/
exports.capitalize = StringUtils.capitalize;
/**
* Capitalises the first letter of a string, making the rest lowercase.
* @param str The string to capitalise
* @returns Capitalised string
*/
exports.capitalise = StringUtils.capitalise;
/**
* Converts a camelCase string to snake_case.
* @param str The camelCase string to convert
* @returns snake_case string
*/
exports.camelToSnake = StringUtils.camelToSnake;
/**
* Converts a snake_case string to camelCase.
* @param str The snake_case string to convert
* @returns camelCase string
*/
exports.snakeToCamel = StringUtils.snakeToCamel;
/**
* Returns the plural form of a single English word based on the supplied count.
*
* Capabilities:
* - Preserves basic input casing (using `applyWordCasing`) so e.g. "Cat" -> "Cats",
* "CAT" -> "CATS".
* - Handles common irregular plurals (person->people, child->children, mouse->mice, etc.).
* - Treats a number of nouns as uncountable (sheep, fish, species, series, news, etc.).
* - Applies common rules: f/fe -> ves (knife->knives), consonant+y -> ies (baby->babies),
* words ending with s/x/z/ch/sh -> add 'es'.
* - For words ending with 'o' there is a small exceptions list that will add 'es' (hero,
* potato, tomato, echo, torpedo); otherwise 's' is added.
*
* Limitations and notes:
* - This is a pragmatic, rule-based implementation covering the most common English cases,
* not a complete linguistic solution. It does not support locales or full irregular/exception
* lists (many English words have irregular forms not included here).
* - The function expects a single word token. It does not pluralize multi-word phrases or
* attempt to inflect verbs. Use a dedicated library (for example, the 'pluralize' npm
* package) if you need comprehensive, production-grade pluralization.
* - Casing preservation is basic (all-caps and Title Case); mixed/mid-word casing (camelCase,
* acronyms inside words) is not fully reconstructed.
*
* @param word The single English word to pluralize (may be mixed case)
* @param count The numeric count; if equal to 1 the original word is returned
* @returns The pluralized word with basic casing preserved
*/
exports.pluralize = StringUtils.pluralize;
/**
* Returns the plural form of a single English word based on the supplied count (alias).
*
* See `pluralize` for capabilities and limitations.
*
* @param word The single English word to pluralize (may be mixed case)
* @param count The numeric count; if equal to 1 the original word is returned
* @returns The pluralized word with basic casing preserved
*/
exports.pluralise = StringUtils.pluralise;
/**
* Preserve basic casing from a template single word and apply it to another word.
*
* Rules:
* - If `casingTemplate` is all upper-case, return `word` in all upper-case.
* - If `casingTemplate` is Title Case (first letter uppercase, rest lowercase), return
* `word` in Title Case.
* - Otherwise return `word` as-is.
*
* This helper focuses on single-word tokens only and intentionally does not handle
* complex multi-word or mixed-case patterns. Use for word-level casing preservation
* (for example, to preserve input casing when returning a pluralized form).
*
* @param casingTemplate A word whose casing should be copied (e.g. 'Cat' or 'CAT')
* @param word The word to apply casing to (usually a transformed/lowercased form)
* @returns The `word` adjusted to match the template's basic casing
*/
exports.applyWordCasing = StringUtils.applyWordCasing;
/**
* Escapes special XML entities/characters in a string.
* Replaces &, <, >, ", and ' with their corresponding XML entities.
* Designed for performance using a single pass and lookup table.
* @param str The string to escape for XML
* @returns The escaped XML string
*/
exports.escapeXml = StringUtils.escapeXml;
/**
* Unescapes XML entities/characters in a string.
* Converts &, <, >, ", ' back to their original characters.
* @param str The string to unescape from XML
* @returns The unescaped string
*/
exports.unescapeXml = StringUtils.unescapeXml;
//# sourceMappingURL=string.js.map