Repository: sindresorhus/camelcase (js)

Issue:
Converting either volume_3d or volume3d results in volume3D making it impossible to decamelize
How to reproduce:
```
console.log(camelcase('volume_3d')); // volume3D
console.log(camelcase('volume3d')); // volume3D
```

this operates under the assumption that decamelizing should return the string to its original decamelized version but I'm not sure how that would be possible for strings in this format.

Pull request:
Removing casing to characters after a number that are followed by a separator (issue #77)
### In this PR
Based on issue #77 for the following [comment](https://github.com/sindresorhus/camelcase/issues/77#issuecomment-1025489619)

The following example the returns the expected:
Expected: b2b_registration_request -> b2bRegistrationRequest
Received: b2b_registration_request -> b2BRegistrationRequest

Assumptions:
  - If a letter proceeded by a number is followed by an underscore or dash, the letter should not be upper cased

## index.js
  - Reordered the replace functions to be able to check for separators
  - Added ternary function to check if the character after the replace value is a underscore or dash and only changing case if it is not
## test.js
  - Added 4 tests for the example string

Fixes #77

Patch:
diff --git a/index.js b/index.js
index 6d80316..2be1bd1 100644
--- a/index.js
+++ b/index.js
@@ -49,8 +49,8 @@ const postProcess = (input, toUpperCase) => {
 	SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
 	NUMBERS_AND_IDENTIFIER.lastIndex = 0;
 
-	return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier))
-		.replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m));
+	return input.replace(NUMBERS_AND_IDENTIFIER, (match, pattern, offset) => ['_', '-'].includes(input.charAt(offset + match.length)) ? match : toUpperCase(match))
+		.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier));
 };
 
 export default function camelCase(input, options) {


Test patch:
diff --git a/test.js b/test.js
index 306b437..4a013f8 100644
--- a/test.js
+++ b/test.js
@@ -2,6 +2,9 @@ import test from 'ava';
 import camelCase from './index.js';
 
 test('camelCase', t => {
+	t.is(camelCase('b2b_registration_request'), 'b2bRegistrationRequest');
+	t.is(camelCase('b2b-registration-request'), 'b2bRegistrationRequest');
+	t.is(camelCase('b2b_registration_b2b_request'), 'b2bRegistrationB2bRequest');
 	t.is(camelCase('foo'), 'foo');
 	t.is(camelCase('IDs'), 'ids');
 	t.is(camelCase('FooIDs'), 'fooIds');
@@ -76,6 +79,7 @@ test('camelCase', t => {
 });
 
 test('camelCase with pascalCase option', t => {
+	t.is(camelCase('b2b_registration_request', {pascalCase: true}), 'B2bRegistrationRequest');
 	t.is(camelCase('foo', {pascalCase: true}), 'Foo');
 	t.is(camelCase('foo-bar', {pascalCase: true}), 'FooBar');
 	t.is(camelCase('foo-bar-baz', {pascalCase: true}), 'FooBarBaz');
@@ -138,6 +142,7 @@ test('camelCase with pascalCase option', t => {
 	t.is(camelCase('РОЗОВЫЙ_ПУШИСТЫЙ-ЕДИНОРОГИ', {pascalCase: true}), 'РозовыйПушистыйЕдинороги');
 	t.is(camelCase('桑德在这里。', {pascalCase: true}), '桑德在这里。');
 	t.is(camelCase('桑德_在这里。', {pascalCase: true}), '桑德在这里。');
+	t.is(camelCase('a1b', {pascalCase: true}), 'A1B');
 });
 
 test('camelCase with preserveConsecutiveUppercase option', t => {