deep-case-crafter 3.3.0 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -15,23 +15,30 @@ type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;
15
15
  type IsStringLiteral<S extends string> = Record<never, never> extends Record<S, never> ? false : true;
16
16
 
17
17
  /**
18
- * Represents the supported string case formats for transformation
18
+ * Represents the supported string case formats for transformation. `'constant'` is
19
+ * CONSTANT_CASE (`USER_ID`), a target case only: keys such as `USER_ID` are left
20
+ * unchanged by every target case.
19
21
  */
20
- type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab';
22
+ type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab' | 'constant';
21
23
  /**
22
24
  * The plain literal of a target case: a string enum member or a branded literal such as
23
25
  * `'snake' & { __brand: 'Case' }` becomes `'snake'`, so it compares equal to the case
24
26
  * detected from a key. Only literals are produced, never `To` itself: TypeScript 4.7
25
27
  * narrows `To` to `To & StringCase` in a true branch, which is `never` for an enum member.
28
+ *
29
+ * Not meant to be used directly. It is exported so that declaration files of code with a
30
+ * generic target case refer to it instead of writing out the list of target cases, which
31
+ * would leave out cases added later.
26
32
  */
27
- type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : never;
33
+ type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : To extends 'constant' ? 'constant' : never;
28
34
 
29
35
  /**
30
36
  * Options for the transformKey function. The transform function takes them as well.
31
37
  */
32
38
  interface TransformKeyOptions {
33
39
  /**
34
- * The target casing format to transform to
40
+ * The target casing format to transform to. `'constant'` is CONSTANT_CASE, such as
41
+ * `USER_ID`.
35
42
  * @default 'camel'
36
43
  */
37
44
  targetCase?: StringCase | undefined;
@@ -109,7 +116,13 @@ type NoUnknownOptions<Options, Known = TransformOptions> = {
109
116
  type NoUnknownKeyOptions<Options> = [
110
117
  Exclude<keyof Options, keyof TransformKeyOptions>
111
118
  ] extends [never] ? unknown : NoUnknownOptions<Options, TransformKeyOptions>;
112
- /** `To` as a plain case literal, or `'camel'` (the default) for `undefined` */
119
+ /**
120
+ * `To` as a plain case literal, or `'camel'` (the default) for `undefined`.
121
+ *
122
+ * Not meant to be used directly. It is exported, like `PlainCase`, so that declaration
123
+ * files of code with a generic target case refer to it by name instead of writing out its
124
+ * definition.
125
+ */
113
126
  type CaseOrDefault<To> = To extends undefined ? 'camel' : PlainCase<To>;
114
127
  /**
115
128
  * The target case the runtime uses for one options object: `'camel'` when `targetCase`
@@ -238,9 +251,9 @@ type SeparatedToCamelCase<S extends string, Sep extends Separator, Acc extends s
238
251
  /** Replaces every `From` separator with `To` (`replaceSeparator`) */
239
252
  type ReplaceSeparator<S extends string, From extends Separator, To extends Separator, Acc extends string = ''> = S extends `${infer Head}${From}${infer Rest}` ? ReplaceSeparator<Rest, From, To, `${Acc}${Head}${To}`> : `${Acc}${S}`;
240
253
  /** A single word in the target case (`singleWordToCase`) */
241
- type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : Lowercase<K>;
254
+ type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : To extends 'constant' ? Uppercase<K> : Lowercase<K>;
242
255
  /** A key of the detected case `From` in the target case (the formatters `transformCase` picks) */
243
- type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
256
+ type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : To extends 'constant' ? Uppercase<From extends 'snake' ? K : ReplaceSeparator<K, '-', '_'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : To extends 'constant' ? Uppercase<ToSeparatedCase<K, '_'>> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
244
257
  /** Applies the detected case of `K` (`transformCase`) */
245
258
  type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
246
259
  /**
@@ -463,7 +476,7 @@ type TransformResultFor<T, Options> = IsAny<Options> extends true ? unknown : Op
463
476
  * The key type `transformKey` returns for an options object. Each member of a union of
464
477
  * options objects gives its own key, and an optional `targetCase` may also be omitted,
465
478
  * which gives the camelCase key (see `TransformResultForOne`). Options typed `any`, or a
466
- * `targetCase` typed `any`, give the key in any of the four cases.
479
+ * `targetCase` typed `any`, give the key in any of the target cases.
467
480
  *
468
481
  * Not exported: declaration files of code around `transformKey` refer to `TransformKey`
469
482
  * instead, because options with a generic type do not match the overload that returns
@@ -536,17 +549,18 @@ declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sour
536
549
 
537
550
  /**
538
551
  * Transforms one key to camelCase, the way `transform` transforms every key. A string
539
- * that is not a key in one of the four cases, such as `'Hello World'`, `'USER_ID'` or a
540
- * UUID, is returned unchanged (see "Key Preservation Rules" in the readme).
552
+ * that is not a camelCase, PascalCase, snake_case or kebab-case key, such as
553
+ * `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
554
+ * Rules" in the readme).
541
555
  *
542
556
  * The result type is the transformed key: `transformKey('user_id')` is typed `'userId'`.
543
557
  */
544
558
  declare function transformKey<K extends string>(key: K, options?: undefined): TransformKey<K>;
545
559
  /**
546
560
  * Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
547
- * every key. A string that is not a key in one of the four cases, such as
548
- * `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
549
- * Rules" in the readme).
561
+ * every key. A string that is not a camelCase, PascalCase, snake_case or kebab-case key,
562
+ * such as `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key
563
+ * Preservation Rules" in the readme).
550
564
  *
551
565
  * The result type is the transformed key: `transformKey('userID', { targetCase: 'snake' })`
552
566
  * is typed `'user_id'`, a union of keys or target cases gives the union of the results,
@@ -569,4 +583,4 @@ declare function transformKey<K extends string, Options extends TransformKeyOpti
569
583
  declare function transformKey(key: string, options?: TransformKeyOptions): string;
570
584
 
571
585
  export { transform, transformKey };
572
- export type { StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
586
+ export type { CaseOrDefault, PlainCase, StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
package/dist/index.d.mts CHANGED
@@ -15,23 +15,30 @@ type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;
15
15
  type IsStringLiteral<S extends string> = Record<never, never> extends Record<S, never> ? false : true;
16
16
 
17
17
  /**
18
- * Represents the supported string case formats for transformation
18
+ * Represents the supported string case formats for transformation. `'constant'` is
19
+ * CONSTANT_CASE (`USER_ID`), a target case only: keys such as `USER_ID` are left
20
+ * unchanged by every target case.
19
21
  */
20
- type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab';
22
+ type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab' | 'constant';
21
23
  /**
22
24
  * The plain literal of a target case: a string enum member or a branded literal such as
23
25
  * `'snake' & { __brand: 'Case' }` becomes `'snake'`, so it compares equal to the case
24
26
  * detected from a key. Only literals are produced, never `To` itself: TypeScript 4.7
25
27
  * narrows `To` to `To & StringCase` in a true branch, which is `never` for an enum member.
28
+ *
29
+ * Not meant to be used directly. It is exported so that declaration files of code with a
30
+ * generic target case refer to it instead of writing out the list of target cases, which
31
+ * would leave out cases added later.
26
32
  */
27
- type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : never;
33
+ type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : To extends 'constant' ? 'constant' : never;
28
34
 
29
35
  /**
30
36
  * Options for the transformKey function. The transform function takes them as well.
31
37
  */
32
38
  interface TransformKeyOptions {
33
39
  /**
34
- * The target casing format to transform to
40
+ * The target casing format to transform to. `'constant'` is CONSTANT_CASE, such as
41
+ * `USER_ID`.
35
42
  * @default 'camel'
36
43
  */
37
44
  targetCase?: StringCase | undefined;
@@ -109,7 +116,13 @@ type NoUnknownOptions<Options, Known = TransformOptions> = {
109
116
  type NoUnknownKeyOptions<Options> = [
110
117
  Exclude<keyof Options, keyof TransformKeyOptions>
111
118
  ] extends [never] ? unknown : NoUnknownOptions<Options, TransformKeyOptions>;
112
- /** `To` as a plain case literal, or `'camel'` (the default) for `undefined` */
119
+ /**
120
+ * `To` as a plain case literal, or `'camel'` (the default) for `undefined`.
121
+ *
122
+ * Not meant to be used directly. It is exported, like `PlainCase`, so that declaration
123
+ * files of code with a generic target case refer to it by name instead of writing out its
124
+ * definition.
125
+ */
113
126
  type CaseOrDefault<To> = To extends undefined ? 'camel' : PlainCase<To>;
114
127
  /**
115
128
  * The target case the runtime uses for one options object: `'camel'` when `targetCase`
@@ -238,9 +251,9 @@ type SeparatedToCamelCase<S extends string, Sep extends Separator, Acc extends s
238
251
  /** Replaces every `From` separator with `To` (`replaceSeparator`) */
239
252
  type ReplaceSeparator<S extends string, From extends Separator, To extends Separator, Acc extends string = ''> = S extends `${infer Head}${From}${infer Rest}` ? ReplaceSeparator<Rest, From, To, `${Acc}${Head}${To}`> : `${Acc}${S}`;
240
253
  /** A single word in the target case (`singleWordToCase`) */
241
- type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : Lowercase<K>;
254
+ type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : To extends 'constant' ? Uppercase<K> : Lowercase<K>;
242
255
  /** A key of the detected case `From` in the target case (the formatters `transformCase` picks) */
243
- type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
256
+ type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : To extends 'constant' ? Uppercase<From extends 'snake' ? K : ReplaceSeparator<K, '-', '_'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : To extends 'constant' ? Uppercase<ToSeparatedCase<K, '_'>> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
244
257
  /** Applies the detected case of `K` (`transformCase`) */
245
258
  type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
246
259
  /**
@@ -463,7 +476,7 @@ type TransformResultFor<T, Options> = IsAny<Options> extends true ? unknown : Op
463
476
  * The key type `transformKey` returns for an options object. Each member of a union of
464
477
  * options objects gives its own key, and an optional `targetCase` may also be omitted,
465
478
  * which gives the camelCase key (see `TransformResultForOne`). Options typed `any`, or a
466
- * `targetCase` typed `any`, give the key in any of the four cases.
479
+ * `targetCase` typed `any`, give the key in any of the target cases.
467
480
  *
468
481
  * Not exported: declaration files of code around `transformKey` refer to `TransformKey`
469
482
  * instead, because options with a generic type do not match the overload that returns
@@ -536,17 +549,18 @@ declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sour
536
549
 
537
550
  /**
538
551
  * Transforms one key to camelCase, the way `transform` transforms every key. A string
539
- * that is not a key in one of the four cases, such as `'Hello World'`, `'USER_ID'` or a
540
- * UUID, is returned unchanged (see "Key Preservation Rules" in the readme).
552
+ * that is not a camelCase, PascalCase, snake_case or kebab-case key, such as
553
+ * `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
554
+ * Rules" in the readme).
541
555
  *
542
556
  * The result type is the transformed key: `transformKey('user_id')` is typed `'userId'`.
543
557
  */
544
558
  declare function transformKey<K extends string>(key: K, options?: undefined): TransformKey<K>;
545
559
  /**
546
560
  * Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
547
- * every key. A string that is not a key in one of the four cases, such as
548
- * `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
549
- * Rules" in the readme).
561
+ * every key. A string that is not a camelCase, PascalCase, snake_case or kebab-case key,
562
+ * such as `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key
563
+ * Preservation Rules" in the readme).
550
564
  *
551
565
  * The result type is the transformed key: `transformKey('userID', { targetCase: 'snake' })`
552
566
  * is typed `'user_id'`, a union of keys or target cases gives the union of the results,
@@ -569,4 +583,4 @@ declare function transformKey<K extends string, Options extends TransformKeyOpti
569
583
  declare function transformKey(key: string, options?: TransformKeyOptions): string;
570
584
 
571
585
  export { transform, transformKey };
572
- export type { StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
586
+ export type { CaseOrDefault, PlainCase, StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
package/dist/index.d.ts CHANGED
@@ -15,23 +15,30 @@ type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;
15
15
  type IsStringLiteral<S extends string> = Record<never, never> extends Record<S, never> ? false : true;
16
16
 
17
17
  /**
18
- * Represents the supported string case formats for transformation
18
+ * Represents the supported string case formats for transformation. `'constant'` is
19
+ * CONSTANT_CASE (`USER_ID`), a target case only: keys such as `USER_ID` are left
20
+ * unchanged by every target case.
19
21
  */
20
- type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab';
22
+ type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab' | 'constant';
21
23
  /**
22
24
  * The plain literal of a target case: a string enum member or a branded literal such as
23
25
  * `'snake' & { __brand: 'Case' }` becomes `'snake'`, so it compares equal to the case
24
26
  * detected from a key. Only literals are produced, never `To` itself: TypeScript 4.7
25
27
  * narrows `To` to `To & StringCase` in a true branch, which is `never` for an enum member.
28
+ *
29
+ * Not meant to be used directly. It is exported so that declaration files of code with a
30
+ * generic target case refer to it instead of writing out the list of target cases, which
31
+ * would leave out cases added later.
26
32
  */
27
- type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : never;
33
+ type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : To extends 'constant' ? 'constant' : never;
28
34
 
29
35
  /**
30
36
  * Options for the transformKey function. The transform function takes them as well.
31
37
  */
32
38
  interface TransformKeyOptions {
33
39
  /**
34
- * The target casing format to transform to
40
+ * The target casing format to transform to. `'constant'` is CONSTANT_CASE, such as
41
+ * `USER_ID`.
35
42
  * @default 'camel'
36
43
  */
37
44
  targetCase?: StringCase | undefined;
@@ -109,7 +116,13 @@ type NoUnknownOptions<Options, Known = TransformOptions> = {
109
116
  type NoUnknownKeyOptions<Options> = [
110
117
  Exclude<keyof Options, keyof TransformKeyOptions>
111
118
  ] extends [never] ? unknown : NoUnknownOptions<Options, TransformKeyOptions>;
112
- /** `To` as a plain case literal, or `'camel'` (the default) for `undefined` */
119
+ /**
120
+ * `To` as a plain case literal, or `'camel'` (the default) for `undefined`.
121
+ *
122
+ * Not meant to be used directly. It is exported, like `PlainCase`, so that declaration
123
+ * files of code with a generic target case refer to it by name instead of writing out its
124
+ * definition.
125
+ */
113
126
  type CaseOrDefault<To> = To extends undefined ? 'camel' : PlainCase<To>;
114
127
  /**
115
128
  * The target case the runtime uses for one options object: `'camel'` when `targetCase`
@@ -238,9 +251,9 @@ type SeparatedToCamelCase<S extends string, Sep extends Separator, Acc extends s
238
251
  /** Replaces every `From` separator with `To` (`replaceSeparator`) */
239
252
  type ReplaceSeparator<S extends string, From extends Separator, To extends Separator, Acc extends string = ''> = S extends `${infer Head}${From}${infer Rest}` ? ReplaceSeparator<Rest, From, To, `${Acc}${Head}${To}`> : `${Acc}${S}`;
240
253
  /** A single word in the target case (`singleWordToCase`) */
241
- type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : Lowercase<K>;
254
+ type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : To extends 'constant' ? Uppercase<K> : Lowercase<K>;
242
255
  /** A key of the detected case `From` in the target case (the formatters `transformCase` picks) */
243
- type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
256
+ type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : To extends 'constant' ? Uppercase<From extends 'snake' ? K : ReplaceSeparator<K, '-', '_'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : To extends 'constant' ? Uppercase<ToSeparatedCase<K, '_'>> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
244
257
  /** Applies the detected case of `K` (`transformCase`) */
245
258
  type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
246
259
  /**
@@ -463,7 +476,7 @@ type TransformResultFor<T, Options> = IsAny<Options> extends true ? unknown : Op
463
476
  * The key type `transformKey` returns for an options object. Each member of a union of
464
477
  * options objects gives its own key, and an optional `targetCase` may also be omitted,
465
478
  * which gives the camelCase key (see `TransformResultForOne`). Options typed `any`, or a
466
- * `targetCase` typed `any`, give the key in any of the four cases.
479
+ * `targetCase` typed `any`, give the key in any of the target cases.
467
480
  *
468
481
  * Not exported: declaration files of code around `transformKey` refer to `TransformKey`
469
482
  * instead, because options with a generic type do not match the overload that returns
@@ -536,17 +549,18 @@ declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sour
536
549
 
537
550
  /**
538
551
  * Transforms one key to camelCase, the way `transform` transforms every key. A string
539
- * that is not a key in one of the four cases, such as `'Hello World'`, `'USER_ID'` or a
540
- * UUID, is returned unchanged (see "Key Preservation Rules" in the readme).
552
+ * that is not a camelCase, PascalCase, snake_case or kebab-case key, such as
553
+ * `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
554
+ * Rules" in the readme).
541
555
  *
542
556
  * The result type is the transformed key: `transformKey('user_id')` is typed `'userId'`.
543
557
  */
544
558
  declare function transformKey<K extends string>(key: K, options?: undefined): TransformKey<K>;
545
559
  /**
546
560
  * Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
547
- * every key. A string that is not a key in one of the four cases, such as
548
- * `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
549
- * Rules" in the readme).
561
+ * every key. A string that is not a camelCase, PascalCase, snake_case or kebab-case key,
562
+ * such as `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key
563
+ * Preservation Rules" in the readme).
550
564
  *
551
565
  * The result type is the transformed key: `transformKey('userID', { targetCase: 'snake' })`
552
566
  * is typed `'user_id'`, a union of keys or target cases gives the union of the results,
@@ -569,4 +583,4 @@ declare function transformKey<K extends string, Options extends TransformKeyOpti
569
583
  declare function transformKey(key: string, options?: TransformKeyOptions): string;
570
584
 
571
585
  export { transform, transformKey };
572
- export type { StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
586
+ export type { CaseOrDefault, PlainCase, StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";const e=/^[^.]+(?:\.[^.]+)*$/;function t(e){if("string"==typeof e)return JSON.stringify(e);if("bigint"==typeof e)return`${e}n`;if(null===e)return"null";if("object"==typeof e||"function"==typeof e)try{return Object.prototype.toString.call(e)}catch(e){return"an object"}return String(e)}function n(e){const t=e.indexOf(".");return-1===t?[e]:[e.slice(0,t),e.slice(t+1)]}const r=[];function o(e){let t,n=!1;for(const r of e)void 0===r?n=!0:(null!=t?t:t=[]).push(r);return{keepKey:n,pathsBelow:t?s(t):r,scope:void 0}}function s(e){return 1===e.length?e:[...new Set(e)].sort()}function c(e,t){const r=[];let s;for(const t of e){const[e,o]=n(t);if("*"===e)r.push(o);else{null!=s||(s=new Map);const t=s.get(e);t?t.push(o):s.set(e,[o])}}const c=new Map;for(const[e,t]of null!=s?s:[])c.set(e,o(r.length?[...t,...r]:t));const{targetCase:i,depth:u,keepReferences:f}=t.plain.options,a={targetCase:i,depth:u,keepReferences:f,exclude:null},l={options:a,visitedRefs:[],named:c,other:o(r),root:t};return a.exclude=l,l}function i(e,t){const n="string"==typeof t?e.named.get(t):void 0;return null!=n?n:e.other}function u(e,t){var n;return null!==(n=t.scope)&&void 0!==n?n:t.scope=0===t.pathsBelow.length?e.root.plain:function(e,t){const n=JSON.stringify(e);let r=t.states.get(n);return r||(r=c(e,t),t.states.set(n,r)),r}(t.pathsBelow,e.root)}const f="[object Object]";function a(e,t,n){"__proto__"===t?Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[t]=n}const l={};let p=!1;function d(e,t,n,r){let o=e[t];o.size>=16e6&&(p=!0,o=e[t]=new Map([[l,o]])),o.set(n,r)}function h(e){return e>=48&&e<=57}function y(e){return e>=97&&e<=122}function g(e){return e>=65&&e<=90}const w="s".charCodeAt(0);function v(e,t){if(!g(e.charCodeAt(t)))return!1;const n=e.charCodeAt(t-1);if(y(n)||h(n))return!0;const r=e.charCodeAt(t+1);return!(!g(n)||!y(r))&&(r!==w||y(e.charCodeAt(t+2)))}function b(e,t,n){return e.split(t).join(n)}function C(e,t,n){let r=n?e[0].toUpperCase():e[0],o=1,s=e.indexOf(t,1);for(;-1!==s;)r+=e.slice(o,s)+e[s+1].toUpperCase(),o=s+2,s=e.indexOf(t,o);return r+e.slice(o)}function m(e,t){let n="",r=0;for(let o=1;o<e.length;o++)v(e,o)&&(n+=e.slice(r,o)+t,r=o);return(n+e.slice(r)).toLowerCase()}function k(e,t){const n=e.length;if(0===n)return e;let r=e.charCodeAt(0);const o=g(r);if(!o&&!y(r))return e;let s=o?1:0,c=!o,i=0,u=0,f=!1,a=r;for(let t=1;t<n;t++){if(r=e.charCodeAt(t),y(r))c=!0;else if(g(r))s++;else if(95===r)i++,95===a&&(f=!0);else if(45===r)u++,45===a&&(f=!0);else if(!h(r))return e;a=r}if(95===a||45===a)return e;if(0===i&&0===u){if(0===s||1===s&&o)return function(e,t){const n=g(e.charCodeAt(0));switch(t){case"pascal":return n?e:e[0].toUpperCase()+e.slice(1);case"camel":return n?e[0].toLowerCase()+e.slice(1):e;default:return n?e.toLowerCase():e}}(e,t);if(!o)switch(t){case"snake":return m(e,"_");case"kebab":return m(e,"-");case"pascal":return e[0].toUpperCase()+e.slice(1);default:return e}if(!c)return e;switch(t){case"snake":return m(e,"_");case"kebab":return m(e,"-");case"camel":return function(e){let t=1;for(;t<e.length&&!v(e,t);)t++;return e.slice(0,t).toLowerCase()+e.slice(t)}(e);default:return e}}if(s>0||f||i>0&&u>0)return e;if(i>0)switch(t){case"camel":return C(e,"_",!1);case"pascal":return C(e,"_",!0);case"kebab":return b(e,"_","-");default:return e}if(function(e){if(36!==e.length)return!1;for(let t=0;t<36;t++){const n=e.charCodeAt(t);if(8===t||13===t||18===t||23===t){if(45!==n)return!1}else if(!(h(n)||n>=97&&n<=102||n>=65&&n<=70))return!1}return!0}(e))return e;switch(t){case"camel":return C(e,"-",!1);case"pascal":return C(e,"-",!0);case"snake":return b(e,"-","_");default:return e}}const j=new Map;let U,x={current:new Map,previous:new Map},M=0;function O(e,t){if(e.length>100)return k(e,t);if(t!==U){let e=j.get(t);e||(e={current:new Map,previous:new Map},j.set(t,e)),U=t,x=e}const n=x;let r=n.current.get(e);if(void 0!==r)return M=0,r;if(r=n.previous.get(e),void 0===r){if(r=k(e,t),++M>256&&M%8!=0)return r}else M=0;return n.current.size>=1e3&&(n.previous=n.current,n.current=new Map),n.current.set(e,r),r}const A={reason:"nesting limit reached"};function R(e,t,n=[],r=0,o=0){var s;if(null===e||"object"!=typeof e)return e;if(r>=t.depth)return e;const c=t.depth===1/0?0:r,h=t.keepReferences;if(h){const t=function(e,t){const n=e.get(t);if(void 0!==n||!p)return n;let r=e.get(l);for(;void 0!==r;){const e=r.get(t);if(void 0!==e)return e;r=r.get(l)}}(null!==(s=n[c])&&void 0!==s?s:n[c]=new Map,e);if(void 0!==t)return t}else if(o>=256)throw A;if(Array.isArray(e)){const s=[];return h&&d(n,c,e,s),function(e,t,n,r,o,s){for(const c of e)t.push(null!==c&&"object"==typeof c?R(c,n,r,o,s+1):c)}(e,s,t,n,r,o),s}const y=r+1,g=function(e){try{return Object.prototype.toString.call(e)}catch(e){return f}}(e);if(function(e,t){if(!(e instanceof Map)&&"[object Map]"!==t)return!1;try{return Map.prototype.has.call(e,void 0),!0}catch(e){return!1}}(e,g)){const r=new Map;return h&&d(n,c,e,r),null===t.exclude?function(e,t,n,r,o,s){for(const[c,i]of e)t.set("string"==typeof c?k(c,n.targetCase):c,R(i,n,r,o,s+1))}(e,r,t,n,y,o):function(e,t,n,r,o){for(const[s,c]of e){const e=i(n,s),f=u(n,e);t.set("string"!=typeof s||e.keepKey?s:k(s,n.options.targetCase),R(c,f.options,f.visitedRefs,r,o+1))}}(e,r,t.exclude,y,o),r}if(function(e,t){if(!(e instanceof Set)&&"[object Set]"!==t)return!1;try{return Set.prototype.has.call(e,void 0),!0}catch(e){return!1}}(e,g)){const r=new Set;return h&&d(n,c,e,r),function(e,t,n,r,o,s){for(const c of e)t.add(R(c,n,r,o,s+1))}(e,r,t,n,y,o),r}if(g===f){const r=e,s={};if(h&&d(n,c,e,s),null===t.exclude)for(const e in r)Object.prototype.hasOwnProperty.call(r,e)&&a(s,O(e,t.targetCase),R(r[e],t,n,y,o+1));else!function(e,t,n,r,o){for(const s in e)if(Object.prototype.hasOwnProperty.call(e,s)){const c=i(n,s),f=u(n,c);a(t,c.keepKey?s:O(s,n.options.targetCase),R(e[s],f.options,f.visitedRefs,r,o+1))}}(r,s,t.exclude,y,o);for(const e of Object.getOwnPropertySymbols(r))s[e]=r[e];return s}return e}const S=["camel","snake","pascal","kebab"];function $(e){const n=e||"camel";if(r=n,!S.includes(r))throw new TypeError(`Unsupported targetCase ${t(n)}. Use one of: ${S.join(", ")}.`);var r;return n}function _(n,r,o){const i=void 0===o?null:function(n,r){if(null==n)return null;if(!Array.isArray(n))throw new TypeError(`Unsupported exclude ${t(n)}. Use an array of paths, such as ["metadata.*"].`);for(const r of n)if("string"!=typeof r||!e.test(r))throw new TypeError(`Unsupported exclude path ${t(r)}. Use non-empty keys joined with dots, such as "metadata.*".`);if(0===n.length)return null;const o={plain:{options:r,visitedRefs:[]},states:new Map};return c(s(n),o)}(o,r);return null===i?R(n,r):R(n,i.options,i.visitedRefs)}exports.transform=function(e,n){var r,o;const s=null==n?void 0:n.targetCase,c=null!==(r=null==n?void 0:n.depth)&&void 0!==r?r:1/0,i=$(s);if("number"!=typeof c||Number.isNaN(c))throw new TypeError(`Unsupported depth ${t(c)}. Use a number, or omit depth to transform every level.`);const u=null==n?void 0:n.exclude,f=null===(o=null==n?void 0:n.keepReferences)||void 0===o||o;if("boolean"!=typeof f)throw new TypeError(`Unsupported keepReferences ${t(f)}. Use true or false.`);if(!f)try{return _(e,{targetCase:i,depth:c,keepReferences:f,exclude:null},u)}catch(e){if(e!==A)throw e}return _(e,{targetCase:i,depth:c,keepReferences:!0,exclude:null},u)},exports.transformKey=function(e,n){if("string"!=typeof e)throw new TypeError(`Unsupported key ${t(e)}. Use a string.`);if("string"==typeof n)throw new TypeError(`Unsupported options ${t(n)}. Use an options object: { targetCase: ${t(n)} }.`);return k(e,$(null==n?void 0:n.targetCase))};
1
+ "use strict";const e=/^[^.]+(?:\.[^.]+)*$/;function t(e){if("string"==typeof e)return JSON.stringify(e);if("bigint"==typeof e)return`${e}n`;if(null===e)return"null";if("object"==typeof e||"function"==typeof e)try{return Object.prototype.toString.call(e)}catch(e){return"an object"}return String(e)}function n(e){const t=e.indexOf(".");return-1===t?[e]:[e.slice(0,t),e.slice(t+1)]}const r=[];function o(e){let t,n=!1;for(const r of e)void 0===r?n=!0:(null!=t?t:t=[]).push(r);return{keepKey:n,pathsBelow:t?s(t):r,scope:void 0}}function s(e){return 1===e.length?e:[...new Set(e)].sort()}function c(e,t){const r=[];let s;for(const t of e){const[e,o]=n(t);if("*"===e)r.push(o);else{null!=s||(s=new Map);const t=s.get(e);t?t.push(o):s.set(e,[o])}}const c=new Map;for(const[e,t]of null!=s?s:[])c.set(e,o(r.length?[...t,...r]:t));const{targetCase:u,depth:i,keepReferences:a}=t.plain.options,f={targetCase:u,depth:i,keepReferences:a,exclude:null},l={options:f,visitedRefs:[],named:c,other:o(r),root:t};return f.exclude=l,l}function u(e,t){const n=JSON.stringify(e);let r=t.states.get(n);return r||(r=c(e,t),t.states.set(n,r)),r}function i(n,r){if(null==n)return null;if(!Array.isArray(n))throw new TypeError(`Unsupported exclude ${t(n)}. Use an array of paths, such as ["metadata.*"].`);for(const r of n)if("string"!=typeof r||!e.test(r))throw new TypeError(`Unsupported exclude path ${t(r)}. Use non-empty keys joined with dots, such as "metadata.*".`);if(0===n.length)return null;const o={plain:{options:r,visitedRefs:[]},states:new Map};return c(s(n),o)}function a(e,t){const n="string"==typeof t?e.named.get(t):void 0;return null!=n?n:e.other}function f(e,t){var n;return null!==(n=t.scope)&&void 0!==n?n:t.scope=0===t.pathsBelow.length?e.root.plain:u(t.pathsBelow,e.root)}const l="[object Object]";function p(e){try{return Object.prototype.toString.call(e)}catch(e){return l}}function d(e,t){if(!(e instanceof Map)&&"[object Map]"!==t)return!1;try{return Map.prototype.has.call(e,void 0),!0}catch(e){return!1}}function h(e,t){if(!(e instanceof Set)&&"[object Set]"!==t)return!1;try{return Set.prototype.has.call(e,void 0),!0}catch(e){return!1}}function y(e,t,n){"__proto__"===t?Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[t]=n}const g={};let w=!1;function v(e,t){const n=e.get(t);if(void 0!==n||!w)return n;let r=e.get(g);for(;void 0!==r;){const e=r.get(t);if(void 0!==e)return e;r=r.get(g)}}function b(e,t,n,r){let o=e[t];o.size>=16e6&&(w=!0,o=e[t]=new Map([[g,o]])),o.set(n,r)}function C(e){return e>=48&&e<=57}function U(e){return e>=97&&e<=122}function m(e){return e>=65&&e<=90}function k(e){if(36!==e.length)return!1;for(let t=0;t<36;t++){const n=e.charCodeAt(t);if(8===t||13===t||18===t||23===t){if(45!==n)return!1}else if(!(C(n)||n>=97&&n<=102||n>=65&&n<=70))return!1}return!0}const j="s".charCodeAt(0);function x(e,t){if(!m(e.charCodeAt(t)))return!1;const n=e.charCodeAt(t-1);if(U(n)||C(n))return!0;const r=e.charCodeAt(t+1);return!(!m(n)||!U(r))&&(r!==j||U(e.charCodeAt(t+2)))}function M(e){let t=1;for(;t<e.length&&!x(e,t);)t++;return e.slice(0,t).toLowerCase()+e.slice(t)}function O(e,t,n){return e.split(t).join(n)}function A(e,t,n){let r=n?e[0].toUpperCase():e[0],o=1,s=e.indexOf(t,1);for(;-1!==s;)r+=e.slice(o,s)+e[s+1].toUpperCase(),o=s+2,s=e.indexOf(t,o);return r+e.slice(o)}function _(e,t){const n=m(e.charCodeAt(0));switch(t){case"pascal":return n?e:e[0].toUpperCase()+e.slice(1);case"camel":return n?e[0].toLowerCase()+e.slice(1):e;case"constant":return e.toUpperCase();default:return n?e.toLowerCase():e}}function R(e,t,n){let r="",o=0;for(let n=1;n<e.length;n++)x(e,n)&&(r+=e.slice(o,n)+t,o=n);return r+=e.slice(o),n?r.toUpperCase():r.toLowerCase()}function S(e,t){const n=e.length;if(0===n)return e;let r=e.charCodeAt(0);const o=m(r);if(!o&&!U(r))return e;let s=o?1:0,c=!o,u=0,i=0,a=!1,f=r;for(let t=1;t<n;t++){if(r=e.charCodeAt(t),U(r))c=!0;else if(m(r))s++;else if(95===r)u++,95===f&&(a=!0);else if(45===r)i++,45===f&&(a=!0);else if(!C(r))return e;f=r}if(95===f||45===f)return e;if(0===u&&0===i){if(0===s||1===s&&o)return _(e,t);if(!o)switch(t){case"snake":return R(e,"_",!1);case"kebab":return R(e,"-",!1);case"pascal":return e[0].toUpperCase()+e.slice(1);case"constant":return R(e,"_",!0);default:return e}if(!c)return e;switch(t){case"snake":return R(e,"_",!1);case"kebab":return R(e,"-",!1);case"camel":return M(e);case"constant":return R(e,"_",!0);default:return e}}if(s>0||a||u>0&&i>0)return e;if(u>0)switch(t){case"camel":return A(e,"_",!1);case"pascal":return A(e,"_",!0);case"kebab":return O(e,"_","-");case"constant":return e.toUpperCase();default:return e}if(k(e))return e;switch(t){case"camel":return A(e,"-",!1);case"pascal":return A(e,"-",!0);case"snake":return O(e,"-","_");case"constant":return O(e,"-","_").toUpperCase();default:return e}}const $=new Map;let E,T={current:new Map,previous:new Map},N=0;function K(e,t){if(e.length>100)return S(e,t);if(t!==E){let e=$.get(t);e||(e={current:new Map,previous:new Map},$.set(t,e)),E=t,T=e}const n=T;let r=n.current.get(e);if(void 0!==r)return N=0,r;if(r=n.previous.get(e),void 0===r){if(r=S(e,t),++N>256&&N%8!=0)return r}else N=0;return n.current.size>=1e3&&(n.previous=n.current,n.current=new Map),n.current.set(e,r),r}const L={reason:"nesting limit reached"};function P(e,t,n=[],r=0,o=0){var s;if(null===e||"object"!=typeof e)return e;if(r>=t.depth)return e;const c=t.depth===1/0?0:r,u=t.keepReferences;if(u){const t=v(null!==(s=n[c])&&void 0!==s?s:n[c]=new Map,e);if(void 0!==t)return t}else if(o>=256)throw L;if(Array.isArray(e)){const s=[];return u&&b(n,c,e,s),B(e,s,t,n,r,o),s}const i=r+1,a=p(e);if(d(e,a)){const r=new Map;return u&&b(n,c,e,r),null===t.exclude?z(e,r,t,n,i,o):D(e,r,t.exclude,i,o),r}if(h(e,a)){const r=new Set;return u&&b(n,c,e,r),J(e,r,t,n,i,o),r}if(a===l){const r=e,s={};if(u&&b(n,c,e,s),null===t.exclude){for(const e in r)if(Object.prototype.hasOwnProperty.call(r,e)){const c=K(e,t.targetCase),u=r[e];if("object"!=typeof u||null===u){y(s,c,u);continue}y(s,c,P(u,t,n,i,o+1))}}else q(r,s,t.exclude,i,o);for(const e of Object.getOwnPropertySymbols(r))s[e]=r[e];return s}return e}function B(e,t,n,r,o,s){for(const c of e)"object"==typeof c&&null!==c?t.push(P(c,n,r,o,s+1)):t.push(c)}function z(e,t,n,r,o,s){for(const[c,u]of e)t.set("string"==typeof c?S(c,n.targetCase):c,P(u,n,r,o,s+1))}function J(e,t,n,r,o,s){for(const c of e)t.add(P(c,n,r,o,s+1))}function q(e,t,n,r,o){for(const s in e)if(Object.prototype.hasOwnProperty.call(e,s)){const c=a(n,s),u=f(n,c);y(t,c.keepKey?s:K(s,n.options.targetCase),P(e[s],u.options,u.visitedRefs,r,o+1))}}function D(e,t,n,r,o){for(const[s,c]of e){const e=a(n,s),u=f(n,e);t.set("string"!=typeof s||e.keepKey?s:S(s,n.options.targetCase),P(c,u.options,u.visitedRefs,r,o+1))}}const F=["camel","snake","pascal","kebab","constant"];function G(e){const n=e||"camel";if(r=n,!F.includes(r))throw new TypeError(`Unsupported targetCase ${t(n)}. Use one of: ${F.join(", ")}.`);var r;return n}function H(e,t,n){const r=void 0===n?null:i(n,t);return null===r?P(e,t):P(e,r.options,r.visitedRefs)}exports.transform=function(e,n){var r,o;const s=null==n?void 0:n.targetCase,c=null!==(r=null==n?void 0:n.depth)&&void 0!==r?r:1/0,u=G(s);if("number"!=typeof c||Number.isNaN(c))throw new TypeError(`Unsupported depth ${t(c)}. Use a number, or omit depth to transform every level.`);const i=null==n?void 0:n.exclude,a=null===(o=null==n?void 0:n.keepReferences)||void 0===o||o;if("boolean"!=typeof a)throw new TypeError(`Unsupported keepReferences ${t(a)}. Use true or false.`);if(!a)try{return H(e,{targetCase:u,depth:c,keepReferences:a,exclude:null},i)}catch(e){if(e!==L)throw e}return H(e,{targetCase:u,depth:c,keepReferences:!0,exclude:null},i)},exports.transformKey=function(e,n){if("string"!=typeof e)throw new TypeError(`Unsupported key ${t(e)}. Use a string.`);if("string"==typeof n)throw new TypeError(`Unsupported options ${t(n)}. Use an options object: { targetCase: ${t(n)} }.`);return S(e,G(null==n?void 0:n.targetCase))};
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- const e=/^[^.]+(?:\.[^.]+)*$/;function t(e){if("string"==typeof e)return JSON.stringify(e);if("bigint"==typeof e)return`${e}n`;if(null===e)return"null";if("object"==typeof e||"function"==typeof e)try{return Object.prototype.toString.call(e)}catch(e){return"an object"}return String(e)}function n(e){const t=e.indexOf(".");return-1===t?[e]:[e.slice(0,t),e.slice(t+1)]}const r=[];function o(e){let t,n=!1;for(const r of e)void 0===r?n=!0:(null!=t?t:t=[]).push(r);return{keepKey:n,pathsBelow:t?s(t):r,scope:void 0}}function s(e){return 1===e.length?e:[...new Set(e)].sort()}function c(e,t){const r=[];let s;for(const t of e){const[e,o]=n(t);if("*"===e)r.push(o);else{null!=s||(s=new Map);const t=s.get(e);t?t.push(o):s.set(e,[o])}}const c=new Map;for(const[e,t]of null!=s?s:[])c.set(e,o(r.length?[...t,...r]:t));const{targetCase:i,depth:u,keepReferences:f}=t.plain.options,a={targetCase:i,depth:u,keepReferences:f,exclude:null},l={options:a,visitedRefs:[],named:c,other:o(r),root:t};return a.exclude=l,l}function i(e,t){const n="string"==typeof t?e.named.get(t):void 0;return null!=n?n:e.other}function u(e,t){var n;return null!==(n=t.scope)&&void 0!==n?n:t.scope=0===t.pathsBelow.length?e.root.plain:function(e,t){const n=JSON.stringify(e);let r=t.states.get(n);return r||(r=c(e,t),t.states.set(n,r)),r}(t.pathsBelow,e.root)}const f="[object Object]";function a(e,t,n){"__proto__"===t?Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[t]=n}const l={};let p=!1;function d(e,t,n,r){let o=e[t];o.size>=16e6&&(p=!0,o=e[t]=new Map([[l,o]])),o.set(n,r)}function h(e){return e>=48&&e<=57}function y(e){return e>=97&&e<=122}function g(e){return e>=65&&e<=90}const w="s".charCodeAt(0);function v(e,t){if(!g(e.charCodeAt(t)))return!1;const n=e.charCodeAt(t-1);if(y(n)||h(n))return!0;const r=e.charCodeAt(t+1);return!(!g(n)||!y(r))&&(r!==w||y(e.charCodeAt(t+2)))}function b(e,t,n){return e.split(t).join(n)}function C(e,t,n){let r=n?e[0].toUpperCase():e[0],o=1,s=e.indexOf(t,1);for(;-1!==s;)r+=e.slice(o,s)+e[s+1].toUpperCase(),o=s+2,s=e.indexOf(t,o);return r+e.slice(o)}function k(e,t){let n="",r=0;for(let o=1;o<e.length;o++)v(e,o)&&(n+=e.slice(r,o)+t,r=o);return(n+e.slice(r)).toLowerCase()}function m(e,t){const n=e.length;if(0===n)return e;let r=e.charCodeAt(0);const o=g(r);if(!o&&!y(r))return e;let s=o?1:0,c=!o,i=0,u=0,f=!1,a=r;for(let t=1;t<n;t++){if(r=e.charCodeAt(t),y(r))c=!0;else if(g(r))s++;else if(95===r)i++,95===a&&(f=!0);else if(45===r)u++,45===a&&(f=!0);else if(!h(r))return e;a=r}if(95===a||45===a)return e;if(0===i&&0===u){if(0===s||1===s&&o)return function(e,t){const n=g(e.charCodeAt(0));switch(t){case"pascal":return n?e:e[0].toUpperCase()+e.slice(1);case"camel":return n?e[0].toLowerCase()+e.slice(1):e;default:return n?e.toLowerCase():e}}(e,t);if(!o)switch(t){case"snake":return k(e,"_");case"kebab":return k(e,"-");case"pascal":return e[0].toUpperCase()+e.slice(1);default:return e}if(!c)return e;switch(t){case"snake":return k(e,"_");case"kebab":return k(e,"-");case"camel":return function(e){let t=1;for(;t<e.length&&!v(e,t);)t++;return e.slice(0,t).toLowerCase()+e.slice(t)}(e);default:return e}}if(s>0||f||i>0&&u>0)return e;if(i>0)switch(t){case"camel":return C(e,"_",!1);case"pascal":return C(e,"_",!0);case"kebab":return b(e,"_","-");default:return e}if(function(e){if(36!==e.length)return!1;for(let t=0;t<36;t++){const n=e.charCodeAt(t);if(8===t||13===t||18===t||23===t){if(45!==n)return!1}else if(!(h(n)||n>=97&&n<=102||n>=65&&n<=70))return!1}return!0}(e))return e;switch(t){case"camel":return C(e,"-",!1);case"pascal":return C(e,"-",!0);case"snake":return b(e,"-","_");default:return e}}const j=new Map;let U,x={current:new Map,previous:new Map},M=0;function O(e,t){if(e.length>100)return m(e,t);if(t!==U){let e=j.get(t);e||(e={current:new Map,previous:new Map},j.set(t,e)),U=t,x=e}const n=x;let r=n.current.get(e);if(void 0!==r)return M=0,r;if(r=n.previous.get(e),void 0===r){if(r=m(e,t),++M>256&&M%8!=0)return r}else M=0;return n.current.size>=1e3&&(n.previous=n.current,n.current=new Map),n.current.set(e,r),r}const A={reason:"nesting limit reached"};function R(e,t,n=[],r=0,o=0){var s;if(null===e||"object"!=typeof e)return e;if(r>=t.depth)return e;const c=t.depth===1/0?0:r,h=t.keepReferences;if(h){const t=function(e,t){const n=e.get(t);if(void 0!==n||!p)return n;let r=e.get(l);for(;void 0!==r;){const e=r.get(t);if(void 0!==e)return e;r=r.get(l)}}(null!==(s=n[c])&&void 0!==s?s:n[c]=new Map,e);if(void 0!==t)return t}else if(o>=256)throw A;if(Array.isArray(e)){const s=[];return h&&d(n,c,e,s),function(e,t,n,r,o,s){for(const c of e)t.push(null!==c&&"object"==typeof c?R(c,n,r,o,s+1):c)}(e,s,t,n,r,o),s}const y=r+1,g=function(e){try{return Object.prototype.toString.call(e)}catch(e){return f}}(e);if(function(e,t){if(!(e instanceof Map)&&"[object Map]"!==t)return!1;try{return Map.prototype.has.call(e,void 0),!0}catch(e){return!1}}(e,g)){const r=new Map;return h&&d(n,c,e,r),null===t.exclude?function(e,t,n,r,o,s){for(const[c,i]of e)t.set("string"==typeof c?m(c,n.targetCase):c,R(i,n,r,o,s+1))}(e,r,t,n,y,o):function(e,t,n,r,o){for(const[s,c]of e){const e=i(n,s),f=u(n,e);t.set("string"!=typeof s||e.keepKey?s:m(s,n.options.targetCase),R(c,f.options,f.visitedRefs,r,o+1))}}(e,r,t.exclude,y,o),r}if(function(e,t){if(!(e instanceof Set)&&"[object Set]"!==t)return!1;try{return Set.prototype.has.call(e,void 0),!0}catch(e){return!1}}(e,g)){const r=new Set;return h&&d(n,c,e,r),function(e,t,n,r,o,s){for(const c of e)t.add(R(c,n,r,o,s+1))}(e,r,t,n,y,o),r}if(g===f){const r=e,s={};if(h&&d(n,c,e,s),null===t.exclude)for(const e in r)Object.prototype.hasOwnProperty.call(r,e)&&a(s,O(e,t.targetCase),R(r[e],t,n,y,o+1));else!function(e,t,n,r,o){for(const s in e)if(Object.prototype.hasOwnProperty.call(e,s)){const c=i(n,s),f=u(n,c);a(t,c.keepKey?s:O(s,n.options.targetCase),R(e[s],f.options,f.visitedRefs,r,o+1))}}(r,s,t.exclude,y,o);for(const e of Object.getOwnPropertySymbols(r))s[e]=r[e];return s}return e}const S=["camel","snake","pascal","kebab"];function $(e){const n=e||"camel";if(r=n,!S.includes(r))throw new TypeError(`Unsupported targetCase ${t(n)}. Use one of: ${S.join(", ")}.`);var r;return n}function _(e,n){var r,o;const s=null==n?void 0:n.targetCase,c=null!==(r=null==n?void 0:n.depth)&&void 0!==r?r:1/0,i=$(s);if("number"!=typeof c||Number.isNaN(c))throw new TypeError(`Unsupported depth ${t(c)}. Use a number, or omit depth to transform every level.`);const u=null==n?void 0:n.exclude,f=null===(o=null==n?void 0:n.keepReferences)||void 0===o||o;if("boolean"!=typeof f)throw new TypeError(`Unsupported keepReferences ${t(f)}. Use true or false.`);if(!f)try{return E(e,{targetCase:i,depth:c,keepReferences:f,exclude:null},u)}catch(e){if(e!==A)throw e}return E(e,{targetCase:i,depth:c,keepReferences:!0,exclude:null},u)}function E(n,r,o){const i=void 0===o?null:function(n,r){if(null==n)return null;if(!Array.isArray(n))throw new TypeError(`Unsupported exclude ${t(n)}. Use an array of paths, such as ["metadata.*"].`);for(const r of n)if("string"!=typeof r||!e.test(r))throw new TypeError(`Unsupported exclude path ${t(r)}. Use non-empty keys joined with dots, such as "metadata.*".`);if(0===n.length)return null;const o={plain:{options:r,visitedRefs:[]},states:new Map};return c(s(n),o)}(o,r);return null===i?R(n,r):R(n,i.options,i.visitedRefs)}function T(e,n){if("string"!=typeof e)throw new TypeError(`Unsupported key ${t(e)}. Use a string.`);if("string"==typeof n)throw new TypeError(`Unsupported options ${t(n)}. Use an options object: { targetCase: ${t(n)} }.`);return m(e,$(null==n?void 0:n.targetCase))}export{_ as transform,T as transformKey};
1
+ const e=/^[^.]+(?:\.[^.]+)*$/;function t(e){if("string"==typeof e)return JSON.stringify(e);if("bigint"==typeof e)return`${e}n`;if(null===e)return"null";if("object"==typeof e||"function"==typeof e)try{return Object.prototype.toString.call(e)}catch(e){return"an object"}return String(e)}function n(e){const t=e.indexOf(".");return-1===t?[e]:[e.slice(0,t),e.slice(t+1)]}const r=[];function o(e){let t,n=!1;for(const r of e)void 0===r?n=!0:(null!=t?t:t=[]).push(r);return{keepKey:n,pathsBelow:t?s(t):r,scope:void 0}}function s(e){return 1===e.length?e:[...new Set(e)].sort()}function c(e,t){const r=[];let s;for(const t of e){const[e,o]=n(t);if("*"===e)r.push(o);else{null!=s||(s=new Map);const t=s.get(e);t?t.push(o):s.set(e,[o])}}const c=new Map;for(const[e,t]of null!=s?s:[])c.set(e,o(r.length?[...t,...r]:t));const{targetCase:u,depth:i,keepReferences:a}=t.plain.options,f={targetCase:u,depth:i,keepReferences:a,exclude:null},l={options:f,visitedRefs:[],named:c,other:o(r),root:t};return f.exclude=l,l}function u(e,t){const n=JSON.stringify(e);let r=t.states.get(n);return r||(r=c(e,t),t.states.set(n,r)),r}function i(n,r){if(null==n)return null;if(!Array.isArray(n))throw new TypeError(`Unsupported exclude ${t(n)}. Use an array of paths, such as ["metadata.*"].`);for(const r of n)if("string"!=typeof r||!e.test(r))throw new TypeError(`Unsupported exclude path ${t(r)}. Use non-empty keys joined with dots, such as "metadata.*".`);if(0===n.length)return null;const o={plain:{options:r,visitedRefs:[]},states:new Map};return c(s(n),o)}function a(e,t){const n="string"==typeof t?e.named.get(t):void 0;return null!=n?n:e.other}function f(e,t){var n;return null!==(n=t.scope)&&void 0!==n?n:t.scope=0===t.pathsBelow.length?e.root.plain:u(t.pathsBelow,e.root)}const l="[object Object]";function p(e){try{return Object.prototype.toString.call(e)}catch(e){return l}}function d(e,t){if(!(e instanceof Map)&&"[object Map]"!==t)return!1;try{return Map.prototype.has.call(e,void 0),!0}catch(e){return!1}}function h(e,t){if(!(e instanceof Set)&&"[object Set]"!==t)return!1;try{return Set.prototype.has.call(e,void 0),!0}catch(e){return!1}}function y(e,t,n){"__proto__"===t?Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[t]=n}const g={};let w=!1;function v(e,t){const n=e.get(t);if(void 0!==n||!w)return n;let r=e.get(g);for(;void 0!==r;){const e=r.get(t);if(void 0!==e)return e;r=r.get(g)}}function b(e,t,n,r){let o=e[t];o.size>=16e6&&(w=!0,o=e[t]=new Map([[g,o]])),o.set(n,r)}function C(e){return e>=48&&e<=57}function U(e){return e>=97&&e<=122}function k(e){return e>=65&&e<=90}function j(e){if(36!==e.length)return!1;for(let t=0;t<36;t++){const n=e.charCodeAt(t);if(8===t||13===t||18===t||23===t){if(45!==n)return!1}else if(!(C(n)||n>=97&&n<=102||n>=65&&n<=70))return!1}return!0}const m="s".charCodeAt(0);function x(e,t){if(!k(e.charCodeAt(t)))return!1;const n=e.charCodeAt(t-1);if(U(n)||C(n))return!0;const r=e.charCodeAt(t+1);return!(!k(n)||!U(r))&&(r!==m||U(e.charCodeAt(t+2)))}function M(e){let t=1;for(;t<e.length&&!x(e,t);)t++;return e.slice(0,t).toLowerCase()+e.slice(t)}function O(e,t,n){return e.split(t).join(n)}function A(e,t,n){let r=n?e[0].toUpperCase():e[0],o=1,s=e.indexOf(t,1);for(;-1!==s;)r+=e.slice(o,s)+e[s+1].toUpperCase(),o=s+2,s=e.indexOf(t,o);return r+e.slice(o)}function _(e,t){const n=k(e.charCodeAt(0));switch(t){case"pascal":return n?e:e[0].toUpperCase()+e.slice(1);case"camel":return n?e[0].toLowerCase()+e.slice(1):e;case"constant":return e.toUpperCase();default:return n?e.toLowerCase():e}}function R(e,t,n){let r="",o=0;for(let n=1;n<e.length;n++)x(e,n)&&(r+=e.slice(o,n)+t,o=n);return r+=e.slice(o),n?r.toUpperCase():r.toLowerCase()}function S(e,t){const n=e.length;if(0===n)return e;let r=e.charCodeAt(0);const o=k(r);if(!o&&!U(r))return e;let s=o?1:0,c=!o,u=0,i=0,a=!1,f=r;for(let t=1;t<n;t++){if(r=e.charCodeAt(t),U(r))c=!0;else if(k(r))s++;else if(95===r)u++,95===f&&(a=!0);else if(45===r)i++,45===f&&(a=!0);else if(!C(r))return e;f=r}if(95===f||45===f)return e;if(0===u&&0===i){if(0===s||1===s&&o)return _(e,t);if(!o)switch(t){case"snake":return R(e,"_",!1);case"kebab":return R(e,"-",!1);case"pascal":return e[0].toUpperCase()+e.slice(1);case"constant":return R(e,"_",!0);default:return e}if(!c)return e;switch(t){case"snake":return R(e,"_",!1);case"kebab":return R(e,"-",!1);case"camel":return M(e);case"constant":return R(e,"_",!0);default:return e}}if(s>0||a||u>0&&i>0)return e;if(u>0)switch(t){case"camel":return A(e,"_",!1);case"pascal":return A(e,"_",!0);case"kebab":return O(e,"_","-");case"constant":return e.toUpperCase();default:return e}if(j(e))return e;switch(t){case"camel":return A(e,"-",!1);case"pascal":return A(e,"-",!0);case"snake":return O(e,"-","_");case"constant":return O(e,"-","_").toUpperCase();default:return e}}const $=new Map;let E,T={current:new Map,previous:new Map},N=0;function L(e,t){if(e.length>100)return S(e,t);if(t!==E){let e=$.get(t);e||(e={current:new Map,previous:new Map},$.set(t,e)),E=t,T=e}const n=T;let r=n.current.get(e);if(void 0!==r)return N=0,r;if(r=n.previous.get(e),void 0===r){if(r=S(e,t),++N>256&&N%8!=0)return r}else N=0;return n.current.size>=1e3&&(n.previous=n.current,n.current=new Map),n.current.set(e,r),r}const P={reason:"nesting limit reached"};function B(e,t,n=[],r=0,o=0){var s;if(null===e||"object"!=typeof e)return e;if(r>=t.depth)return e;const c=t.depth===1/0?0:r,u=t.keepReferences;if(u){const t=v(null!==(s=n[c])&&void 0!==s?s:n[c]=new Map,e);if(void 0!==t)return t}else if(o>=256)throw P;if(Array.isArray(e)){const s=[];return u&&b(n,c,e,s),K(e,s,t,n,r,o),s}const i=r+1,a=p(e);if(d(e,a)){const r=new Map;return u&&b(n,c,e,r),null===t.exclude?z(e,r,t,n,i,o):D(e,r,t.exclude,i,o),r}if(h(e,a)){const r=new Set;return u&&b(n,c,e,r),J(e,r,t,n,i,o),r}if(a===l){const r=e,s={};if(u&&b(n,c,e,s),null===t.exclude){for(const e in r)if(Object.prototype.hasOwnProperty.call(r,e)){const c=L(e,t.targetCase),u=r[e];if("object"!=typeof u||null===u){y(s,c,u);continue}y(s,c,B(u,t,n,i,o+1))}}else q(r,s,t.exclude,i,o);for(const e of Object.getOwnPropertySymbols(r))s[e]=r[e];return s}return e}function K(e,t,n,r,o,s){for(const c of e)"object"==typeof c&&null!==c?t.push(B(c,n,r,o,s+1)):t.push(c)}function z(e,t,n,r,o,s){for(const[c,u]of e)t.set("string"==typeof c?S(c,n.targetCase):c,B(u,n,r,o,s+1))}function J(e,t,n,r,o,s){for(const c of e)t.add(B(c,n,r,o,s+1))}function q(e,t,n,r,o){for(const s in e)if(Object.prototype.hasOwnProperty.call(e,s)){const c=a(n,s),u=f(n,c);y(t,c.keepKey?s:L(s,n.options.targetCase),B(e[s],u.options,u.visitedRefs,r,o+1))}}function D(e,t,n,r,o){for(const[s,c]of e){const e=a(n,s),u=f(n,e);t.set("string"!=typeof s||e.keepKey?s:S(s,n.options.targetCase),B(c,u.options,u.visitedRefs,r,o+1))}}const F=["camel","snake","pascal","kebab","constant"];function G(e){const n=e||"camel";if(r=n,!F.includes(r))throw new TypeError(`Unsupported targetCase ${t(n)}. Use one of: ${F.join(", ")}.`);var r;return n}function H(e,n){var r,o;const s=null==n?void 0:n.targetCase,c=null!==(r=null==n?void 0:n.depth)&&void 0!==r?r:1/0,u=G(s);if("number"!=typeof c||Number.isNaN(c))throw new TypeError(`Unsupported depth ${t(c)}. Use a number, or omit depth to transform every level.`);const i=null==n?void 0:n.exclude,a=null===(o=null==n?void 0:n.keepReferences)||void 0===o||o;if("boolean"!=typeof a)throw new TypeError(`Unsupported keepReferences ${t(a)}. Use true or false.`);if(!a)try{return I(e,{targetCase:u,depth:c,keepReferences:a,exclude:null},i)}catch(e){if(e!==P)throw e}return I(e,{targetCase:u,depth:c,keepReferences:!0,exclude:null},i)}function I(e,t,n){const r=void 0===n?null:i(n,t);return null===r?B(e,t):B(e,r.options,r.visitedRefs)}function Q(e,n){if("string"!=typeof e)throw new TypeError(`Unsupported key ${t(e)}. Use a string.`);if("string"==typeof n)throw new TypeError(`Unsupported options ${t(n)}. Use an options object: { targetCase: ${t(n)} }.`);return S(e,G(null==n?void 0:n.targetCase))}export{H as transform,Q as transformKey};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "deep-case-crafter",
3
- "version": "3.3.0",
4
- "description": "Convert object keys between camelCase, snake_case, kebab-case and PascalCase, deeply (objects, arrays, Map, Set), with exact TypeScript types",
3
+ "version": "3.4.1",
4
+ "description": "Convert object keys between camelCase, snake_case, kebab-case and PascalCase, or to CONSTANT_CASE, deeply (objects, arrays, Map, Set), with exact TypeScript types",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -36,12 +36,14 @@
36
36
  "snake_case",
37
37
  "kebab-case",
38
38
  "PascalCase",
39
+ "CONSTANT_CASE",
39
40
  "camelcase",
40
41
  "snakecase",
41
42
  "kebabcase",
42
43
  "pascalcase",
43
44
  "camel-case",
44
45
  "snake-case",
46
+ "constant-case",
45
47
  "case-convert",
46
48
  "convert-case",
47
49
  "case-conversion",
package/readme.md CHANGED
@@ -9,7 +9,8 @@
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/gyrgy/deep-case-crafter/blob/main/LICENSE)
10
10
 
11
11
  Converts the keys of nested objects, arrays, Maps and Sets between camelCase,
12
- snake_case, kebab-case and PascalCase, and types the result exactly.
12
+ snake_case, kebab-case and PascalCase, or to CONSTANT_CASE, and types the result
13
+ exactly.
13
14
 
14
15
  ```typescript
15
16
  import { transform } from 'deep-case-crafter';
@@ -34,6 +35,9 @@ const body = transform(user, { targetCase: 'snake' });
34
35
  keys in different cases can be mixed in one input.
35
36
  - **Acronym-aware**: `userID` → `user_id`, `HTTPResponse` → `http_response` or
36
37
  `httpResponse`.
38
+ - **CONSTANT_CASE output**: `targetCase: 'constant'` turns `userID` into `USER_ID` and
39
+ `HTTPResponse` into `HTTP_RESPONSE`. It is an output case only: the other cases leave
40
+ `USER_ID` as it is.
37
41
  - **Deep**: objects, arrays, `Map` and `Set`, down to an optional `depth`, circular
38
42
  references included. `Date`, `RegExp`, typed arrays and other built-in values are
39
43
  returned untouched.
@@ -89,6 +93,10 @@ console.log(pascalCaseResult);
89
93
  const kebabCaseResult = transform(input, { targetCase: 'kebab' });
90
94
  console.log(kebabCaseResult);
91
95
  // Output: { "user-id": 1, "first-name": "John" }
96
+
97
+ const constantCaseResult = transform(input, { targetCase: 'constant' });
98
+ console.log(constantCaseResult);
99
+ // Output: { USER_ID: 1, FIRST_NAME: 'John' }
92
100
  ```
93
101
 
94
102
  ---
@@ -193,26 +201,28 @@ typed alternative), [camelcase-keys](https://www.npmjs.com/package/camelcase-key
193
201
  `toSnakeCaseKeys`). Plain snake_case keys such as `user_id` become the same camelCase
194
202
  keys in all of them. The table shows where they differ:
195
203
 
196
- | | deep-case-crafter | ts-case-convert | camelcase-keys, snakecase-keys | es-toolkit |
197
- | -------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------- |
198
- | `userID` to snake_case | `user_id` | `user_i_d` | `user_id` | `user_id`, typed `user_i_d` |
199
- | `HTTPResponse` to snake_case | `http_response` | `h_t_t_p_response` | `http_response` | `http_response`, typed `h_t_t_p_response` |
200
- | `isAValidUser` to snake_case | `is_a_valid_user` | `is_a_valid_user` | `is_a_valid_user` | `is_a_valid_user` |
201
- | `addressLine1` to snake_case | `address_line1` | `address_line_1`, or `address_line1` with `no-split-numbers` | `address_line1` | `address_line_1`, typed `address_line1` |
202
- | Objects inside a `Map` or `Set` | keys converted | outside its documented scope | outside their documented scope | left as they are, as documented |
203
- | Result type for `await res.json()`, an `any` input | `any` | a union that includes `Date` and `null`, so `.userId` does not compile | `any` | `any` |
204
- | A `readonly string[]` field in the result type | `readonly string[]` | an object type whose `.map()` does not compile | `readonly string[]` | an object type: `.map()` compiles, `[0]` and `for...of` do not |
205
- | A `[number, number]` field in the result type | `[number, number]` | `number[]` | `[number, number]` | `number[]` |
206
- | Leaving chosen keys unchanged | `exclude` paths, such as `'metadata.*'` | no option | `exclude` keys by name or RegExp; camelcase-keys also `stopPaths` | no option |
207
- | Target cases | camel, snake, kebab, Pascal | camel, snake, Pascal, SCREAMING_SNAKE | camel and Pascal; snake | camel, snake, kebab, Pascal, SCREAMING_SNAKE |
204
+ | | deep-case-crafter | ts-case-convert | camelcase-keys, snakecase-keys | es-toolkit |
205
+ | -------------------------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------- |
206
+ | `userID` to snake_case | `user_id` | `user_i_d` | `user_id` | `user_id`, typed `user_i_d` |
207
+ | `HTTPResponse` to snake_case | `http_response` | `h_t_t_p_response` | `http_response` | `http_response`, typed `h_t_t_p_response` |
208
+ | `isAValidUser` to snake_case | `is_a_valid_user` | `is_a_valid_user` | `is_a_valid_user` | `is_a_valid_user` |
209
+ | `addressLine1` to snake_case | `address_line1` | `address_line_1`, or `address_line1` with `no-split-numbers` | `address_line1` | `address_line_1`, typed `address_line1` |
210
+ | Objects inside a `Map` or `Set` | keys converted | outside its documented scope | outside their documented scope | left as they are, as documented |
211
+ | Result type for `await res.json()`, an `any` input | `any` | a union that includes `Date` and `null`, so `.userId` does not compile | `any` | `any` |
212
+ | A `readonly string[]` field in the result type | `readonly string[]` | an object type whose `.map()` does not compile | `readonly string[]` | an object type: `.map()` compiles, `[0]` and `for...of` do not |
213
+ | A `[number, number]` field in the result type | `[number, number]` | `number[]` | `[number, number]` | `number[]` |
214
+ | Leaving chosen keys unchanged | `exclude` paths, such as `'metadata.*'` | no option | `exclude` keys by name or RegExp; camelcase-keys also `stopPaths` | no option |
215
+ | Target cases | camel, snake, kebab, Pascal, SCREAMING_SNAKE | camel, snake, Pascal, SCREAMING_SNAKE | camel and Pascal; snake | camel, snake, kebab, Pascal, SCREAMING_SNAKE |
208
216
 
209
217
  Where the others are better or different:
210
218
 
211
219
  - **Bundle size**: bundled and minified with esbuild, ts-case-convert's `objectToCamel`
212
220
  and `objectToSnake` take about 1.1 KB gzipped, deep-case-crafter's `transform` about
213
221
  2.8 KB.
214
- - **More output**: ts-case-convert and es-toolkit also convert to SCREAMING_SNAKE_CASE,
215
- which deep-case-crafter does not.
222
+ - **CONSTANT_CASE input**: camelcase-keys, snakecase-keys and es-toolkit also read
223
+ SCREAMING_SNAKE_CASE keys, so `USER_ID` becomes `userId` or `user_id`.
224
+ deep-case-crafter converts to CONSTANT_CASE, but leaves keys such as `USER_ID` and
225
+ `ID` unchanged (see [Key Preservation Rules](#key-preservation-rules)).
216
226
  - **Single strings**: like `transformKey`, ts-case-convert's `toSnake('userId')` and
217
227
  es-toolkit's `snakeCase('userId')` return `'user_id'`. es-toolkit's functions also
218
228
  convert text, such as `'Hello World'` → `'hello_world'`, which `transformKey` returns
@@ -231,8 +241,8 @@ Where the others are better or different:
231
241
 
232
242
  ## Acronym-aware conversion
233
243
 
234
- When converting **to** `snake_case` or `kebab-case`, runs of capitals (acronyms)
235
- are treated as whole words instead of being split letter-by-letter:
244
+ When converting **to** `snake_case`, `kebab-case` or `CONSTANT_CASE`, runs of capitals
245
+ (acronyms) are treated as whole words instead of being split letter-by-letter:
236
246
 
237
247
  ```typescript
238
248
  const input = { getHTTPResponse: 1, userID: 2, parseHTMLString: 3 };
@@ -276,7 +286,7 @@ console.log(result);
276
286
  ```
277
287
 
278
288
  Words of two or more capitals, such as `ID`, `USD` or `GET`, are left unchanged, like
279
- `SCREAMING_SNAKE` keys (see [Key Preservation Rules](#key-preservation-rules)). A single
289
+ `CONSTANT_CASE` keys (see [Key Preservation Rules](#key-preservation-rules)). A single
280
290
  capital, with or without digits (`S`, `S3`), is converted as a one-letter word.
281
291
 
282
292
  Converting back to `PascalCase` cannot restore a lowercased acronym: `httpResponse`
@@ -295,7 +305,7 @@ The result **types** match all of this, so `result.get_http_response` and
295
305
 
296
306
  - **data**: The data structure to transform (`object`, `array`, `Map`, or `Set`).
297
307
  - **options**:
298
- - `targetCase` (optional): `'camel' | 'snake' | 'pascal' | 'kebab'` (default: `'camel'`, also used for any falsy value such as `''`, `null` or `0`). Any other value throws a `TypeError`.
308
+ - `targetCase` (optional): `'camel' | 'snake' | 'pascal' | 'kebab' | 'constant'` (default: `'camel'`, also used for any falsy value such as `''`, `null` or `0`). Any other value throws a `TypeError`. `'constant'` is CONSTANT_CASE, also called SCREAMING_SNAKE_CASE: `userID` becomes `USER_ID`, split into words as for snake_case. It is a target case only: keys such as `USER_ID` or `ID` are left unchanged by every target case, so they do not convert back. A single capital, with or without digits, is still converted as a one-letter word: `'constant'` turns `p99` into `P99`, and camel, snake and kebab turn it back into `p99` (see [Key Preservation Rules](#key-preservation-rules)).
299
309
  - `depth` (optional): **How many nesting levels to transform** (default: `Infinity` — the whole structure; `null` means the same). `1` transforms only the top-level keys, `2` the keys one level down as well, and so on. The values of objects, Maps and Sets are one level deeper; array items stay on their array's level. A value that is not a number (`NaN`, `'3'`, `3n`) throws a `TypeError`.
300
310
  - `exclude` (optional): **Keys to leave unchanged**, as paths of keys joined with dots, such as `['users_by_id.*', 'metadata.*']` (default: none; `null` means the same). See [Leaving keys unchanged](#leaving-keys-unchanged). Anything but an array of such paths throws a `TypeError`.
301
311
  - `keepReferences` (optional): **Whether shared and circular references keep their shape** (default: `true`; `null` means the same). An object reached through several references then gives one result, and a circular reference points back into the result. Pass `false` for data that cannot contain such references, such as the output of `JSON.parse`, to skip that bookkeeping: inputs with many objects and arrays convert up to about twice as fast. Use it for tree-shaped data only: an object is then copied once for every path that leads to it, so shared objects inside shared objects multiply. Data with more than 256 nested objects, arrays, Maps and Sets on one path, as circular references always have, is converted again from the start with the bookkeeping, so it takes longer and getters can run twice. With a finite `depth`, circular references stop at the depth limit before that, and are copied once per path down to it. Anything but a boolean throws a `TypeError`.
@@ -369,11 +379,14 @@ type of the `exclude` paths, such as `['metadata.*']`.
369
379
  (without `sourceCase`), and options passed with only the input type given
370
380
  (`transform<Data>(data, options)`). Leave the type arguments out to get the precise
371
381
  type.
372
- - When a library exports a value or function built on `transform`, annotate it with
373
- `TransformResult<...>`. Otherwise the result type is written out into its declaration
374
- files: recursive types (trees, JSON values) are cut off as `any`, and very large types
375
- or symbol-keyed members fail to compile (TS7056, TS4118). Generic functions around
376
- `transform` are not affected.
382
+ - When a library exports a value or function built on `transform` or `transformKey`,
383
+ annotate its result with `TransformResult<...>` or `TransformKey<...>`. Otherwise the
384
+ result type is written out into its declaration files. For data of a fixed type,
385
+ recursive types (trees, JSON values) are then cut off as `any`, and very large types or
386
+ symbol-keyed members fail to compile (TS7056, TS4118). For a target case typed
387
+ `StringCase`, generic data included, the result can cover only the cases that existed
388
+ when the library was built, not cases added later. A generic target case, as in
389
+ `<C extends StringCase>(targetCase: C)`, is not affected.
377
390
  - Some values cannot be told apart by their types. `Error`, `URL`, boxed primitives,
378
391
  iterators, module namespace objects and other built-ins whose types do not declare a
379
392
  `Symbol.toStringTag` are returned untouched, but typed as plain objects with
@@ -398,15 +411,15 @@ transformKey('HTTPResponse', { targetCase: 'snake' }); // 'http_response'
398
411
  transformKey('Hello World', { targetCase: 'snake' }); // 'Hello World'
399
412
  ```
400
413
 
401
- It converts names, not text: a string that is not a key in one of the four cases is
402
- returned unchanged, such as `'Hello World'`, `'USER_ID'`, `'_id'` or a UUID (see
403
- [Key Preservation Rules](#key-preservation-rules)).
414
+ It converts names, not text: a string that is not a camelCase, PascalCase, snake_case or
415
+ kebab-case key is returned unchanged, such as `'Hello World'`, `'USER_ID'`, `'_id'` or a
416
+ UUID (see [Key Preservation Rules](#key-preservation-rules)).
404
417
 
405
418
  The result type is the converted key. A string literal gives a literal, a union of keys
406
419
  or of target cases gives the union of their results, and a `string` gives `string`.
407
420
  `TransformKey<Key, TargetCase>` names it: `TransformKey<'userID', 'snake'>` is
408
421
  `'user_id'`. Options that are not known exactly, such as an options object that may be
409
- `undefined`, give a wider type: `string`, or the key in any of the four cases.
422
+ `undefined`, give a wider type: `string`, or the key in any of the target cases.
410
423
 
411
424
  To use it in a callback, wrap it in a function:
412
425
  `keys.map((key) => transformKey(key, { targetCase: 'snake' }))`.
@@ -448,6 +461,10 @@ A key is left **completely unchanged** when it:
448
461
  - **is not written in one recognisable case**, e.g. `mixed_Case`, `USER_ID`, `a_b-c`, or
449
462
  is a word of two or more capitals, e.g. `ID`, `USD`, `B2B`.
450
463
 
464
+ CONSTANT_CASE keys such as `USER_ID` fall under the last rule, so every target case keeps
465
+ them: `'constant'` is a target case only. A single capital, with or without digits
466
+ (`X`, `P99`), is not kept: it is converted as a one-letter word.
467
+
451
468
  Trailing digits are kept but treated as part of the word, so they do **not** by
452
469
  themselves preserve a key — `address_1` becomes `address1`, and the single word
453
470
  `item2` stays `item2`.
@@ -496,8 +513,8 @@ Worth knowing:
496
513
  Measured with Node 24 on an Apple M4 Pro, with Node's default stack and memory sizes:
497
514
 
498
515
  - **Nesting depth.** The conversion is recursive. Depending on the shape, the first call
499
- in a process converts 1,300 to 2,400 levels of nesting (nested Maps the fewest, nested
500
- objects the most), and once the code is warm about 2,600 (nested arrays) to 4,400;
516
+ in a process converts 1,300 to 2,300 levels of nesting (nested Maps the fewest, nested
517
+ objects the most), and once the code is warm about 2,600 (nested arrays) to 5,200;
501
518
  deeper data throws a `RangeError`. API data nests far less, but untrusted input can nest
502
519
  deeper (`JSON.parse` accepts millions of levels), so catch the error where such input is
503
520
  converted.
@@ -518,7 +535,8 @@ stable. Key outputs change only to fix bugs, and every change is listed in the
518
535
  [changelog](https://github.com/gyrgy/deep-case-crafter/blob/main/CHANGELOG.md): if you
519
536
  store converted keys, such as database columns, check it before upgrading. A minor
520
537
  version can add an option, which `Required<TransformOptions>` and
521
- `Required<TransformKeyOptions>` then require.
538
+ `Required<TransformKeyOptions>` then require, or a target case, which code that handles
539
+ every member of `StringCase` then has to handle too.
522
540
 
523
541
  ### Upgrading to v3
524
542