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 +28 -14
- package/dist/index.d.mts +28 -14
- package/dist/index.d.ts +28 -14
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +4 -2
- package/readme.md +49 -31
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
|
-
/**
|
|
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
|
|
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
|
|
540
|
-
* UUID, is returned unchanged (see "Key Preservation
|
|
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
|
|
548
|
-
* `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
540
|
-
* UUID, is returned unchanged (see "Key Preservation
|
|
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
|
|
548
|
-
* `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
540
|
-
* UUID, is returned unchanged (see "Key Preservation
|
|
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
|
|
548
|
-
* `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key
|
|
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:
|
|
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:
|
|
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.
|
|
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
|
[](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
|
|
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
|
|
197
|
-
| -------------------------------------------------- |
|
|
198
|
-
| `userID` to snake_case | `user_id`
|
|
199
|
-
| `HTTPResponse` to snake_case | `http_response`
|
|
200
|
-
| `isAValidUser` to snake_case | `is_a_valid_user`
|
|
201
|
-
| `addressLine1` to snake_case | `address_line1`
|
|
202
|
-
| Objects inside a `Map` or `Set` | keys converted
|
|
203
|
-
| Result type for `await res.json()`, an `any` input | `any`
|
|
204
|
-
| A `readonly string[]` field in the result type | `readonly string[]`
|
|
205
|
-
| A `[number, number]` field in the result type | `[number, number]`
|
|
206
|
-
| Leaving chosen keys unchanged | `exclude` paths, such as `'metadata.*'`
|
|
207
|
-
| Target cases | camel, snake, kebab, Pascal
|
|
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
|
-
- **
|
|
215
|
-
|
|
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 `
|
|
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
|
-
`
|
|
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
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
|
402
|
-
returned unchanged, such as `'Hello World'`, `'USER_ID'`, `'_id'` or a
|
|
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
|
|
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,
|
|
500
|
-
objects the most), and once the code is warm about 2,600 (nested arrays) to
|
|
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
|
|