Skip to content

Commit

Permalink
Add preserveConsecutiveUppercase option (#78)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
rocktimsaikia and sindresorhus committed Oct 28, 2020
1 parent a077c7b commit e2c4891
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 12 deletions.
19 changes: 16 additions & 3 deletions index.d.ts
Expand Up @@ -7,6 +7,13 @@ declare namespace camelcase {
*/
readonly pascalCase?: boolean;

/**
Preserve the consecutive uppercase characters: `foo-BAR` → `FooBAR`.
@default false
*/
readonly preserveConsecutiveUppercase?: boolean;

/**
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.
Expand All @@ -18,13 +25,10 @@ declare namespace camelcase {
camelCase('lorem-ipsum', {locale: 'en-US'});
//=> 'loremIpsum'
camelCase('lorem-ipsum', {locale: 'tr-TR'});
//=> 'loremİpsum'
camelCase('lorem-ipsum', {locale: ['en-US', 'en-GB']});
//=> 'loremIpsum'
camelCase('lorem-ipsum', {locale: ['tr', 'TR', 'tr-TR']});
//=> 'loremİpsum'
```
Expand Down Expand Up @@ -62,6 +66,12 @@ camelCase('Foo-Bar', {pascalCase: true});
camelCase('--foo.bar', {pascalCase: false});
//=> 'fooBar'
camelCase('Foo-BAR', {preserveConsecutiveUppercase: true});
//=> 'fooBAR'
camelCase('fooBAR', {pascalCase: true, preserveConsecutiveUppercase: true}));
//=> 'FooBAR'
camelCase('foo bar');
//=> 'fooBar'
Expand All @@ -76,6 +86,9 @@ camelCase(['foo', 'bar']);
camelCase(['__foo__', '--bar'], {pascalCase: true});
//=> 'FooBar'
camelCase(['foo', 'BAR'], {pascalCase: true, preserveConsecutiveUppercase: true})
//=> 'FooBAR'
camelCase('lorem-ipsum', {locale: 'en-US'});
//=> 'loremIpsum'
```
Expand Down
32 changes: 23 additions & 9 deletions index.js
Expand Up @@ -29,18 +29,26 @@ const preserveCamelCase = (string, locale) => {
return string;
};

const preserveConsecutiveUppercase = input => {
return input.replace(/^[\p{Lu}](?![\p{Lu}])/gu, m1 => m1.toLowerCase());
};

const postProcess = (input, options) => {
return input.replace(/[_.\- ]+([\p{Alpha}\p{N}_]|$)/gu, (_, p1) => p1.toLocaleUpperCase(options.locale))
.replace(/\d+([\p{Alpha}\p{N}_]|$)/gu, m => m.toLocaleUpperCase(options.locale));
};

const camelCase = (input, options) => {
if (!(typeof input === 'string' || Array.isArray(input))) {
throw new TypeError('Expected the input to be `string | string[]`');
}

options = {
...{pascalCase: false},
pascalCase: false,
preserveConsecutiveUppercase: false,
...options
};

const postProcess = x => options.pascalCase ? x.charAt(0).toLocaleUpperCase(options.locale) + x.slice(1) : x;

if (Array.isArray(input)) {
input = input.map(x => x.trim())
.filter(x => x.length)
Expand All @@ -63,13 +71,19 @@ const camelCase = (input, options) => {
input = preserveCamelCase(input, options.locale);
}

input = input
.replace(/^[_.\- ]+/, '')
.toLocaleLowerCase(options.locale)
.replace(/[_.\- ]+([\p{Alpha}\p{N}_]|$)/gu, (_, p1) => p1.toLocaleUpperCase(options.locale))
.replace(/\d+([\p{Alpha}\p{N}_]|$)/gu, m => m.toLocaleUpperCase(options.locale));
input = input.replace(/^[_.\- ]+/, '');

if (options.preserveConsecutiveUppercase) {
input = preserveConsecutiveUppercase(input);
} else {
input = input.toLocaleLowerCase();
}

if (options.pascalCase) {
input = input.charAt(0).toLocaleUpperCase(options.locale) + input.slice(1);
}

return postProcess(input);
return postProcess(input, options);
};

module.exports = camelCase;
Expand Down
16 changes: 16 additions & 0 deletions readme.md
Expand Up @@ -35,6 +35,12 @@ camelCase('Foo-Bar', {pascalCase: true});
camelCase('--foo.bar', {pascalCase: false});
//=> 'fooBar'

camelCase('Foo-BAR', {preserveConsecutiveUppercase: true});
//=> 'fooBAR'

camelCase('fooBAR', {pascalCase: true, preserveConsecutiveUppercase: true}));
//=> 'FooBAR'

camelCase('foo bar');
//=> 'fooBar'

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

camelCase(['foo', 'BAR'], {pascalCase: true, preserveConsecutiveUppercase: true})
//=> 'FooBAR'

camelCase('lorem-ipsum', {locale: 'en-US'});
//=> 'loremIpsum'
```
Expand All @@ -74,6 +83,13 @@ Default: `false`

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

##### preserveConsecutiveUppercase

Type: `boolean`\
Default: `false`

Preserve the consecutive uppercase characters: `foo-BAR``FooBAR`.

##### locale

Type: `string | string[]`\
Expand Down
62 changes: 62 additions & 0 deletions test.js
Expand Up @@ -130,6 +130,68 @@ test('camelCase with pascalCase option', t => {
t.is(camelCase('桑德_在这里。', {pascalCase: true}), '桑德在这里。');
});

test('camelCase with preserveConsecutiveUppercase option', t => {
t.is(camelCase('foo-BAR', {preserveConsecutiveUppercase: true}), 'fooBAR');
t.is(camelCase('Foo-BAR', {preserveConsecutiveUppercase: true}), 'fooBAR');
t.is(camelCase('fooBAR', {preserveConsecutiveUppercase: true}), 'fooBAR');
t.is(camelCase('fooBaR', {preserveConsecutiveUppercase: true}), 'fooBaR');
t.is(camelCase('FOÈ-BAR', {preserveConsecutiveUppercase: true}), 'FOÈBAR');
t.is(camelCase(['foo', 'BAR'], {preserveConsecutiveUppercase: true}), 'fooBAR');
t.is(camelCase(['foo', '-BAR'], {preserveConsecutiveUppercase: true}), 'fooBAR');
t.is(camelCase(['foo', '-BAR', 'baz'], {preserveConsecutiveUppercase: true}), 'fooBARBaz');
t.is(camelCase(['', ''], {preserveConsecutiveUppercase: true}), '');
t.is(camelCase('--', {preserveConsecutiveUppercase: true}), '');
t.is(camelCase('', {preserveConsecutiveUppercase: true}), '');
t.is(camelCase('--__--_--_', {preserveConsecutiveUppercase: true}), '');
t.is(camelCase(['---_', '--', '', '-_- '], {preserveConsecutiveUppercase: true}), '');
t.is(camelCase('foo BAR?', {preserveConsecutiveUppercase: true}), 'fooBAR?');
t.is(camelCase('foo BAR!', {preserveConsecutiveUppercase: true}), 'fooBAR!');
t.is(camelCase('foo BAR$', {preserveConsecutiveUppercase: true}), 'fooBAR$');
t.is(camelCase('foo-BAR#', {preserveConsecutiveUppercase: true}), 'fooBAR#');
t.is(camelCase('XMLHttpRequest', {preserveConsecutiveUppercase: true}), 'XMLHttpRequest');
t.is(camelCase('AjaxXMLHttpRequest', {preserveConsecutiveUppercase: true}), 'ajaxXMLHttpRequest');
t.is(camelCase('Ajax-XMLHttpRequest', {preserveConsecutiveUppercase: true}), 'ajaxXMLHttpRequest');
t.is(camelCase([], {preserveConsecutiveUppercase: true}), '');
t.is(camelCase('mGridCOl6@md', {preserveConsecutiveUppercase: true}), 'mGridCOl6@md');
t.is(camelCase('A::a', {preserveConsecutiveUppercase: true}), 'a::a');
t.is(camelCase('Hello1WORLD', {preserveConsecutiveUppercase: true}), 'hello1WORLD');
t.is(camelCase('Hello11WORLD', {preserveConsecutiveUppercase: true}), 'hello11WORLD');
t.is(camelCase('РозовыйПушистыйFOOдинорогиf', {preserveConsecutiveUppercase: true}), 'розовыйПушистыйFOOдинорогиf');
t.is(camelCase('桑德在这里。', {preserveConsecutiveUppercase: true}), '桑德在这里。');
t.is(camelCase('桑德_在这里。', {preserveConsecutiveUppercase: true}), '桑德在这里。');
});

test('camelCase with both pascalCase and preserveConsecutiveUppercase option', t => {
t.is(camelCase('foo-BAR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
t.is(camelCase('fooBAR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
t.is(camelCase('fooBaR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBaR');
t.is(camelCase('fOÈ-BAR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FOÈBAR');
t.is(camelCase('--foo.BAR', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
t.is(camelCase(['Foo', 'BAR'], {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
t.is(camelCase(['foo', '-BAR'], {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR');
t.is(camelCase(['foo', '-BAR', 'baz'], {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBARBaz');
t.is(camelCase(['', ''], {pascalCase: true, preserveConsecutiveUppercase: true}), '');
t.is(camelCase('--', {pascalCase: true, preserveConsecutiveUppercase: true}), '');
t.is(camelCase('', {pascalCase: true, preserveConsecutiveUppercase: true}), '');
t.is(camelCase('--__--_--_', {pascalCase: true, preserveConsecutiveUppercase: true}), '');
t.is(camelCase(['---_', '--', '', '-_- '], {pascalCase: true, preserveConsecutiveUppercase: true}), '');
t.is(camelCase('foo BAR?', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR?');
t.is(camelCase('foo BAR!', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR!');
t.is(camelCase('Foo BAR$', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR$');
t.is(camelCase('foo-BAR#', {pascalCase: true, preserveConsecutiveUppercase: true}), 'FooBAR#');
t.is(camelCase('xMLHttpRequest', {pascalCase: true, preserveConsecutiveUppercase: true}), 'XMLHttpRequest');
t.is(camelCase('ajaxXMLHttpRequest', {pascalCase: true, preserveConsecutiveUppercase: true}), 'AjaxXMLHttpRequest');
t.is(camelCase('Ajax-XMLHttpRequest', {pascalCase: true, preserveConsecutiveUppercase: true}), 'AjaxXMLHttpRequest');
t.is(camelCase([], {pascalCase: true, preserveConsecutiveUppercase: true}), '');
t.is(camelCase('mGridCOl6@md', {pascalCase: true, preserveConsecutiveUppercase: true}), 'MGridCOl6@md');
t.is(camelCase('A::a', {pascalCase: true, preserveConsecutiveUppercase: true}), 'A::a');
t.is(camelCase('Hello1WORLD', {pascalCase: true, preserveConsecutiveUppercase: true}), 'Hello1WORLD');
t.is(camelCase('Hello11WORLD', {pascalCase: true, preserveConsecutiveUppercase: true}), 'Hello11WORLD');
t.is(camelCase('pозовыйПушистыйFOOдинорогиf', {pascalCase: true, preserveConsecutiveUppercase: true}), 'PозовыйПушистыйFOOдинорогиf');
t.is(camelCase('桑德在这里。', {pascalCase: true, preserveConsecutiveUppercase: true}), '桑德在这里。');
t.is(camelCase('桑德_在这里。', {pascalCase: true, preserveConsecutiveUppercase: true}), '桑德在这里。');
});

test('camelCase with locale option', t => {
t.is(camelCase('lorem-ipsum', {locale: 'tr-TR'}), 'loremİpsum');
t.is(camelCase('lorem-ipsum', {locale: 'en-EN'}), 'loremIpsum');
Expand Down

0 comments on commit e2c4891

Please sign in to comment.