Skip to content

Commit b0dba5f

Browse files
authored
Merge pull request #72 from constructive-io/devin/1774491087-consolidate-camelize-api
refactor(inflekt): remove camelize, standardize on toCamelCase/toPascalCase
2 parents d45378f + c161215 commit b0dba5f

5 files changed

Lines changed: 108 additions & 71 deletions

File tree

packages/inflekt/README.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ import {
3434
distinctPluralize,
3535
lcFirst,
3636
ucFirst,
37-
camelize,
37+
toCamelCase,
38+
toPascalCase,
39+
toScreamingSnake,
3840
underscore,
3941
toFieldName,
4042
toQueryName,
@@ -58,9 +60,12 @@ pluralizeLast('UserProfile'); // 'UserProfiles'
5860
lcFirst('UserProfile'); // 'userProfile'
5961
ucFirst('userProfile'); // 'UserProfile'
6062

61-
// Snake case / camel case conversions
62-
camelize('user_profile'); // 'UserProfile'
63-
camelize('user_profile', true); // 'userProfile'
63+
// Case conversions (handles underscores, hyphens, and camelCase boundaries)
64+
toCamelCase('user_profile'); // 'userProfile'
65+
toCamelCase('user-profile'); // 'userProfile'
66+
toPascalCase('user_profile'); // 'UserProfile'
67+
toPascalCase('user-profile'); // 'UserProfile'
68+
toScreamingSnake('userProfile'); // 'USER_PROFILE'
6469
underscore('UserProfile'); // 'user_profile'
6570

6671
// GraphQL naming helpers
@@ -72,7 +77,7 @@ const apiResponse = {
7277
user_name: 'John',
7378
order_items: [{ item_id: 1, product_name: 'Widget' }]
7479
};
75-
inflektTree(apiResponse, (key) => camelize(key, true));
80+
inflektTree(apiResponse, toCamelCase);
7681
// Result: { userName: 'John', orderItems: [{ itemId: 1, productName: 'Widget' }] }
7782
```
7883

@@ -91,7 +96,9 @@ inflektTree(apiResponse, (key) => camelize(key, true));
9196

9297
- `lcFirst(str)` - Convert first character to lowercase (PascalCase to camelCase)
9398
- `ucFirst(str)` - Convert first character to uppercase (camelCase to PascalCase)
94-
- `camelize(str, lowFirstLetter?)` - Convert snake_case to PascalCase (or camelCase if lowFirstLetter is true)
99+
- `toCamelCase(str)` - Convert to camelCase (handles underscores, hyphens, and PascalCase input)
100+
- `toPascalCase(str)` - Convert to PascalCase (handles underscores, hyphens, and camelCase input)
101+
- `toScreamingSnake(str)` - Convert to SCREAMING_SNAKE_CASE
95102
- `underscore(str)` - Convert PascalCase/camelCase to snake_case
96103
- `fixCapitalisedPlural(str)` - Fix capitalized S after numbers (e.g., `Table1S` -> `Table1s`)
97104

@@ -124,7 +131,7 @@ const apiResponse = {
124131
]
125132
};
126133

127-
const result = inflektTree(apiResponse, (key) => camelize(key, true));
134+
const result = inflektTree(apiResponse, toCamelCase);
128135
// Result:
129136
// {
130137
// userName: 'John',
@@ -156,18 +163,18 @@ const input = {
156163
_metadata: { _internal: true }
157164
};
158165

159-
const result = inflektTree(input, (key) => camelize(key, true), {
166+
const result = inflektTree(input, toCamelCase, {
160167
skip: (key) => key.startsWith('_')
161168
});
162169
// Result: { userName: 'John', _private_field: 'secret', _metadata: { _internal: true } }
163170

164171
// Skip specific keys
165-
const result2 = inflektTree(input, (key) => camelize(key, true), {
172+
const result2 = inflektTree(input, toCamelCase, {
166173
skip: (key) => key === 'created_at' || key === 'updated_at'
167174
});
168175

169176
// Skip based on path depth (only transform top 2 levels)
170-
const result3 = inflektTree(deepObject, (key) => camelize(key, true), {
177+
const result3 = inflektTree(deepObject, toCamelCase, {
171178
skip: (key, path) => path.length > 1
172179
});
173180
```

packages/inflekt/__tests__/inflection.test.ts

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {
77
distinctPluralizeLast,
88
lcFirst,
99
ucFirst,
10-
camelize,
10+
toCamelCase,
11+
toPascalCase,
12+
toScreamingSnake,
1113
underscore,
1214
fixCapitalisedPlural,
1315
toFieldName,
@@ -188,22 +190,68 @@ describe('ucFirst', () => {
188190
});
189191
});
190192

191-
describe('camelize', () => {
192-
it('should convert snake_case to PascalCase by default', () => {
193-
expect(camelize('user_profile')).toBe('UserProfile');
194-
expect(camelize('order_item')).toBe('OrderItem');
195-
expect(camelize('api_schema')).toBe('ApiSchema');
193+
describe('toCamelCase', () => {
194+
it('should convert snake_case to camelCase', () => {
195+
expect(toCamelCase('user_profile')).toBe('userProfile');
196+
expect(toCamelCase('order_item')).toBe('orderItem');
197+
expect(toCamelCase('api_schema')).toBe('apiSchema');
196198
});
197199

198-
it('should convert snake_case to camelCase when lowFirstLetter is true', () => {
199-
expect(camelize('user_profile', true)).toBe('userProfile');
200-
expect(camelize('order_item', true)).toBe('orderItem');
201-
expect(camelize('api_schema', true)).toBe('apiSchema');
200+
it('should convert hyphenated strings to camelCase', () => {
201+
expect(toCamelCase('user-profile')).toBe('userProfile');
202+
expect(toCamelCase('order-item')).toBe('orderItem');
203+
expect(toCamelCase('my-component')).toBe('myComponent');
204+
});
205+
206+
it('should convert PascalCase to camelCase', () => {
207+
expect(toCamelCase('UserProfile')).toBe('userProfile');
208+
expect(toCamelCase('HelloWorld')).toBe('helloWorld');
209+
});
210+
211+
it('should handle single words', () => {
212+
expect(toCamelCase('user')).toBe('user');
213+
expect(toCamelCase('User')).toBe('user');
214+
});
215+
});
216+
217+
describe('toPascalCase', () => {
218+
it('should convert snake_case to PascalCase', () => {
219+
expect(toPascalCase('user_profile')).toBe('UserProfile');
220+
expect(toPascalCase('order_item')).toBe('OrderItem');
221+
expect(toPascalCase('api_schema')).toBe('ApiSchema');
222+
});
223+
224+
it('should convert hyphenated strings to PascalCase', () => {
225+
expect(toPascalCase('user-profile')).toBe('UserProfile');
226+
expect(toPascalCase('order-item')).toBe('OrderItem');
227+
expect(toPascalCase('my-component')).toBe('MyComponent');
228+
});
229+
230+
it('should convert camelCase to PascalCase', () => {
231+
expect(toPascalCase('userProfile')).toBe('UserProfile');
232+
expect(toPascalCase('helloWorld')).toBe('HelloWorld');
233+
});
234+
235+
it('should handle single words', () => {
236+
expect(toPascalCase('user')).toBe('User');
237+
expect(toPascalCase('User')).toBe('User');
238+
});
239+
});
240+
241+
describe('toScreamingSnake', () => {
242+
it('should convert camelCase to SCREAMING_SNAKE_CASE', () => {
243+
expect(toScreamingSnake('userProfile')).toBe('USER_PROFILE');
244+
expect(toScreamingSnake('orderItem')).toBe('ORDER_ITEM');
245+
});
246+
247+
it('should convert PascalCase to SCREAMING_SNAKE_CASE', () => {
248+
expect(toScreamingSnake('UserProfile')).toBe('USER_PROFILE');
249+
expect(toScreamingSnake('OrderItem')).toBe('ORDER_ITEM');
202250
});
203251

204252
it('should handle single words', () => {
205-
expect(camelize('user')).toBe('User');
206-
expect(camelize('user', true)).toBe('user');
253+
expect(toScreamingSnake('user')).toBe('USER');
254+
expect(toScreamingSnake('User')).toBe('USER');
207255
});
208256
});
209257

packages/inflekt/__tests__/transform-keys.test.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { inflektTree, camelize, underscore } from '../src';
1+
import { inflektTree, toCamelCase, underscore } from '../src';
22

33
describe('inflektTree', () => {
44
describe('basic key transformation', () => {
55
it('should transform flat object keys from snake_case to camelCase', () => {
66
const input = { user_name: 'John', user_age: 30 };
7-
const result = inflektTree(input, (key) => camelize(key, true));
7+
const result = inflektTree(input, toCamelCase);
88
expect(result).toEqual({ userName: 'John', userAge: 30 });
99
});
1010

@@ -15,7 +15,7 @@ describe('inflektTree', () => {
1515
});
1616

1717
it('should handle empty objects', () => {
18-
const result = inflektTree({}, (key) => camelize(key, true));
18+
const result = inflektTree({}, toCamelCase);
1919
expect(result).toEqual({});
2020
});
2121
});
@@ -29,7 +29,7 @@ describe('inflektTree', () => {
2929
profile_bio: 'bio text',
3030
},
3131
};
32-
const result = inflektTree(input, (key) => camelize(key, true));
32+
const result = inflektTree(input, toCamelCase);
3333
expect(result).toEqual({
3434
userName: 'John',
3535
userProfile: {
@@ -49,7 +49,7 @@ describe('inflektTree', () => {
4949
},
5050
},
5151
};
52-
const result = inflektTree(input, (key) => camelize(key, true));
52+
const result = inflektTree(input, toCamelCase);
5353
expect(result).toEqual({
5454
levelOne: {
5555
levelTwo: {
@@ -70,7 +70,7 @@ describe('inflektTree', () => {
7070
{ item_id: 2, item_name: 'Product B' },
7171
],
7272
};
73-
const result = inflektTree(input, (key) => camelize(key, true));
73+
const result = inflektTree(input, toCamelCase);
7474
expect(result).toEqual({
7575
orderItems: [
7676
{ itemId: 1, itemName: 'Product A' },
@@ -81,7 +81,7 @@ describe('inflektTree', () => {
8181

8282
it('should handle arrays of primitives', () => {
8383
const input = { user_tags: ['tag1', 'tag2', 'tag3'] };
84-
const result = inflektTree(input, (key) => camelize(key, true));
84+
const result = inflektTree(input, toCamelCase);
8585
expect(result).toEqual({ userTags: ['tag1', 'tag2', 'tag3'] });
8686
});
8787

@@ -92,7 +92,7 @@ describe('inflektTree', () => {
9292
[{ cell_value: 3 }, { cell_value: 4 }],
9393
],
9494
};
95-
const result = inflektTree(input, (key) => camelize(key, true));
95+
const result = inflektTree(input, toCamelCase);
9696
expect(result).toEqual({
9797
dataMatrix: [
9898
[{ cellValue: 1 }, { cellValue: 2 }],
@@ -120,7 +120,7 @@ describe('inflektTree', () => {
120120
updated_at: '2024-01-02',
121121
},
122122
};
123-
const result = inflektTree(input, (key) => camelize(key, true));
123+
const result = inflektTree(input, toCamelCase);
124124
expect(result).toEqual({
125125
userName: 'John',
126126
orderItems: [
@@ -144,7 +144,7 @@ describe('inflektTree', () => {
144144
it('should preserve Date objects', () => {
145145
const date = new Date('2024-01-15T12:00:00Z');
146146
const input = { created_at: date };
147-
const result = inflektTree(input, (key) => camelize(key, true));
147+
const result = inflektTree(input, toCamelCase);
148148

149149
expect(result.createdAt).toBeInstanceOf(Date);
150150
expect(result.createdAt.getTime()).toBe(date.getTime());
@@ -158,7 +158,7 @@ describe('inflektTree', () => {
158158
last_login: date,
159159
},
160160
};
161-
const result = inflektTree(input, (key) => camelize(key, true));
161+
const result = inflektTree(input, toCamelCase);
162162

163163
expect(result.userData.lastLogin).toBeInstanceOf(Date);
164164
expect(result.userData.lastLogin.getTime()).toBe(date.getTime());
@@ -167,35 +167,35 @@ describe('inflektTree', () => {
167167

168168
describe('null and undefined handling', () => {
169169
it('should return null for null input', () => {
170-
const result = inflektTree(null, (key) => camelize(key, true));
170+
const result = inflektTree(null, toCamelCase);
171171
expect(result).toBeNull();
172172
});
173173

174174
it('should return undefined for undefined input', () => {
175-
const result = inflektTree(undefined, (key) => camelize(key, true));
175+
const result = inflektTree(undefined, toCamelCase);
176176
expect(result).toBeUndefined();
177177
});
178178

179179
it('should preserve null values in objects', () => {
180180
const input = { user_name: null as null, user_age: 30 };
181-
const result = inflektTree(input, (key) => camelize(key, true));
181+
const result = inflektTree(input, toCamelCase);
182182
expect(result).toEqual({ userName: null, userAge: 30 });
183183
});
184184

185185
it('should preserve undefined values in objects', () => {
186186
const input = { user_name: undefined as undefined, user_age: 30 };
187-
const result = inflektTree(input, (key) => camelize(key, true));
187+
const result = inflektTree(input, toCamelCase);
188188
expect(result).toEqual({ userName: undefined, userAge: 30 });
189189
});
190190
});
191191

192192
describe('primitive inputs', () => {
193193
it('should return primitives as-is', () => {
194-
expect(inflektTree('string', (key) => camelize(key, true))).toBe(
194+
expect(inflektTree('string', toCamelCase)).toBe(
195195
'string'
196196
);
197-
expect(inflektTree(123, (key) => camelize(key, true))).toBe(123);
198-
expect(inflektTree(true, (key) => camelize(key, true))).toBe(true);
197+
expect(inflektTree(123, toCamelCase)).toBe(123);
198+
expect(inflektTree(true, toCamelCase)).toBe(true);
199199
});
200200
});
201201

@@ -206,7 +206,7 @@ describe('inflektTree', () => {
206206
_private_field: 'secret',
207207
_another_private: 'data',
208208
};
209-
const result = inflektTree(input, (key) => camelize(key, true), {
209+
const result = inflektTree(input, toCamelCase, {
210210
skip: (key) => key.startsWith('_'),
211211
});
212212
expect(result).toEqual({
@@ -226,7 +226,7 @@ describe('inflektTree', () => {
226226
},
227227
},
228228
};
229-
const result = inflektTree(input, (key) => camelize(key, true), {
229+
const result = inflektTree(input, toCamelCase, {
230230
skip: (key, path) => path.length > 1, // only transform top 2 levels
231231
});
232232
expect(result).toEqual({
@@ -269,7 +269,7 @@ describe('inflektTree', () => {
269269
created_at: '2024-01-01',
270270
updated_at: '2024-01-02',
271271
};
272-
const result = inflektTree(input, (key) => camelize(key, true), {
272+
const result = inflektTree(input, toCamelCase, {
273273
skip: (key) => key === 'created_at' || key === 'updated_at',
274274
});
275275
expect(result).toEqual({
@@ -283,7 +283,7 @@ describe('inflektTree', () => {
283283
const input = {
284284
items: [{ item_id: 1, _meta: 'data' }],
285285
};
286-
const result = inflektTree(input, (key) => camelize(key, true), {
286+
const result = inflektTree(input, toCamelCase, {
287287
skip: (key) => key.startsWith('_'),
288288
});
289289
expect(result).toEqual({
@@ -296,7 +296,7 @@ describe('inflektTree', () => {
296296
it('should be able to convert to snake_case and back to camelCase', () => {
297297
const original = { userName: 'John', orderItems: [{ itemId: 1 }] };
298298
const snakeCase = inflektTree(original, underscore);
299-
const backToCamel = inflektTree(snakeCase, (key) => camelize(key, true));
299+
const backToCamel = inflektTree(snakeCase, toCamelCase);
300300
expect(backToCamel).toEqual(original);
301301
});
302302
});

0 commit comments

Comments
 (0)