-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
utils.ts
116 lines (100 loc) · 3.53 KB
/
utils.ts
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Latin-1 Supplement
// upper case ranges
// [À-ÖØ-ß]
// lower case ranges
// [à-öø-ÿ]
export const magicSplit =
/^[a-zà-öø-ÿа-я]+|[A-ZÀ-ÖØ-ßА-Я][a-zà-öø-ÿа-я]+|[a-zà-öø-ÿа-я]+|[0-9]+|[A-ZÀ-ÖØ-ßА-Я]+(?![a-zà-öø-ÿа-я])/g
export const spaceSplit = /\S+/g
/**
* A string.matchAll function that will return an array of "string parts" and the indexes at which it split each part
*/
export function getPartsAndIndexes(
string: string,
splitRegex: RegExp,
): {
parts: string[]
prefixes: string[]
} {
const result = { parts: [] as string[], prefixes: [] as string[] }
const matches = string.matchAll(splitRegex)
let lastWordEndIndex = 0
for (const match of matches) {
if (typeof match.index !== 'number') continue
const word = match[0]
result.parts.push(word)
const prefix = string.slice(lastWordEndIndex, match.index).trim()
result.prefixes.push(prefix)
lastWordEndIndex = match.index + word.length
}
const tail = string.slice(lastWordEndIndex).trim()
if (tail) {
result.parts.push('')
result.prefixes.push(tail)
}
return result
}
/**
* A function that splits a string on words and returns an array of words.
* - It can prefix each word with a given character
* - It can strip or keep special characters, this affects the logic for adding a prefix as well
*/
export function splitAndPrefix(
string: string,
options?: { keepSpecialCharacters?: boolean; keep?: string[]; prefix?: string },
): string[] {
const { keepSpecialCharacters = false, keep, prefix = '' } = options || {}
const normalString = string.trim().normalize('NFC')
const hasSpaces = normalString.includes(' ')
const split = hasSpaces ? spaceSplit : magicSplit
const partsAndIndexes = getPartsAndIndexes(normalString, split)
return partsAndIndexes.parts
.map((_part, i) => {
let foundPrefix = partsAndIndexes.prefixes[i] || ''
let part = _part
if (keepSpecialCharacters === false) {
if (keep) {
part = part
.normalize('NFD')
.replace(new RegExp(`[^a-zA-ZØßø0-9${keep.join('')}]`, 'g'), '')
}
if (!keep) {
part = part.normalize('NFD').replace(/[^a-zA-ZØßø0-9]/g, '')
foundPrefix = ''
}
}
if (keep && foundPrefix) {
foundPrefix = foundPrefix.replace(new RegExp(`[^${keep.join('')}]`, 'g'), '')
}
// the first word doesn't need a prefix, so only return the found prefix
if (i === 0) {
// console.log(`foundPrefix → `, foundPrefix)
return foundPrefix + part
}
if (!foundPrefix && !part) return ''
if (!hasSpaces) {
// return the found prefix OR fall back to a given prefix
return (foundPrefix || prefix) + part
}
// space based sentence was split on spaces, so only return found prefixes
if (!foundPrefix && prefix.match(/\s/)) {
// in this case we have no more found prefix, it was trimmed, but we're looking to add a space
// so let's return that space
return ' ' + part
}
return (foundPrefix || prefix) + part
})
.filter(Boolean)
}
/**
* Capitalises a single word
* @returns the word with the first character in uppercase and the rest in lowercase
*/
export function capitaliseWord(string: string): string {
const match = string.matchAll(magicSplit).next().value
const firstLetterIndex = match ? match.index : 0
return (
string.slice(0, firstLetterIndex + 1).toUpperCase() +
string.slice(firstLetterIndex + 1).toLowerCase()
)
}