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

Handle Unicode character casing and require Node.js 10 #62

Merged
merged 9 commits into from Apr 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions .travis.yml
Expand Up @@ -2,5 +2,3 @@ language: node_js
node_js:
- '12'
- '10'
- '8'
- '6'
5 changes: 5 additions & 0 deletions index.d.ts
Expand Up @@ -13,6 +13,8 @@ declare const camelcase: {
/**
Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`.

Correctly handles Unicode strings.

@param input - String to convert to camel case.

@example
Expand All @@ -27,6 +29,9 @@ declare const camelcase: {

camelCase('Foo-Bar');
//=> 'fooBar'

camelCase('розовый_пушистый_единороги');
//=> 'розовыйПушистыйЕдинороги'

camelCase('Foo-Bar', {pascalCase: true});
//=> 'FooBar'
Expand Down
27 changes: 14 additions & 13 deletions index.js
Expand Up @@ -8,21 +8,21 @@ const preserveCamelCase = string => {
for (let i = 0; i < string.length; i++) {
const character = string[i];

if (isLastCharLower && /[a-zA-Z]/.test(character) && character.toUpperCase() === character) {
if (isLastCharLower && /[\p{Lu}]/u.test(character)) {
string = string.slice(0, i) + '-' + string.slice(i);
isLastCharLower = false;
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = true;
i++;
} else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(character) && character.toLowerCase() === character) {
} else if (isLastCharUpper && isLastLastCharUpper && /[\p{Ll}]/u.test(character)) {
string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = false;
isLastCharLower = true;
} else {
isLastCharLower = character.toLowerCase() === character && character.toUpperCase() !== character;
isLastCharLower = character.toLocaleLowerCase() === character && character.toLocaleUpperCase() !== character;
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = character.toUpperCase() === character && character.toLowerCase() !== character;
isLastCharUpper = character.toLocaleUpperCase() === character && character.toLocaleLowerCase() !== character;
}
}

Expand All @@ -34,11 +34,12 @@ const camelCase = (input, options) => {
throw new TypeError('Expected the input to be `string | string[]`');
}

options = Object.assign({
pascalCase: false
}, options);
options = {
...{pascalCase: false},
...options
};

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

if (Array.isArray(input)) {
input = input.map(x => x.trim())
Expand All @@ -53,20 +54,20 @@ const camelCase = (input, options) => {
}

if (input.length === 1) {
return options.pascalCase ? input.toUpperCase() : input.toLowerCase();
return options.pascalCase ? input.toLocaleUpperCase() : input.toLocaleLowerCase();
}

const hasUpperCase = input !== input.toLowerCase();
const hasUpperCase = input !== input.toLocaleLowerCase();

if (hasUpperCase) {
input = preserveCamelCase(input);
}

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

return postProcess(input);
};
Expand Down
1 change: 1 addition & 0 deletions index.test-d.ts
Expand Up @@ -2,6 +2,7 @@ import {expectType} from 'tsd';
import camelCase = require('.');

expectType<string>(camelCase('foo-bar'));
expectType<string>(camelCase('розовый_пушистый_единороги'));
expectType<string>(camelCase('Foo-Bar', {pascalCase: true}));
expectType<string>(camelCase(['foo', 'bar']));
expectType<string>(camelCase(['__foo__', '--bar'], {pascalCase: true}));
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -10,7 +10,7 @@
"url": "sindresorhus.com"
},
"engines": {
"node": ">=6"
"node": ">=10"
},
"scripts": {
"test": "xo && ava && tsd"
Expand Down
5 changes: 5 additions & 0 deletions readme.md
Expand Up @@ -2,6 +2,8 @@

> Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`

Correctly handles Unicode strings.


## Install

Expand All @@ -24,6 +26,9 @@ camelCase('foo_bar');
camelCase('Foo-Bar');
//=> 'fooBar'

camelCase('розовый_пушистый_единороги');
//=> 'розовыйПушистыйЕдинороги'

camelCase('Foo-Bar', {pascalCase: true});
//=> 'FooBar'

Expand Down
11 changes: 11 additions & 0 deletions test.js
Expand Up @@ -58,6 +58,12 @@ test('camelCase', t => {
t.is(camelCase('1Hello'), '1Hello');
t.is(camelCase('1hello'), '1Hello');
t.is(camelCase('h2w'), 'h2W');
t.is(camelCase('розовый_пушистый-единороги'), 'розовыйПушистыйЕдинороги');
t.is(camelCase('розовый_пушистый-единороги'), 'розовыйПушистыйЕдинороги');
t.is(camelCase('РОЗОВЫЙ_ПУШИСТЫЙ-ЕДИНОРОГИ'), 'розовыйПушистыйЕдинороги');
t.is(camelCase('桑德在这里。'), '桑德在这里。');
t.is(camelCase('桑德在这里。'), '桑德在这里。');
t.is(camelCase('桑德_在这里。'), '桑德在这里。');
});

test('camelCase with pascalCase option', t => {
Expand Down Expand Up @@ -117,6 +123,11 @@ test('camelCase with pascalCase option', t => {
t.is(camelCase('1hello', {pascalCase: true}), '1Hello');
t.is(camelCase('1Hello', {pascalCase: true}), '1Hello');
t.is(camelCase('h1W', {pascalCase: true}), 'H1W');
t.is(camelCase('РозовыйПушистыйЕдинороги', {pascalCase: true}), 'РозовыйПушистыйЕдинороги');
t.is(camelCase('розовый_пушистый-единороги', {pascalCase: true}), 'РозовыйПушистыйЕдинороги');
t.is(camelCase('РОЗОВЫЙ_ПУШИСТЫЙ-ЕДИНОРОГИ', {pascalCase: true}), 'РозовыйПушистыйЕдинороги');
t.is(camelCase('桑德在这里。', {pascalCase: true}), '桑德在这里。');
t.is(camelCase('桑德_在这里。', {pascalCase: true}), '桑德在这里。');
});

test('invalid input', t => {
Expand Down