Skip to content

Commit e3e5fb8

Browse files
committed
Fix casing of unicode characters
1 parent fd80ba4 commit e3e5fb8

File tree

2 files changed

+56
-25
lines changed

2 files changed

+56
-25
lines changed

lib/utils/casing.js

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
const assert = require('assert')
22

3-
const invalidChars = /[^a-zA-Z0-9:]+/g
3+
function parseWords (str) {
4+
return str
5+
.normalize()
6+
.replace(/[^\p{L}0-9:]+/gu, ' ')
7+
.split(/([\p{Upper}\s][0-9\p{Lower}:]*)/gu)
8+
.map(word => word.trim())
9+
.filter(Boolean)
10+
}
11+
12+
function capitalizeFirstLetter (str) {
13+
return str[0].toUpperCase() + str.slice(1)
14+
}
415

516
/**
617
* Convert text to kebab-case
718
* @param {string} str Text to be converted
819
* @return {string}
920
*/
1021
function kebabCase (str) {
11-
return str
12-
.replace(/[A-Z]/g, match => '-' + match)
13-
.replace(/([^a-zA-Z])-([A-Z])/g, match => match[0] + match[2])
14-
.replace(/^-/, '')
15-
.replace(invalidChars, '-')
22+
return parseWords(str)
23+
.join('-')
1624
.toLowerCase()
1725
}
1826

@@ -22,11 +30,8 @@ function kebabCase (str) {
2230
* @return {string}
2331
*/
2432
function snakeCase (str) {
25-
return str
26-
.replace(/[A-Z]/g, match => '_' + match)
27-
.replace(/([^a-zA-Z])_([A-Z])/g, match => match[0] + match[2])
28-
.replace(/^_/, '')
29-
.replace(invalidChars, '_')
33+
return parseWords(str)
34+
.join('_')
3035
.toLowerCase()
3136
}
3237

@@ -36,12 +41,9 @@ function snakeCase (str) {
3641
* @return {string} Converted string
3742
*/
3843
function camelCase (str) {
39-
return str
40-
.replace(/_/g, (_, index) => index === 0 ? _ : '-')
41-
.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) =>
42-
index === 0 ? letter.toLowerCase() : letter.toUpperCase()
43-
)
44-
.replace(invalidChars, '')
44+
return parseWords(str)
45+
.map((word, index) => index === 0 ? word.toLowerCase() : capitalizeFirstLetter(word))
46+
.join('')
4547
}
4648

4749
/**
@@ -50,10 +52,9 @@ function camelCase (str) {
5052
* @return {string} Converted string
5153
*/
5254
function pascalCase (str) {
53-
return str
54-
.replace(/_/g, (_, index) => index === 0 ? _ : '-')
55-
.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => letter.toUpperCase())
56-
.replace(invalidChars, '')
55+
return parseWords(str)
56+
.map((word) => capitalizeFirstLetter(word))
57+
.join('')
5758
}
5859

5960
const convertersMap = {

tests/lib/utils/casing.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('getConverter()', () => {
99
it('should convert string to camelCase', () => {
1010
const converter = casing.getConverter('camelCase')
1111

12+
assert.equal(converter('foo'), 'foo')
1213
assert.equal(converter('fooBar'), 'fooBar')
1314
assert.equal(converter('foo-bar'), 'fooBar')
1415
assert.equal(converter('foo_bar'), 'fooBar')
@@ -17,11 +18,14 @@ describe('getConverter()', () => {
1718
assert.equal(converter('FooBAR'), 'fooBAR')
1819
assert.equal(converter('Foo1BAZ'), 'foo1BAZ')
1920
assert.equal(converter('foo1b_a_z'), 'foo1bAZ')
21+
assert.equal(converter('darИībaÊÊw'), 'darИībaÊÊw')
22+
assert.equal(converter(' foo Bar '), 'fooBar')
2023
})
2124

2225
it('should convert string to PascalCase', () => {
2326
const converter = casing.getConverter('PascalCase')
2427

28+
assert.equal(converter('foo'), 'Foo')
2529
assert.equal(converter('fooBar'), 'FooBar')
2630
assert.equal(converter('foo-bar'), 'FooBar')
2731
assert.equal(converter('foo_bar'), 'FooBar')
@@ -30,30 +34,56 @@ describe('getConverter()', () => {
3034
assert.equal(converter('FooBAR'), 'FooBAR')
3135
assert.equal(converter('Foo1BAZ'), 'Foo1BAZ')
3236
assert.equal(converter('foo1b_a_z'), 'Foo1bAZ')
37+
assert.equal(converter('darИībaÊÊw'), 'DarИībaÊÊw')
38+
assert.equal(converter(' foo Bar '), 'FooBar')
3339
})
3440

3541
it('should convert string to kebab-case', () => {
3642
const converter = casing.getConverter('kebab-case')
3743

44+
assert.equal(converter('foo'), 'foo')
3845
assert.equal(converter('fooBar'), 'foo-bar')
3946
assert.equal(converter('foo-bar'), 'foo-bar')
4047
assert.equal(converter('foo_bar'), 'foo-bar')
4148
assert.equal(converter('FooBar'), 'foo-bar')
42-
assert.equal(converter('Foo1Bar'), 'foo1bar')
49+
assert.equal(converter('Foo1Bar'), 'foo1-bar')
4350
assert.equal(converter('FooBAR'), 'foo-b-a-r')
44-
assert.equal(converter('Foo1BAZ'), 'foo1b-a-z')
51+
assert.equal(converter('Foo1BAZ'), 'foo1-b-a-z')
4552
assert.equal(converter('foo1b_a_z'), 'foo1b-a-z')
53+
assert.equal(converter('darИībaÊÊw'), 'dar-иība-ê-êw')
54+
assert.equal(converter(' foo Bar '), 'foo-bar')
4655
})
4756

4857
it('should convert string to snake_case', () => {
4958
const converter = casing.getConverter('snake_case')
5059

60+
assert.equal(converter('a'), 'a')
5161
assert.equal(converter('fooBar'), 'foo_bar')
5262
assert.equal(converter('foo-bar'), 'foo_bar')
5363
assert.equal(converter('FooBar'), 'foo_bar')
54-
assert.equal(converter('Foo1Bar'), 'foo1bar')
64+
assert.equal(converter('Foo1Bar'), 'foo1_bar')
5565
assert.equal(converter('FooBAR'), 'foo_b_a_r')
56-
assert.equal(converter('Foo1BAZ'), 'foo1b_a_z')
66+
assert.equal(converter('Foo1BAZ'), 'foo1_b_a_z')
5767
assert.equal(converter('foo1b_a_z'), 'foo1b_a_z')
68+
assert.equal(converter('darИībaÊÊw'), 'dar_иība_ê_êw')
69+
assert.equal(converter(' foo Bar '), 'foo_bar')
70+
})
71+
72+
it('unicode tests', () => {
73+
const words = [
74+
['ÊtreSîne', 'être_sîne'],
75+
['darbībaÊÊw', 'darbība_ê_êw'],
76+
['klâwen-ûf', 'klâwen_ûf'],
77+
['γλώσσαΤη', 'γλώσσα_τη'],
78+
['пустынныхИвдалП', 'пустынных_ивдал_п'],
79+
['kpłĄżć', 'kpł_ążć']
80+
]
81+
for (const [word, snake] of words) {
82+
assert.equal(casing.camelCase(word), casing.camelCase(casing.snakeCase(word)))
83+
assert.equal(casing.pascalCase(word), casing.pascalCase(casing.kebabCase(word)))
84+
assert.equal(casing.kebabCase(word), casing.kebabCase(casing.camelCase(word)))
85+
assert.equal(casing.snakeCase(word), casing.snakeCase(casing.pascalCase(word)))
86+
assert.equal(casing.snakeCase(word), snake)
87+
}
5888
})
5989
})

0 commit comments

Comments
 (0)