Skip to content

Commit e2c4891

Browse files
Add preserveConsecutiveUppercase option (#78)
Co-authored-by: Sindre Sorhus <[email protected]>
1 parent a077c7b commit e2c4891

File tree

4 files changed

+117
-12
lines changed

4 files changed

+117
-12
lines changed

index.d.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ declare namespace camelcase {
77
*/
88
readonly pascalCase?: boolean;
99

10+
/**
11+
Preserve the consecutive uppercase characters: `foo-BAR` → `FooBAR`.
12+
13+
@default false
14+
*/
15+
readonly preserveConsecutiveUppercase?: boolean;
16+
1017
/**
1118
The locale parameter indicates the locale to be used to convert to upper/lower case according to any locale-specific case mappings. If multiple locales are given in an array, the best available locale is used.
1219
@@ -18,13 +25,10 @@ declare namespace camelcase {
1825
1926
camelCase('lorem-ipsum', {locale: 'en-US'});
2027
//=> 'loremIpsum'
21-
2228
camelCase('lorem-ipsum', {locale: 'tr-TR'});
2329
//=> 'loremİpsum'
24-
2530
camelCase('lorem-ipsum', {locale: ['en-US', 'en-GB']});
2631
//=> 'loremIpsum'
27-
2832
camelCase('lorem-ipsum', {locale: ['tr', 'TR', 'tr-TR']});
2933
//=> 'loremİpsum'
3034
```
@@ -62,6 +66,12 @@ camelCase('Foo-Bar', {pascalCase: true});
6266
camelCase('--foo.bar', {pascalCase: false});
6367
//=> 'fooBar'
6468
69+
camelCase('Foo-BAR', {preserveConsecutiveUppercase: true});
70+
//=> 'fooBAR'
71+
72+
camelCase('fooBAR', {pascalCase: true, preserveConsecutiveUppercase: true}));
73+
//=> 'FooBAR'
74+
6575
camelCase('foo bar');
6676
//=> 'fooBar'
6777
@@ -76,6 +86,9 @@ camelCase(['foo', 'bar']);
7686
camelCase(['__foo__', '--bar'], {pascalCase: true});
7787
//=> 'FooBar'
7888
89+
camelCase(['foo', 'BAR'], {pascalCase: true, preserveConsecutiveUppercase: true})
90+
//=> 'FooBAR'
91+
7992
camelCase('lorem-ipsum', {locale: 'en-US'});
8093
//=> 'loremIpsum'
8194
```

index.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,26 @@ const preserveCamelCase = (string, locale) => {
2929
return string;
3030
};
3131

32+
const preserveConsecutiveUppercase = input => {
33+
return input.replace(/^[\p{Lu}](?![\p{Lu}])/gu, m1 => m1.toLowerCase());
34+
};
35+
36+
const postProcess = (input, options) => {
37+
return input.replace(/[_.\- ]+([\p{Alpha}\p{N}_]|$)/gu, (_, p1) => p1.toLocaleUpperCase(options.locale))
38+
.replace(/\d+([\p{Alpha}\p{N}_]|$)/gu, m => m.toLocaleUpperCase(options.locale));
39+
};
40+
3241
const camelCase = (input, options) => {
3342
if (!(typeof input === 'string' || Array.isArray(input))) {
3443
throw new TypeError('Expected the input to be `string | string[]`');
3544
}
3645

3746
options = {
38-
...{pascalCase: false},
47+
pascalCase: false,
48+
preserveConsecutiveUppercase: false,
3949
...options
4050
};
4151

42-
const postProcess = x => options.pascalCase ? x.charAt(0).toLocaleUpperCase(options.locale) + x.slice(1) : x;
43-
4452
if (Array.isArray(input)) {
4553
input = input.map(x => x.trim())
4654
.filter(x => x.length)
@@ -63,13 +71,19 @@ const camelCase = (input, options) => {
6371
input = preserveCamelCase(input, options.locale);
6472
}
6573

66-
input = input
67-
.replace(/^[_.\- ]+/, '')
68-
.toLocaleLowerCase(options.locale)
69-
.replace(/[_.\- ]+([\p{Alpha}\p{N}_]|$)/gu, (_, p1) => p1.toLocaleUpperCase(options.locale))
70-
.replace(/\d+([\p{Alpha}\p{N}_]|$)/gu, m => m.toLocaleUpperCase(options.locale));
74+
input = input.replace(/^[_.\- ]+/, '');
75+
76+
if (options.preserveConsecutiveUppercase) {
77+
input = preserveConsecutiveUppercase(input);
78+
} else {
79+
input = input.toLocaleLowerCase();
80+
}
81+
82+
if (options.pascalCase) {
83+
input = input.charAt(0).toLocaleUpperCase(options.locale) + input.slice(1);
84+
}
7185

72-
return postProcess(input);
86+
return postProcess(input, options);
7387
};
7488

7589
module.exports = camelCase;

readme.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ camelCase('Foo-Bar', {pascalCase: true});
3535
camelCase('--foo.bar', {pascalCase: false});
3636
//=> 'fooBar'
3737

38+
camelCase('Foo-BAR', {preserveConsecutiveUppercase: true});
39+
//=> 'fooBAR'
40+
41+
camelCase('fooBAR', {pascalCase: true, preserveConsecutiveUppercase: true}));
42+
//=> 'FooBAR'
43+
3844
camelCase('foo bar');
3945
//=> 'fooBar'
4046

@@ -49,6 +55,9 @@ camelCase(['foo', 'bar']);
4955
camelCase(['__foo__', '--bar'], {pascalCase: true});
5056
//=> 'FooBar'
5157

58+
camelCase(['foo', 'BAR'], {pascalCase: true, preserveConsecutiveUppercase: true})
59+
//=> 'FooBAR'
60+
5261
camelCase('lorem-ipsum', {locale: 'en-US'});
5362
//=> 'loremIpsum'
5463
```
@@ -74,6 +83,13 @@ Default: `false`
7483

7584
Uppercase the first character: `foo-bar``FooBar`
7685

86+
##### preserveConsecutiveUppercase
87+
88+
Type: `boolean`\
89+
Default: `false`
90+
91+
Preserve the consecutive uppercase characters: `foo-BAR``FooBAR`.
92+
7793
##### locale
7894

7995
Type: `string | string[]`\

test.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,68 @@ test('camelCase with pascalCase option', t => {
130130
t.is(camelCase('桑德_在这里。', {pascalCase: true}), '桑德在这里。');
131131
});
132132

133+
test('camelCase with preserveConsecutiveUppercase option', t => {
134+
t.is(camelCase('foo-BAR', {preserveConsecutiveUppercase: true}), 'fooBAR');
135+
t.is(camelCase('Foo-BAR', {preserveConsecutiveUppercase: true}), 'fooBAR');
136+
t.is(camelCase('fooBAR', {preserveConsecutiveUppercase: true}), 'fooBAR');
137+
t.is(camelCase('fooBaR', {preserveConsecutiveUppercase: true}), 'fooBaR');
138+
t.is(camelCase('FOÈ-BAR', {preserveConsecutiveUppercase: true}), 'FOÈBAR');
139+
t.is(camelCase(['foo', 'BAR'], {preserveConsecutiveUppercase: true}), 'fooBAR');
140+
t.is(camelCase(['foo', '-BAR'], {preserveConsecutiveUppercase: true}), 'fooBAR');
141+
t.is(camelCase(['foo', '-BAR', 'baz'], {preserveConsecutiveUppercase: true}), 'fooBARBaz');
142+
t.is(camelCase(['', ''], {preserveConsecutiveUppercase: true}), '');
143+
t.is(camelCase('--', {preserveConsecutiveUppercase: true}), '');
144+
t.is(camelCase('', {preserveConsecutiveUppercase: true}), '');
145+
t.is(camelCase('--__--_--_', {preserveConsecutiveUppercase: true}), '');
146+
t.is(camelCase(['---_', '--', '', '-_- '], {preserveConsecutiveUppercase: true}), '');
147+
t.is(camelCase('foo BAR?', {preserveConsecutiveUppercase: true}), 'fooBAR?');
148+
t.is(camelCase('foo BAR!', {preserveConsecutiveUppercase: true}), 'fooBAR!');
149+
t.is(camelCase('foo BAR$', {preserveConsecutiveUppercase: true}), 'fooBAR$');
150+
t.is(camelCase('foo-BAR#', {preserveConsecutiveUppercase: true}), 'fooBAR#');
151+
t.is(camelCase('XMLHttpRequest', {preserveConsecutiveUppercase: true}), 'XMLHttpRequest');
152+
t.is(camelCase('AjaxXMLHttpRequest', {preserveConsecutiveUppercase: true}), 'ajaxXMLHttpRequest');
153+
t.is(camelCase('Ajax-XMLHttpRequest', {preserveConsecutiveUppercase: true}), 'ajaxXMLHttpRequest');
154+
t.is(camelCase([], {preserveConsecutiveUppercase: true}), '');
155+
t.is(camelCase('mGridCOl6@md', {preserveConsecutiveUppercase: true}), 'mGridCOl6@md');
156+
t.is(camelCase('A::a', {preserveConsecutiveUppercase: true}), 'a::a');
157+
t.is(camelCase('Hello1WORLD', {preserveConsecutiveUppercase: true}), 'hello1WORLD');
158+
t.is(camelCase('Hello11WORLD', {preserveConsecutiveUppercase: true}), 'hello11WORLD');
159+
t.is(camelCase('РозовыйПушистыйFOOдинорогиf', {preserveConsecutiveUppercase: true}), 'розовыйПушистыйFOOдинорогиf');
160+
t.is(camelCase('桑德在这里。', {preserveConsecutiveUppercase: true}), '桑德在这里。');
161+
t.is(camelCase('桑德_在这里。', {preserveConsecutiveUppercase: true}), '桑德在这里。');
162+
});
163+
164+
test('camelCase with both pascalCase and preserveConsecutiveUppercase option', t => {
165+
t.is(camelCase('foo-BAR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
166+
t.is(camelCase('fooBAR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
167+
t.is(camelCase('fooBaR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBaR');
168+
t.is(camelCase('fOÈ-BAR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FOÈBAR');
169+
t.is(camelCase('--foo.BAR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
170+
t.is(camelCase(['Foo', 'BAR'], {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
171+
t.is(camelCase(['foo', '-BAR'], {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
172+
t.is(camelCase(['foo', '-BAR', 'baz'], {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBARBaz');
173+
t.is(camelCase(['', ''], {pascalCase: true, preserveConsecutiveUppercase: true}), '');
174+
t.is(camelCase('--', {pascalCase: true, preserveConsecutiveUppercase: true}), '');
175+
t.is(camelCase('', {pascalCase: true, preserveConsecutiveUppercase: true}), '');
176+
t.is(camelCase('--__--_--_', {pascalCase: true, preserveConsecutiveUppercase: true}), '');
177+
t.is(camelCase(['---_', '--', '', '-_- '], {pascalCase: true, preserveConsecutiveUppercase: true}), '');
178+
t.is(camelCase('foo BAR?', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR?');
179+
t.is(camelCase('foo BAR!', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR!');
180+
t.is(camelCase('Foo BAR$', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR$');
181+
t.is(camelCase('foo-BAR#', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR#');
182+
t.is(camelCase('xMLHttpRequest', {pascalCase: true, preserveConsecutiveUppercase: true}), 'XMLHttpRequest');
183+
t.is(camelCase('ajaxXMLHttpRequest', {pascalCase: true, preserveConsecutiveUppercase: true}), 'AjaxXMLHttpRequest');
184+
t.is(camelCase('Ajax-XMLHttpRequest', {pascalCase: true, preserveConsecutiveUppercase: true}), 'AjaxXMLHttpRequest');
185+
t.is(camelCase([], {pascalCase: true, preserveConsecutiveUppercase: true}), '');
186+
t.is(camelCase('mGridCOl6@md', {pascalCase: true, preserveConsecutiveUppercase: true}), 'MGridCOl6@md');
187+
t.is(camelCase('A::a', {pascalCase: true, preserveConsecutiveUppercase: true}), 'A::a');
188+
t.is(camelCase('Hello1WORLD', {pascalCase: true, preserveConsecutiveUppercase: true}), 'Hello1WORLD');
189+
t.is(camelCase('Hello11WORLD', {pascalCase: true, preserveConsecutiveUppercase: true}), 'Hello11WORLD');
190+
t.is(camelCase('pозовыйПушистыйFOOдинорогиf', {pascalCase: true, preserveConsecutiveUppercase: true}), 'PозовыйПушистыйFOOдинорогиf');
191+
t.is(camelCase('桑德在这里。', {pascalCase: true, preserveConsecutiveUppercase: true}), '桑德在这里。');
192+
t.is(camelCase('桑德_在这里。', {pascalCase: true, preserveConsecutiveUppercase: true}), '桑德在这里。');
193+
});
194+
133195
test('camelCase with locale option', t => {
134196
t.is(camelCase('lorem-ipsum', {locale: 'tr-TR'}), 'loremİpsum');
135197
t.is(camelCase('lorem-ipsum', {locale: 'en-EN'}), 'loremIpsum');

0 commit comments

Comments
 (0)