Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/inflekt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
underscore,
toFieldName,
toQueryName,
inflektObject,
} from 'inflekt';

// Basic singularization/pluralization
Expand Down Expand Up @@ -65,6 +66,10 @@ underscore('UserProfile'); // 'user_profile'
// GraphQL naming helpers
toFieldName('Users'); // 'user'
toQueryName('User'); // 'users'

// Object key transformation (kebab-case to camelCase)
inflektObject({ 'schema-file': 'test.graphql', 'dry-run': true });
// { schemaFile: 'test.graphql', dryRun: true }
```

## API
Expand All @@ -91,6 +96,10 @@ toQueryName('User'); // 'users'
- `toFieldName(pluralTypeName)` - Convert plural PascalCase to singular camelCase field name
- `toQueryName(singularTypeName)` - Convert singular PascalCase to plural camelCase query name

### Object Transformations

- `inflektObject(obj)` - Convert kebab-case object keys to camelCase (useful for CLI argument conversion)

## Latin Suffix Overrides

This library handles Latin plural suffixes differently than the standard `inflection` package to match PostGraphile's behavior:
Expand Down
119 changes: 119 additions & 0 deletions packages/inflekt/__tests__/inflection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
fixCapitalisedPlural,
toFieldName,
toQueryName,
inflektObject,
} from '../src';

describe('singularize', () => {
Expand Down Expand Up @@ -198,3 +199,121 @@ describe('toQueryName', () => {
expect(toQueryName('Category')).toBe('categories');
});
});

describe('inflektObject', () => {
it('should convert kebab-case keys to camelCase', () => {
const input = {
'schema-file': 'test.graphql',
'dry-run': true,
'api-names': ['api1', 'api2'],
};
const expected = {
schemaFile: 'test.graphql',
dryRun: true,
apiNames: ['api1', 'api2'],
};
expect(inflektObject(input)).toEqual(expected);
});

it('should preserve camelCase keys', () => {
const input = {
endpoint: 'http://localhost:3000',
output: './generated',
verbose: false,
};
expect(inflektObject(input)).toEqual(input);
});

it('should handle mixed kebab-case and camelCase keys', () => {
const input = {
'schema-file': 'schema.graphql',
endpoint: 'http://localhost:3000',
'react-query': true,
output: './dist',
'browser-compatible': false,
};
const expected = {
schemaFile: 'schema.graphql',
endpoint: 'http://localhost:3000',
reactQuery: true,
output: './dist',
browserCompatible: false,
};
expect(inflektObject(input)).toEqual(expected);
});

it('should handle empty objects', () => {
expect(inflektObject({})).toEqual({});
});

it('should handle objects with various value types', () => {
const input = {
'string-value': 'test',
'number-value': 42,
'boolean-value': true,
'null-value': null as null,
'undefined-value': undefined as undefined,
'array-value': [1, 2, 3],
'object-value': { nested: 'value' },
};
const expected = {
stringValue: 'test',
numberValue: 42,
booleanValue: true,
nullValue: null as null,
undefinedValue: undefined as undefined,
arrayValue: [1, 2, 3],
objectValue: { nested: 'value' },
};
expect(inflektObject(input)).toEqual(expected);
});

it('should handle multiple consecutive hyphens', () => {
const input = {
'multi-word-key-name': 'value',
};
const expected = {
multiWordKeyName: 'value',
};
expect(inflektObject(input)).toEqual(expected);
});

it('should preserve single-word keys', () => {
const input = {
endpoint: 'url',
schemas: ['public'],
orm: true,
};
expect(inflektObject(input)).toEqual(input);
});

it('should handle CLI argument conversion use case', () => {
const argv = {
endpoint: 'http://localhost:5000/graphql',
'schema-file': undefined as string | undefined,
output: 'codegen',
schemas: undefined as string[] | undefined,
'api-names': undefined as string[] | undefined,
'react-query': true,
orm: false,
'browser-compatible': true,
authorization: 'Bearer token123',
'dry-run': false,
verbose: true,
};
const expected = {
endpoint: 'http://localhost:5000/graphql',
schemaFile: undefined as string | undefined,
output: 'codegen',
schemas: undefined as string[] | undefined,
apiNames: undefined as string[] | undefined,
reactQuery: true,
orm: false,
browserCompatible: true,
authorization: 'Bearer token123',
dryRun: false,
verbose: true,
};
expect(inflektObject(argv)).toEqual(expected);
});
});
1 change: 1 addition & 0 deletions packages/inflekt/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
export * from './pluralize';
export * from './case';
export * from './naming';
export * from './object';
23 changes: 23 additions & 0 deletions packages/inflekt/src/object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Object key transformation utilities
*/

/**
* Convert kebab-case keys to camelCase in an object
* @param obj - Object with kebab-case keys
* @returns New object with camelCase keys
* @example inflektObject({ 'schema-file': 'test.graphql', 'dry-run': true }) -> { schemaFile: 'test.graphql', dryRun: true }
*/
export function inflektObject<T extends Record<string, any>>(obj: T): Record<string, any> {
const result: Record<string, any> = {};

for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// Convert kebab-case to camelCase
const camelKey = key.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
result[camelKey] = obj[key];
}
}

return result;
}