All files / string split-words.ts

100% Statements 29/29
100% Branches 23/23
100% Functions 3/3
100% Lines 29/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 5813x         611x 611x       687x 124x   563x 479x   84x                           74x 74x 74x 74x 687x 687x 76x 48x   76x 611x 31x 31x 580x 5x 5x 5x   575x   687x   74x 55x   74x    
const DIGIT_PATTERN = /^\p{N}$/v;
 
type CharacterKind = 'digit' | 'lower' | 'separator' | 'upper';
 
function isBoundary(previous: CharacterKind, current: CharacterKind): boolean {
  const isLetterToDigit = (previous === 'digit') !== (current === 'digit');
  return previous !== 'separator' && ((previous === 'lower' && current === 'upper') || isLetterToDigit);
}
 
function kindOf(character: string): CharacterKind {
  if (character !== character.toLowerCase()) {
    return 'upper';
  }
  if (character !== character.toUpperCase()) {
    return 'lower';
  }
  return DIGIT_PATTERN.test(character) ? 'digit' : 'separator';
}
 
/**
 * Splits a string into words, whatever its case style (camelCase, kebab-case, snake_case, spaces…), in one
 * pass. Acronyms and numbers are words of their own.
 *
 * @param input - The identifier or sentence to split, in any case style.
 * @returns The words, with their original case; empty when there is none.
 * @example
 * splitWords('XMLHttpRequest'); // ['XML', 'Http', 'Request']
 * splitWords('user_id-v2 Name'); // ['user', 'id', 'v', '2', 'Name']
 */
export function splitWords(input: string): string[] {
  const result: string[] = [];
  let word = '';
  let previous: CharacterKind = 'separator';
  for (const character of input) {
    const kind = kindOf(character);
    if (kind === 'separator') {
      if (word !== '') {
        result.push(word);
      }
      word = '';
    } else if (isBoundary(previous, kind)) {
      result.push(word);
      word = character;
    } else if (previous === 'upper' && kind === 'lower' && word.length > 1) {
      const lastCapital = word.slice(-1);
      result.push(word.slice(0, -1));
      word = lastCapital + character;
    } else {
      word += character;
    }
    previous = kind;
  }
  if (word !== '') {
    result.push(word);
  }
  return result;
}