import mapObject from 'map-obj'; import { camelCase } from 'camel-case'; import { pascalCase } from 'pascal-case'; import { snakeCase } from 'snake-case'; import type { CamelCase, CamelCasedProperties, CamelCasedPropertiesDeep, PascalCase, PascalCasedProperties, PascalCasedPropertiesDeep, SnakeCase, SnakeCasedProperties, SnakeCasedPropertiesDeep, } from 'type-fest'; import type { Options as MapOptions } from 'map-obj'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type PlainObject = Record; function isLastCharNumber(key: string): boolean { return /^\d$/.test(key[key.length - 1]); } function splitLastChar(key: string): string { return `${key.slice(0, key.length - 1)}_${key.slice( key.length - 1, key.length )}`; } /** * Converts a string to snake case. * * @param text - The input string * @returns The converted string * * @example * ```js * snakecase('fooBar'); * //=> 'foo_bar' * ``` */ function snakecase(text: T): SnakeCase { const matches = text.match(/\d+/g); if (!matches) { return snakeCase(text) as SnakeCase; } let modifiedStr: string = text; for (let i = 0; i < matches.length; i++) { const match = matches[i]; const mathIndex = modifiedStr.indexOf(match); modifiedStr = `${modifiedStr.slice(0, mathIndex)}_${modifiedStr.slice( mathIndex, modifiedStr.length )}`; } return snakeCase(modifiedStr) as SnakeCase; } /** * Converts object keys to snake case. * * @param obj - The input object * @param options - The options to config this convert function * @returns The converted object * * @example * ```js * snakecaseKeys({ 'fooBar': true }); * //=> { 'foo_bar': true } * ``` */ function snakecaseKeys( obj: T, options?: O ): O['deep'] extends true ? SnakeCasedPropertiesDeep : SnakeCasedProperties { return mapObject( obj, (key, val) => [snakecase(key as string), val], options ) as O['deep'] extends true ? SnakeCasedPropertiesDeep : SnakeCasedProperties; } /** * Converts object keys to snake case deeply. * * @param obj - The input object * @returns The converted object * * @example * ```js * snakecaseKeysDeep({ 'fooBar': { 'barFoo': true } }); * //=> { 'foo_bar': { 'bar_foo': true } } * ``` */ function snakecaseKeysDeep( obj: T ): SnakeCasedPropertiesDeep { return snakecaseKeys(obj, { deep: true }); } /** * Converts a string to camel case. * * @param text - The input string * @returns The converted string * * @example * ```js * camelcase('foo_bar'); * //=> 'fooBar' * ``` */ function camelcase(text: T): CamelCase { const parts = text.split('_'); const modifiedStr = parts.reduce((acc, part) => { if (acc === '') return part; if (/^\d+/.test(part)) { return acc + part; } return `${acc}_${part}`; }, ''); return camelCase(modifiedStr) as CamelCase; } /** * Converts object keys to camel case. * * @param obj - The input object * @param options - The options to config this convert function * @returns The converted object * * @example * ```js * camelcaseKeys({ 'foo_bar': true }); * //=> { 'fooBar': true } * ``` */ function camelcaseKeys( obj: T, options?: O ): O['deep'] extends true ? CamelCasedPropertiesDeep : CamelCasedProperties { return mapObject( obj, (key, val) => [camelcase(key as string), val], options ) as O['deep'] extends true ? CamelCasedPropertiesDeep : CamelCasedProperties; } /** * Converts object keys to camel case deeply. * * @param obj - The input object * @returns The converted object * * @example * ```js * camelcaseKeysDeep({ 'foo_bar': { 'bar_foo': true } }); * //=> { 'fooBar': { 'barFoo': true } } * ``` */ function camelcaseKeysDeep( obj: T ): CamelCasedPropertiesDeep { return camelcaseKeys(obj, { deep: true }); } /** * Converts a string to pascal case. * * @param text - The input string * @returns The converted string * * @example * ```js * pascalcase('fooBar'); * //=> 'FooBar' * ``` */ function pascalcase(str: T): PascalCase { return pascalCase( isLastCharNumber(str) ? splitLastChar(str) : str ) as PascalCase; } /** * Converts object keys to pascal case. * * @param obj - The input object * @param options - The options to config this convert function * @returns The converted object * * @example * ```js * pascalcaseKeys({ 'fooBar': true }); * //=> { 'FooBar': true } * ``` */ function pascalcaseKeys( obj: T, options?: O ): O['deep'] extends true ? PascalCasedPropertiesDeep : PascalCasedProperties { return mapObject( obj, (key, val) => [pascalcase(key as string), val], options ) as O['deep'] extends true ? PascalCasedPropertiesDeep : PascalCasedProperties; } /** * Converts object keys to pascal case deeply. * * @param obj - The input object * @returns The converted object * * @example * ```js * pascalcaseKeysDeep({ 'fooBar': { 'barFoo': true } }); * //=> { 'FooBar': { 'BarFoo': true } } * ``` */ function pascalcaseKeysDeep( obj: T ): PascalCasedPropertiesDeep { return pascalcaseKeys(obj, { deep: true }); } export { snakecase, snakecaseKeys, snakecaseKeysDeep, camelcase, camelcaseKeys, camelcaseKeysDeep, pascalcase, pascalcaseKeys, pascalcaseKeysDeep, };