Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preserveConsecutiveUppercase option #78

Merged
merged 11 commits into from Oct 28, 2020
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 {

rocktimsaikia marked this conversation as resolved.
Show resolved Hide resolved
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