From ae788e7e650aa3c33ecb8429195542bfc058968e Mon Sep 17 00:00:00 2001 From: ghou9khub Date: Thu, 29 Jan 2026 00:22:32 -0800 Subject: [PATCH 1/2] Rename to argv and apply inflektTree --- packages/inflekt/README.md | 44 ++++++++ packages/inflekt/__tests__/argv.test.ts | 133 ++++++++++++++++++++++++ packages/inflekt/src/argv.ts | 29 ++++++ packages/inflekt/src/index.ts | 1 + 4 files changed, 207 insertions(+) create mode 100644 packages/inflekt/__tests__/argv.test.ts create mode 100644 packages/inflekt/src/argv.ts diff --git a/packages/inflekt/README.md b/packages/inflekt/README.md index 1d199a9..fbfc339 100644 --- a/packages/inflekt/README.md +++ b/packages/inflekt/README.md @@ -39,6 +39,7 @@ import { toFieldName, toQueryName, inflektTree, + camelizeArgv, } from 'inflekt'; // Basic singularization/pluralization @@ -74,6 +75,11 @@ const apiResponse = { }; inflektTree(apiResponse, (key) => camelize(key, true)); // Result: { userName: 'John', orderItems: [{ itemId: 1, productName: 'Widget' }] } + +// CLI argument transformation (kebab-case to camelCase) +const argv = { 'schema-file': 'test.graphql', 'dry-run': true, _: [] }; +camelizeArgv(argv); +// Result: { schemaFile: 'test.graphql', dryRun: true, _: [] } ``` ## API @@ -104,6 +110,10 @@ inflektTree(apiResponse, (key) => camelize(key, true)); - `inflektTree(obj, transformer, options?)` - Recursively transform all property names in an object tree +### CLI Argument Utilities + +- `camelizeArgv(argv)` - Transform CLI argument keys (kebab-case/snake_case) to camelCase, preserving minimist internals + ## Deep Object Key Transformation The `inflektTree` function recursively transforms all property names in an object tree using any transformer function. This is useful for converting API responses between naming conventions. @@ -180,6 +190,40 @@ const result3 = inflektTree(deepObject, (key) => camelize(key, true), { - Returns primitives unchanged - Works with any transformer function +## CLI Argument Transformation + +The `camelizeArgv` function is a specialized utility for transforming CLI argument objects (typically from minimist or similar parsers) from kebab-case or snake_case to camelCase. + +### Usage + +```typescript +import { camelizeArgv } from 'inflekt'; + +// Transform CLI arguments +const argv = { + 'schema-file': 'test.graphql', + 'dry-run': true, + output_dir: 'dist', + _: ['arg1', 'arg2'] +}; + +const parsedArgv = camelizeArgv(argv); +// Result: +// { +// schemaFile: 'test.graphql', +// dryRun: true, +// outputDir: 'dist', +// _: ['arg1', 'arg2'] +// } +``` + +### Features + +- Transforms kebab-case and snake_case keys to camelCase +- Only transforms top-level keys (preserves nested object structure) +- Preserves minimist internal keys (`_` and keys starting with `_`) +- Built on top of `inflektTree` for consistency + ## Latin Suffix Overrides This library handles Latin plural suffixes differently than the standard `inflection` package to match PostGraphile's behavior: diff --git a/packages/inflekt/__tests__/argv.test.ts b/packages/inflekt/__tests__/argv.test.ts new file mode 100644 index 0000000..15dcbc0 --- /dev/null +++ b/packages/inflekt/__tests__/argv.test.ts @@ -0,0 +1,133 @@ +import { camelizeArgv } from '../src/argv'; + +describe('camelizeArgv', () => { + it('should convert kebab-case keys to camelCase', () => { + const argv = { + 'schema-file': 'test.graphql', + 'dry-run': true, + output: 'dist' + }; + + const result = camelizeArgv(argv); + + expect(result).toEqual({ + schemaFile: 'test.graphql', + dryRun: true, + output: 'dist' + }); + }); + + it('should convert snake_case keys to camelCase', () => { + const argv = { + schema_file: 'test.graphql', + dry_run: true, + output_dir: 'dist' + }; + + const result = camelizeArgv(argv); + + expect(result).toEqual({ + schemaFile: 'test.graphql', + dryRun: true, + outputDir: 'dist' + }); + }); + + it('should preserve minimist internal _ key', () => { + const argv = { + 'schema-file': 'test.graphql', + _: ['arg1', 'arg2'] + }; + + const result = camelizeArgv(argv); + + expect(result).toEqual({ + schemaFile: 'test.graphql', + _: ['arg1', 'arg2'] + }); + }); + + it('should skip keys starting with underscore', () => { + const argv = { + 'schema-file': 'test.graphql', + _private: 'secret', + _internal_flag: true + }; + + const result = camelizeArgv(argv); + + expect(result).toEqual({ + schemaFile: 'test.graphql', + _private: 'secret', + _internal_flag: true + }); + }); + + it('should only transform top-level keys', () => { + const argv = { + 'schema-file': 'test.graphql', + config: { + 'nested-key': 'value', + another_nested: 'data' + } + }; + + const result = camelizeArgv(argv); + + expect(result).toEqual({ + schemaFile: 'test.graphql', + config: { + 'nested-key': 'value', + another_nested: 'data' + } + }); + }); + + it('should handle arrays in values', () => { + const argv = { + 'include-files': ['file1.ts', 'file2.ts'], + _: [] as string[] + }; + + const result = camelizeArgv(argv); + + expect(result).toEqual({ + includeFiles: ['file1.ts', 'file2.ts'], + _: [] + }); + }); + + it('should handle mixed kebab-case and snake_case', () => { + const argv = { + 'schema-file': 'test.graphql', + output_dir: 'dist', + 'dry-run': true, + verbose_mode: false + }; + + const result = camelizeArgv(argv); + + expect(result).toEqual({ + schemaFile: 'test.graphql', + outputDir: 'dist', + dryRun: true, + verboseMode: false + }); + }); + + it('should preserve already camelCase keys', () => { + const argv = { + schemaFile: 'test.graphql', + dryRun: true, + _: [] as string[] + }; + + const result = camelizeArgv(argv); + + expect(result).toEqual({ + schemaFile: 'test.graphql', + dryRun: true, + _: [] + }); + }); +}); diff --git a/packages/inflekt/src/argv.ts b/packages/inflekt/src/argv.ts new file mode 100644 index 0000000..ecb4ed6 --- /dev/null +++ b/packages/inflekt/src/argv.ts @@ -0,0 +1,29 @@ +/** + * Object key transformation utilities for CLI arguments and other use cases + */ + +import { camelize } from 'inflection'; +import { inflektTree } from './transform-keys'; + +const isTopLevel = (_key: string, path: string[]) => path.length === 0; + +/** + * Camelize argv keys (typically from minimist or similar CLI parsers) + * Transforms kebab-case and snake_case keys to camelCase at the top level only. + * Skips minimist internal keys like '_' and keys starting with '_'. + * + * @param argv - Parsed CLI arguments object + * @returns New object with camelCase keys + * + * @example + * const argv = { 'schema-file': 'test.graphql', 'dry-run': true, _: [] }; + * const parsedArgv = camelizeArgv(argv); + * // Result: { schemaFile: 'test.graphql', dryRun: true, _: [] } + */ +export const camelizeArgv = (argv: Record) => + inflektTree(argv, (key) => camelize(key, true), { + skip: (key, path) => + !isTopLevel(key, path) || + key === '_' || + key.startsWith('_') + }); diff --git a/packages/inflekt/src/index.ts b/packages/inflekt/src/index.ts index d9d12e4..2ee2c71 100644 --- a/packages/inflekt/src/index.ts +++ b/packages/inflekt/src/index.ts @@ -9,3 +9,4 @@ export * from './pluralize'; export * from './case'; export * from './naming'; export * from './transform-keys'; +export * from './argv'; From 0f08769bca34d2d1f69e81f841375213e4438fe0 Mon Sep 17 00:00:00 2001 From: ghou9khub Date: Thu, 29 Jan 2026 20:05:02 -0800 Subject: [PATCH 2/2] Revert argv change. Update camelize to suppor kebab case --- packages/inflekt/README.md | 44 ------ packages/inflekt/__tests__/argv.test.ts | 133 ------------------ packages/inflekt/__tests__/inflection.test.ts | 12 ++ packages/inflekt/src/argv.ts | 29 ---- packages/inflekt/src/case.ts | 8 +- packages/inflekt/src/index.ts | 1 - 6 files changed, 17 insertions(+), 210 deletions(-) delete mode 100644 packages/inflekt/__tests__/argv.test.ts delete mode 100644 packages/inflekt/src/argv.ts diff --git a/packages/inflekt/README.md b/packages/inflekt/README.md index fbfc339..1d199a9 100644 --- a/packages/inflekt/README.md +++ b/packages/inflekt/README.md @@ -39,7 +39,6 @@ import { toFieldName, toQueryName, inflektTree, - camelizeArgv, } from 'inflekt'; // Basic singularization/pluralization @@ -75,11 +74,6 @@ const apiResponse = { }; inflektTree(apiResponse, (key) => camelize(key, true)); // Result: { userName: 'John', orderItems: [{ itemId: 1, productName: 'Widget' }] } - -// CLI argument transformation (kebab-case to camelCase) -const argv = { 'schema-file': 'test.graphql', 'dry-run': true, _: [] }; -camelizeArgv(argv); -// Result: { schemaFile: 'test.graphql', dryRun: true, _: [] } ``` ## API @@ -110,10 +104,6 @@ camelizeArgv(argv); - `inflektTree(obj, transformer, options?)` - Recursively transform all property names in an object tree -### CLI Argument Utilities - -- `camelizeArgv(argv)` - Transform CLI argument keys (kebab-case/snake_case) to camelCase, preserving minimist internals - ## Deep Object Key Transformation The `inflektTree` function recursively transforms all property names in an object tree using any transformer function. This is useful for converting API responses between naming conventions. @@ -190,40 +180,6 @@ const result3 = inflektTree(deepObject, (key) => camelize(key, true), { - Returns primitives unchanged - Works with any transformer function -## CLI Argument Transformation - -The `camelizeArgv` function is a specialized utility for transforming CLI argument objects (typically from minimist or similar parsers) from kebab-case or snake_case to camelCase. - -### Usage - -```typescript -import { camelizeArgv } from 'inflekt'; - -// Transform CLI arguments -const argv = { - 'schema-file': 'test.graphql', - 'dry-run': true, - output_dir: 'dist', - _: ['arg1', 'arg2'] -}; - -const parsedArgv = camelizeArgv(argv); -// Result: -// { -// schemaFile: 'test.graphql', -// dryRun: true, -// outputDir: 'dist', -// _: ['arg1', 'arg2'] -// } -``` - -### Features - -- Transforms kebab-case and snake_case keys to camelCase -- Only transforms top-level keys (preserves nested object structure) -- Preserves minimist internal keys (`_` and keys starting with `_`) -- Built on top of `inflektTree` for consistency - ## Latin Suffix Overrides This library handles Latin plural suffixes differently than the standard `inflection` package to match PostGraphile's behavior: diff --git a/packages/inflekt/__tests__/argv.test.ts b/packages/inflekt/__tests__/argv.test.ts deleted file mode 100644 index 15dcbc0..0000000 --- a/packages/inflekt/__tests__/argv.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { camelizeArgv } from '../src/argv'; - -describe('camelizeArgv', () => { - it('should convert kebab-case keys to camelCase', () => { - const argv = { - 'schema-file': 'test.graphql', - 'dry-run': true, - output: 'dist' - }; - - const result = camelizeArgv(argv); - - expect(result).toEqual({ - schemaFile: 'test.graphql', - dryRun: true, - output: 'dist' - }); - }); - - it('should convert snake_case keys to camelCase', () => { - const argv = { - schema_file: 'test.graphql', - dry_run: true, - output_dir: 'dist' - }; - - const result = camelizeArgv(argv); - - expect(result).toEqual({ - schemaFile: 'test.graphql', - dryRun: true, - outputDir: 'dist' - }); - }); - - it('should preserve minimist internal _ key', () => { - const argv = { - 'schema-file': 'test.graphql', - _: ['arg1', 'arg2'] - }; - - const result = camelizeArgv(argv); - - expect(result).toEqual({ - schemaFile: 'test.graphql', - _: ['arg1', 'arg2'] - }); - }); - - it('should skip keys starting with underscore', () => { - const argv = { - 'schema-file': 'test.graphql', - _private: 'secret', - _internal_flag: true - }; - - const result = camelizeArgv(argv); - - expect(result).toEqual({ - schemaFile: 'test.graphql', - _private: 'secret', - _internal_flag: true - }); - }); - - it('should only transform top-level keys', () => { - const argv = { - 'schema-file': 'test.graphql', - config: { - 'nested-key': 'value', - another_nested: 'data' - } - }; - - const result = camelizeArgv(argv); - - expect(result).toEqual({ - schemaFile: 'test.graphql', - config: { - 'nested-key': 'value', - another_nested: 'data' - } - }); - }); - - it('should handle arrays in values', () => { - const argv = { - 'include-files': ['file1.ts', 'file2.ts'], - _: [] as string[] - }; - - const result = camelizeArgv(argv); - - expect(result).toEqual({ - includeFiles: ['file1.ts', 'file2.ts'], - _: [] - }); - }); - - it('should handle mixed kebab-case and snake_case', () => { - const argv = { - 'schema-file': 'test.graphql', - output_dir: 'dist', - 'dry-run': true, - verbose_mode: false - }; - - const result = camelizeArgv(argv); - - expect(result).toEqual({ - schemaFile: 'test.graphql', - outputDir: 'dist', - dryRun: true, - verboseMode: false - }); - }); - - it('should preserve already camelCase keys', () => { - const argv = { - schemaFile: 'test.graphql', - dryRun: true, - _: [] as string[] - }; - - const result = camelizeArgv(argv); - - expect(result).toEqual({ - schemaFile: 'test.graphql', - dryRun: true, - _: [] - }); - }); -}); diff --git a/packages/inflekt/__tests__/inflection.test.ts b/packages/inflekt/__tests__/inflection.test.ts index 6d86a36..862f603 100644 --- a/packages/inflekt/__tests__/inflection.test.ts +++ b/packages/inflekt/__tests__/inflection.test.ts @@ -141,6 +141,18 @@ describe('camelize', () => { expect(camelize('api_schema', true)).toBe('apiSchema'); }); + it('should convert kebab-case to PascalCase by default', () => { + expect(camelize('schema-file')).toBe('SchemaFile'); + expect(camelize('dry-run')).toBe('DryRun'); + expect(camelize('output-dir')).toBe('OutputDir'); + }); + + it('should convert kebab-case to camelCase when lowFirstLetter is true', () => { + expect(camelize('schema-file', true)).toBe('schemaFile'); + expect(camelize('dry-run', true)).toBe('dryRun'); + expect(camelize('output-dir', true)).toBe('outputDir'); + }); + it('should handle single words', () => { expect(camelize('user')).toBe('User'); expect(camelize('user', true)).toBe('user'); diff --git a/packages/inflekt/src/argv.ts b/packages/inflekt/src/argv.ts deleted file mode 100644 index ecb4ed6..0000000 --- a/packages/inflekt/src/argv.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Object key transformation utilities for CLI arguments and other use cases - */ - -import { camelize } from 'inflection'; -import { inflektTree } from './transform-keys'; - -const isTopLevel = (_key: string, path: string[]) => path.length === 0; - -/** - * Camelize argv keys (typically from minimist or similar CLI parsers) - * Transforms kebab-case and snake_case keys to camelCase at the top level only. - * Skips minimist internal keys like '_' and keys starting with '_'. - * - * @param argv - Parsed CLI arguments object - * @returns New object with camelCase keys - * - * @example - * const argv = { 'schema-file': 'test.graphql', 'dry-run': true, _: [] }; - * const parsedArgv = camelizeArgv(argv); - * // Result: { schemaFile: 'test.graphql', dryRun: true, _: [] } - */ -export const camelizeArgv = (argv: Record) => - inflektTree(argv, (key) => camelize(key, true), { - skip: (key, path) => - !isTopLevel(key, path) || - key === '_' || - key.startsWith('_') - }); diff --git a/packages/inflekt/src/case.ts b/packages/inflekt/src/case.ts index 9bc7c13..7be701b 100644 --- a/packages/inflekt/src/case.ts +++ b/packages/inflekt/src/case.ts @@ -28,15 +28,17 @@ export function fixCapitalisedPlural(str: string): string { } /** - * Convert snake_case to PascalCase (or camelCase if lowFirstLetter is true) - * @param str - The snake_case string to convert + * Convert snake_case or kebab-case to PascalCase (or camelCase if lowFirstLetter is true) + * @param str - The snake_case or kebab-case string to convert * @param lowFirstLetter - If true, returns camelCase instead of PascalCase * @example camelize('user_profile') -> 'UserProfile' * @example camelize('user_profile', true) -> 'userProfile' + * @example camelize('schema-file') -> 'SchemaFile' + * @example camelize('schema-file', true) -> 'schemaFile' */ export function camelize(str: string, lowFirstLetter?: boolean): string { const result = str - .split('_') + .split(/[-_]/) .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(''); diff --git a/packages/inflekt/src/index.ts b/packages/inflekt/src/index.ts index 2ee2c71..d9d12e4 100644 --- a/packages/inflekt/src/index.ts +++ b/packages/inflekt/src/index.ts @@ -9,4 +9,3 @@ export * from './pluralize'; export * from './case'; export * from './naming'; export * from './transform-keys'; -export * from './argv';