Skip to content

Commit

Permalink
Add option to disable the platform locale (#92)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
jhnns and sindresorhus committed Jan 1, 2022
1 parent f28c463 commit 65ccea6
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 17 deletions.
4 changes: 3 additions & 1 deletion index.d.ts
Expand Up @@ -17,6 +17,8 @@ declare namespace camelcase {
/**
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.
Setting `locale: false` ignores the platform locale and uses the [Unicode Default Case Conversion](https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#simple-single-character-case-mapping) algorithm.
Default: The host environment’s current locale.
@example
Expand All @@ -33,7 +35,7 @@ declare namespace camelcase {
//=> 'loremİpsum'
```
*/
readonly locale?: string | readonly string[];
readonly locale?: false | string | readonly string[];
}
}

Expand Down
37 changes: 22 additions & 15 deletions index.js
Expand Up @@ -10,7 +10,7 @@ const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');

const preserveCamelCase = (string, locale) => {
const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
let isLastCharLower = false;
let isLastCharUpper = false;
let isLastLastCharUpper = false;
Expand All @@ -30,27 +30,27 @@ const preserveCamelCase = (string, locale) => {
isLastCharUpper = false;
isLastCharLower = true;
} else {
isLastCharLower = character.toLocaleLowerCase(locale) === character && character.toLocaleUpperCase(locale) !== character;
isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = character.toLocaleUpperCase(locale) === character && character.toLocaleLowerCase(locale) !== character;
isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
}
}

return string;
};

const preserveConsecutiveUppercase = input => {
const preserveConsecutiveUppercase = (input, toLowerCase) => {
LEADING_CAPITAL.lastIndex = 0;

return input.replace(LEADING_CAPITAL, m1 => m1.toLowerCase());
return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1));
};

const postProcess = (input, options) => {
const postProcess = (input, toUpperCase) => {
SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
NUMBERS_AND_IDENTIFIER.lastIndex = 0;

return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => identifier.toLocaleUpperCase(options.locale))
.replace(NUMBERS_AND_IDENTIFIER, m => m.toLocaleUpperCase(options.locale));
return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier))
.replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m));
};

const camelCase = (input, options) => {
Expand All @@ -76,29 +76,36 @@ const camelCase = (input, options) => {
return '';
}

const toLowerCase = options.locale === false ?
string => string.toLowerCase() :
string => string.toLocaleLowerCase(options.locale);
const toUpperCase = options.locale === false ?
string => string.toUpperCase() :
string => string.toLocaleUpperCase(options.locale);

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

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

if (hasUpperCase) {
input = preserveCamelCase(input, options.locale);
input = preserveCamelCase(input, toLowerCase, toUpperCase);
}

input = input.replace(LEADING_SEPARATORS, '');

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

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

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

module.exports = camelCase;
Expand Down
16 changes: 15 additions & 1 deletion readme.md
Expand Up @@ -94,7 +94,7 @@ Preserve the consecutive uppercase characters: `foo-BAR` → `FooBAR`.

##### locale

Type: `string | string[]`\
Type: `false | string | string[]`\
Default: The host environment’s current locale.

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 @@ -115,6 +115,20 @@ camelCase('lorem-ipsum', {locale: ['tr', 'TR', 'tr-TR']});
//=> 'loremİpsum'
```

Setting `locale: false` ignores the platform locale and uses the [Unicode Default Case Conversion](https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#simple-single-character-case-mapping) algorithm:

```js
const camelCase = require('camelcase');

// On a platform with 'tr-TR'

camelCase('lorem-ipsum');
//=> 'loremİpsum'

camelCase('lorem-ipsum', {locale: false});
//=> 'loremIpsum'
```

## camelcase for enterprise

Available as part of the Tidelift Subscription.
Expand Down
35 changes: 35 additions & 0 deletions test.js
Expand Up @@ -204,8 +204,43 @@ test('camelCase with locale option', t => {
t.is(camelCase('ipsum-dolor', {pascalCase: true, locale: ['en-EN', 'en-GB']}), 'IpsumDolor');
});

test('camelCase with disabled locale', t => {
withLocaleCaseFunctionsMocked(() => {
t.is(camelCase('lorem-ipsum', {locale: false}), 'loremIpsum');
t.is(camelCase('ipsum-dolor', {pascalCase: true, locale: false}), 'IpsumDolor');
t.is(camelCase('ipsum-DOLOR', {pascalCase: true, locale: false, preserveConsecutiveUppercase: true}), 'IpsumDOLOR');
});
});

test('invalid input', t => {
t.throws(() => {
camelCase(1);
}, /Expected the input to be/);
});

/* eslint-disable no-extend-native */
const withLocaleCaseFunctionsMocked = fn => {
const throwWhenBeingCalled = () => {
throw new Error('Should not be called');
};

const toLocaleUpperCase = Object.getOwnPropertyDescriptor(String.prototype, 'toLocaleUpperCase');
const toLocaleLowerCase = Object.getOwnPropertyDescriptor(String.prototype, 'toLocaleLowerCase');

Object.defineProperty(String.prototype, 'toLocaleUpperCase', {
...toLocaleUpperCase,
value: throwWhenBeingCalled
});
Object.defineProperty(String.prototype, 'toLocaleLowerCase', {
...toLocaleLowerCase,
value: throwWhenBeingCalled
});

try {
fn();
} finally {
Object.defineProperty(String.prototype, 'toLocaleUpperCase', toLocaleUpperCase);
Object.defineProperty(String.prototype, 'toLocaleLowerCase', toLocaleLowerCase);
}
};
/* eslint-enable no-extend-native */

0 comments on commit 65ccea6

Please sign in to comment.