Skip to content

Commit 79b9a64

Browse files
committed
Preserve leading underscores and dollar signs
1 parent 7aa464f commit 79b9a64

2 files changed

Lines changed: 45 additions & 8 deletions

File tree

index.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ export default function camelCase(input, options) {
7777
return '';
7878
}
7979

80+
// Preserve leading _ and $ as they have semantic meaning
81+
const leadingPrefix = input.match(/^[_$]*/)[0];
82+
input = input.slice(leadingPrefix.length);
83+
84+
if (input.length === 0) {
85+
return leadingPrefix;
86+
}
87+
8088
const toLowerCase = options.locale === false
8189
? string => string.toLowerCase()
8290
: string => string.toLocaleLowerCase(options.locale);
@@ -87,10 +95,10 @@ export default function camelCase(input, options) {
8795

8896
if (input.length === 1) {
8997
if (SEPARATORS.test(input)) {
90-
return '';
98+
return leadingPrefix;
9199
}
92100

93-
return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
101+
return leadingPrefix + (options.pascalCase ? toUpperCase(input) : toLowerCase(input));
94102
}
95103

96104
const hasUpperCase = input !== toLowerCase(input);
@@ -106,5 +114,5 @@ export default function camelCase(input, options) {
106114
input = toUpperCase(input.charAt(0)) + input.slice(1);
107115
}
108116

109-
return postProcess(input, toUpperCase);
117+
return leadingPrefix + postProcess(input, toUpperCase);
110118
}

test.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test('camelCase', t => {
2222
t.is(camelCase('foo..bar'), 'fooBar');
2323
t.is(camelCase('..foo..bar..'), 'fooBar');
2424
t.is(camelCase('foo_bar'), 'fooBar');
25-
t.is(camelCase('__foo__bar__'), 'fooBar');
25+
t.is(camelCase('__foo__bar__'), '__fooBar');
2626
t.is(camelCase('foo bar'), 'fooBar');
2727
t.is(camelCase(' foo bar '), 'fooBar');
2828
t.is(camelCase('-'), '');
@@ -42,13 +42,13 @@ test('camelCase', t => {
4242
t.is(camelCase(['', '']), '');
4343
t.is(camelCase('--'), '');
4444
t.is(camelCase(''), '');
45-
t.is(camelCase('_'), '');
45+
t.is(camelCase('_'), '_');
4646
t.is(camelCase(' '), '');
4747
t.is(camelCase('.'), '');
4848
t.is(camelCase('..'), '');
4949
t.is(camelCase('--'), '');
5050
t.is(camelCase(' '), '');
51-
t.is(camelCase('__'), '');
51+
t.is(camelCase('__'), '__');
5252
t.is(camelCase('--__--_--_'), '');
5353
t.is(camelCase(['---_', '--', '', '-_- ']), '');
5454
t.is(camelCase('foo bar?'), 'fooBar?');
@@ -95,8 +95,7 @@ test('camelCase with pascalCase option', t => {
9595
t.is(camelCase('foo..bar', {pascalCase: true}), 'FooBar');
9696
t.is(camelCase('..foo..bar..', {pascalCase: true}), 'FooBar');
9797
t.is(camelCase('foo_bar', {pascalCase: true}), 'FooBar');
98-
t.is(camelCase('__foo__bar__', {pascalCase: true}), 'FooBar');
99-
t.is(camelCase('__foo__bar__', {pascalCase: true}), 'FooBar');
98+
t.is(camelCase('__foo__bar__', {pascalCase: true}), '__FooBar');
10099
t.is(camelCase('foo bar', {pascalCase: true}), 'FooBar');
101100
t.is(camelCase(' foo bar ', {pascalCase: true}), 'FooBar');
102101
t.is(camelCase('-', {pascalCase: true}), '');
@@ -228,6 +227,36 @@ test('camelCase with disabled locale', t => {
228227
});
229228
});
230229

230+
test('preserve leading underscores and dollar signs', t => {
231+
// Leading _ and $ have semantic meaning (private/internal, jQuery/observables)
232+
t.is(camelCase('_foo_bar'), '_fooBar');
233+
t.is(camelCase('$foo_bar'), '$fooBar');
234+
t.is(camelCase('__foo_bar'), '__fooBar');
235+
t.is(camelCase('$$foo_bar'), '$$fooBar');
236+
t.is(camelCase('$_foo_bar'), '$_fooBar');
237+
t.is(camelCase('_$foo_bar'), '_$fooBar');
238+
239+
// Edge cases: only prefix characters
240+
t.is(camelCase('_'), '_');
241+
t.is(camelCase('__'), '__');
242+
t.is(camelCase('$'), '$');
243+
t.is(camelCase('$$$'), '$$$');
244+
t.is(camelCase('_$'), '_$');
245+
246+
// With pascalCase
247+
t.is(camelCase('_foo_bar', {pascalCase: true}), '_FooBar');
248+
t.is(camelCase('$foo_bar', {pascalCase: true}), '$FooBar');
249+
t.is(camelCase('__foo_bar', {pascalCase: true}), '__FooBar');
250+
251+
// With preserveConsecutiveUppercase
252+
t.is(camelCase('_foo_BAR', {preserveConsecutiveUppercase: true}), '_fooBAR');
253+
t.is(camelCase('$foo_BAR', {preserveConsecutiveUppercase: true}), '$fooBAR');
254+
255+
// Combined with other transformations
256+
t.is(camelCase('_foo-bar_baz'), '_fooBarBaz');
257+
t.is(camelCase('$http_service'), '$httpService');
258+
});
259+
231260
test('invalid input', t => {
232261
t.throws(() => {
233262
camelCase(1);

0 commit comments

Comments
 (0)