Skip to content

Commit a3225b0

Browse files
authored
Absorb all special characters when converting string cases (#367)
1 parent d956af2 commit a3225b0

File tree

3 files changed

+168
-1
lines changed

3 files changed

+168
-1
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@codama/nodes': minor
3+
---
4+
5+
Absorb all special characters when converting string cases

packages/nodes/src/shared/stringCases.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function capitalize(str: string): string {
1414
export function titleCase(str: string): TitleCaseString {
1515
return str
1616
.replace(/([A-Z])/g, ' $1')
17-
.split(/[-_\s+.]/)
17+
.split(/[^a-zA-Z0-9]+/)
1818
.filter(word => word.length > 0)
1919
.map(capitalize)
2020
.join(' ') as TitleCaseString;
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { describe, expect, test } from 'vitest';
2+
3+
import { camelCase, snakeCase, titleCase } from '../../src';
4+
5+
describe('snakeCase', () => {
6+
test('casing', () => {
7+
expect(snakeCase('lowercased')).toBe('lowercased');
8+
expect(snakeCase('UPPERCASED')).toBe('u_p_p_e_r_c_a_s_e_d');
9+
expect(snakeCase('Capitalized')).toBe('capitalized');
10+
});
11+
test('numbers', () => {
12+
expect(snakeCase('1before after2 bet3ween')).toBe('1before_after2_bet3ween');
13+
expect(snakeCase('50m3 1 2 3 numb3rs 8 3v3rywh3r3 9')).toBe('50m3_1_2_3_numb3rs_8_3v3rywh3r3_9');
14+
expect(snakeCase(snakeCase('50m3 1 2 3 numb3rs 8 3v3rywh3r3 9'))).toBe('50m3_1_2_3_numb3rs_8_3v3rywh3r3_9');
15+
});
16+
test('special characters', () => {
17+
expect(snakeCase('some::special\\\\chars+++in=between')).toBe('some_special_chars_in_between');
18+
expect(snakeCase('$peçia!::ch*rs')).toBe('pe_ia_ch_rs');
19+
expect(snakeCase('multiple.........dots')).toBe('multiple_dots');
20+
expect(snakeCase('multiple---------dashes')).toBe('multiple_dashes');
21+
expect(snakeCase('multiple_________underscores')).toBe('multiple_underscores');
22+
});
23+
test('from snake case', () => {
24+
expect(snakeCase('from_lowercased_snake_case')).toBe('from_lowercased_snake_case');
25+
expect(snakeCase('From_Capitalized_Snake_Case')).toBe('from_capitalized_snake_case');
26+
expect(snakeCase('FROM_UPPERCASED_SNAKE_CASE')).toBe('f_r_o_m_u_p_p_e_r_c_a_s_e_d_s_n_a_k_e_c_a_s_e');
27+
expect(snakeCase('fr0m_5nak3_c4s3_w1th_42n_numb3r5')).toBe('fr0m_5nak3_c4s3_w1th_42n_numb3r5');
28+
expect(snakeCase('frøm_snake_case_w:th_$peçia!_ch*rs')).toBe('fr_m_snake_case_w_th_pe_ia_ch_rs');
29+
expect(snakeCase(snakeCase('frøm_d0ubl3_Snake_c*se'))).toBe('fr_m_d0ubl3_snake_c_se');
30+
});
31+
test('from title case', () => {
32+
expect(snakeCase('from lowercased title case')).toBe('from_lowercased_title_case');
33+
expect(snakeCase('From Capitalized Title Case')).toBe('from_capitalized_title_case');
34+
expect(snakeCase('FROM UPPERCASED TITLE CASE')).toBe('f_r_o_m_u_p_p_e_r_c_a_s_e_d_t_i_t_l_e_c_a_s_e');
35+
expect(snakeCase('Fr0m T1tl3 C4s3 W1th 42n Numb3r5')).toBe('fr0m_t1tl3_c4s3_w1th_42n_numb3r5');
36+
expect(snakeCase('Frøm Title Case W:th $peçia! Ch*rs')).toBe('fr_m_title_case_w_th_pe_ia_ch_rs');
37+
expect(snakeCase(snakeCase('Frøm D0ubl3 Title C*se'))).toBe('fr_m_d0ubl3_title_c_se');
38+
});
39+
test('from pascal case', () => {
40+
expect(snakeCase('FromPascaleCase')).toBe('from_pascale_case');
41+
expect(snakeCase('Fr0mP45c4l3C4s3W1th42nNumb3r5')).toBe('fr0m_p45c4l3_c4s3_w1th42n_numb3r5');
42+
expect(snakeCase('FrømPascaleCaseW:th$peçia!Ch*rs')).toBe('fr_m_pascale_case_w_th_pe_ia_ch_rs');
43+
expect(snakeCase(snakeCase('FrømD0ubl3PascaleC*se'))).toBe('fr_m_d0ubl3_pascale_c_se');
44+
});
45+
test('from camel case', () => {
46+
expect(snakeCase('FromCamelCase')).toBe('from_camel_case');
47+
expect(snakeCase('Fr0mC4m3lC4s3W1th42nNumb3r5')).toBe('fr0m_c4m3l_c4s3_w1th42n_numb3r5');
48+
expect(snakeCase('FrømCamelCaseW:th$peçia!Ch*rs')).toBe('fr_m_camel_case_w_th_pe_ia_ch_rs');
49+
expect(snakeCase(snakeCase('FrømD0ubl3CamelC*se'))).toBe('fr_m_d0ubl3_camel_c_se');
50+
});
51+
test('from paths', () => {
52+
expect(snakeCase('crate::my_module::my_type')).toBe('crate_my_module_my_type');
53+
expect(snakeCase('/Users/username/My File.txt')).toBe('users_username_my_file_txt');
54+
expect(snakeCase('C:\\Users\\username\\My\\ File.txt')).toBe('c_users_username_my_file_txt');
55+
});
56+
});
57+
58+
describe('titleCase', () => {
59+
test('casing', () => {
60+
expect(titleCase('lowercased')).toBe('Lowercased');
61+
expect(titleCase('UPPERCASED')).toBe('U P P E R C A S E D');
62+
expect(titleCase('Capitalized')).toBe('Capitalized');
63+
});
64+
test('numbers', () => {
65+
expect(titleCase('1before after2 bet3ween')).toBe('1before After2 Bet3ween');
66+
expect(titleCase('50m3 1 2 3 numb3rs 8 3v3rywh3r3 9')).toBe('50m3 1 2 3 Numb3rs 8 3v3rywh3r3 9');
67+
expect(titleCase(titleCase('50m3 1 2 3 numb3rs 8 3v3rywh3r3 9'))).toBe('50m3 1 2 3 Numb3rs 8 3v3rywh3r3 9');
68+
});
69+
test('special characters', () => {
70+
expect(titleCase('some::special\\\\chars+++in=between')).toBe('Some Special Chars In Between');
71+
expect(titleCase('$peçia!::ch*rs')).toBe('Pe Ia Ch Rs');
72+
expect(titleCase('multiple.........dots')).toBe('Multiple Dots');
73+
expect(titleCase('multiple---------dashes')).toBe('Multiple Dashes');
74+
expect(titleCase('multiple_________underscores')).toBe('Multiple Underscores');
75+
});
76+
test('from snake case', () => {
77+
expect(titleCase('from_lowercased_snake_case')).toBe('From Lowercased Snake Case');
78+
expect(titleCase('From_Capitalized_Snake_Case')).toBe('From Capitalized Snake Case');
79+
expect(titleCase('FROM_UPPERCASED_SNAKE_CASE')).toBe('F R O M U P P E R C A S E D S N A K E C A S E');
80+
expect(titleCase('fr0m_5nak3_c4s3_w1th_42n_numb3r5')).toBe('Fr0m 5nak3 C4s3 W1th 42n Numb3r5');
81+
expect(titleCase('frøm_snake_case_w:th_$peçia!_ch*rs')).toBe('Fr M Snake Case W Th Pe Ia Ch Rs');
82+
expect(titleCase(titleCase('frøm_d0ubl3_Snake_c*se'))).toBe('Fr M D0ubl3 Snake C Se');
83+
});
84+
test('from title case', () => {
85+
expect(titleCase('from lowercased title case')).toBe('From Lowercased Title Case');
86+
expect(titleCase('From Capitalized Title Case')).toBe('From Capitalized Title Case');
87+
expect(titleCase('FROM UPPERCASED TITLE CASE')).toBe('F R O M U P P E R C A S E D T I T L E C A S E');
88+
expect(titleCase('Fr0m T1tl3 C4s3 W1th 42n Numb3r5')).toBe('Fr0m T1tl3 C4s3 W1th 42n Numb3r5');
89+
expect(titleCase('Frøm Title Case W:th $peçia! Ch*rs')).toBe('Fr M Title Case W Th Pe Ia Ch Rs');
90+
expect(titleCase(titleCase('Frøm D0ubl3 Title C*se'))).toBe('Fr M D0ubl3 Title C Se');
91+
});
92+
test('from pascal case', () => {
93+
expect(titleCase('FromPascaleCase')).toBe('From Pascale Case');
94+
expect(titleCase('Fr0mP45c4l3C4s3W1th42nNumb3r5')).toBe('Fr0m P45c4l3 C4s3 W1th42n Numb3r5');
95+
expect(titleCase('FrømPascaleCaseW:th$peçia!Ch*rs')).toBe('Fr M Pascale Case W Th Pe Ia Ch Rs');
96+
expect(titleCase(titleCase('FrømD0ubl3PascaleC*se'))).toBe('Fr M D0ubl3 Pascale C Se');
97+
});
98+
test('from camel case', () => {
99+
expect(titleCase('FromCamelCase')).toBe('From Camel Case');
100+
expect(titleCase('Fr0mC4m3lC4s3W1th42nNumb3r5')).toBe('Fr0m C4m3l C4s3 W1th42n Numb3r5');
101+
expect(titleCase('FrømCamelCaseW:th$peçia!Ch*rs')).toBe('Fr M Camel Case W Th Pe Ia Ch Rs');
102+
expect(titleCase(titleCase('FrømD0ubl3CamelC*se'))).toBe('Fr M D0ubl3 Camel C Se');
103+
});
104+
test('from paths', () => {
105+
expect(titleCase('crate::my_module::my_type')).toBe('Crate My Module My Type');
106+
expect(titleCase('/Users/username/My File.txt')).toBe('Users Username My File Txt');
107+
expect(titleCase('C:\\Users\\username\\My\\ File.txt')).toBe('C Users Username My File Txt');
108+
});
109+
});
110+
111+
describe('camelCase', () => {
112+
test('casing', () => {
113+
expect(camelCase('lowercased')).toBe('lowercased');
114+
expect(camelCase('UPPERCASED')).toBe('uPPERCASED');
115+
expect(camelCase('Capitalized')).toBe('capitalized');
116+
});
117+
test('numbers', () => {
118+
expect(camelCase('1before after2 bet3ween')).toBe('1beforeAfter2Bet3ween');
119+
expect(camelCase('50m3 1 2 3 numb3rs 8 3v3rywh3r3 9')).toBe('50m3123Numb3rs83v3rywh3r39');
120+
expect(titleCase(camelCase('50m3 1 2 3 numb3rs 8 3v3rywh3r3 9'))).toBe('50m3123 Numb3rs83v3rywh3r39');
121+
});
122+
test('special characters', () => {
123+
expect(camelCase('some::special\\\\chars+++in=between')).toBe('someSpecialCharsInBetween');
124+
expect(camelCase('$peçia!::ch*rs')).toBe('peIaChRs');
125+
expect(camelCase('multiple.........dots')).toBe('multipleDots');
126+
expect(camelCase('multiple---------dashes')).toBe('multipleDashes');
127+
expect(camelCase('multiple_________underscores')).toBe('multipleUnderscores');
128+
});
129+
test('from snake case', () => {
130+
expect(camelCase('from_lowercased_snake_case')).toBe('fromLowercasedSnakeCase');
131+
expect(camelCase('From_Capitalized_Snake_Case')).toBe('fromCapitalizedSnakeCase');
132+
expect(camelCase('FROM_UPPERCASED_SNAKE_CASE')).toBe('fROMUPPERCASEDSNAKECASE');
133+
expect(camelCase('fr0m_5nak3_c4s3_w1th_42n_numb3r5')).toBe('fr0m5nak3C4s3W1th42nNumb3r5');
134+
expect(camelCase('frøm_snake_case_w:th_$peçia!_ch*rs')).toBe('frMSnakeCaseWThPeIaChRs');
135+
expect(camelCase(camelCase('frøm_d0ubl3_Snake_c*se'))).toBe('frMD0ubl3SnakeCSe');
136+
});
137+
test('from title case', () => {
138+
expect(camelCase('from lowercased title case')).toBe('fromLowercasedTitleCase');
139+
expect(camelCase('From Capitalized Title Case')).toBe('fromCapitalizedTitleCase');
140+
expect(camelCase('FROM UPPERCASED TITLE CASE')).toBe('fROMUPPERCASEDTITLECASE');
141+
expect(camelCase('Fr0m T1tl3 C4s3 W1th 42n Numb3r5')).toBe('fr0mT1tl3C4s3W1th42nNumb3r5');
142+
expect(camelCase('Frøm Title Case W:th $peçia! Ch*rs')).toBe('frMTitleCaseWThPeIaChRs');
143+
expect(camelCase(camelCase('Frøm D0ubl3 Title C*se'))).toBe('frMD0ubl3TitleCSe');
144+
});
145+
test('from pascal case', () => {
146+
expect(camelCase('FromPascaleCase')).toBe('fromPascaleCase');
147+
expect(camelCase('Fr0mP45c4l3C4s3W1th42nNumb3r5')).toBe('fr0mP45c4l3C4s3W1th42nNumb3r5');
148+
expect(camelCase('FrømPascaleCaseW:th$peçia!Ch*rs')).toBe('frMPascaleCaseWThPeIaChRs');
149+
expect(camelCase(camelCase('FrømD0ubl3PascaleC*se'))).toBe('frMD0ubl3PascaleCSe');
150+
});
151+
test('from camel case', () => {
152+
expect(camelCase('FromCamelCase')).toBe('fromCamelCase');
153+
expect(camelCase('Fr0mC4m3lC4s3W1th42nNumb3r5')).toBe('fr0mC4m3lC4s3W1th42nNumb3r5');
154+
expect(camelCase('FrømCamelCaseW:th$peçia!Ch*rs')).toBe('frMCamelCaseWThPeIaChRs');
155+
expect(camelCase(camelCase('FrømD0ubl3CamelC*se'))).toBe('frMD0ubl3CamelCSe');
156+
});
157+
test('from paths', () => {
158+
expect(camelCase('crate::my_module::my_type')).toBe('crateMyModuleMyType');
159+
expect(camelCase('/Users/username/My File.txt')).toBe('usersUsernameMyFileTxt');
160+
expect(camelCase('C:\\Users\\username\\My\\ File.txt')).toBe('cUsersUsernameMyFileTxt');
161+
});
162+
});

0 commit comments

Comments
 (0)