deep-case-crafter 3.1.0 → 3.2.0
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 +95 -11
- package/dist/index.d.mts +95 -11
- package/dist/index.d.ts +95 -11
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/readme.md +58 -7
package/dist/index.d.cts
CHANGED
|
@@ -27,14 +27,19 @@ type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab';
|
|
|
27
27
|
type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : never;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Options for the transform function
|
|
30
|
+
* Options for the transformKey function. The transform function takes them as well.
|
|
31
31
|
*/
|
|
32
|
-
interface
|
|
32
|
+
interface TransformKeyOptions {
|
|
33
33
|
/**
|
|
34
34
|
* The target casing format to transform to
|
|
35
35
|
* @default 'camel'
|
|
36
36
|
*/
|
|
37
37
|
targetCase?: StringCase | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Options for the transform function
|
|
41
|
+
*/
|
|
42
|
+
interface TransformOptions extends TransformKeyOptions {
|
|
38
43
|
/**
|
|
39
44
|
* How many nesting levels to transform: `1` transforms only the top-level keys, `2`
|
|
40
45
|
* also the keys one level down, and so on. The values of objects, Maps and Sets are one
|
|
@@ -69,13 +74,24 @@ interface TransformOptions {
|
|
|
69
74
|
sourceCase?: StringCase | undefined;
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
72
|
-
* Rejects option names
|
|
73
|
-
* whole, so without this an object literal would
|
|
74
|
-
* as `depht` included. The error names the
|
|
77
|
+
* Rejects option names that are not in `Known` (by default, the options of `transform`).
|
|
78
|
+
* The options object is inferred as a whole, so without this an object literal would
|
|
79
|
+
* accept any extra property, typos such as `depht` included. The error names the
|
|
80
|
+
* unknown option.
|
|
75
81
|
*/
|
|
76
|
-
type NoUnknownOptions<Options> = {
|
|
77
|
-
[K in Exclude<keyof Options, keyof
|
|
82
|
+
type NoUnknownOptions<Options, Known = TransformOptions> = {
|
|
83
|
+
[K in Exclude<keyof Options, keyof Known>]: `Unknown option: ${K & string}`;
|
|
78
84
|
};
|
|
85
|
+
/**
|
|
86
|
+
* `NoUnknownOptions` for `transformKey`, but `unknown` when every option name is known.
|
|
87
|
+
* The options parameter is then `TransformKeyOptions` itself rather than an
|
|
88
|
+
* intersection, so TypeScript checks the argument for a property in common with it, as
|
|
89
|
+
* it does for every type whose properties are all optional: `'snake'`, an array, or a
|
|
90
|
+
* stored options object with only a misspelled option is rejected.
|
|
91
|
+
*/
|
|
92
|
+
type NoUnknownKeyOptions<Options> = [
|
|
93
|
+
Exclude<keyof Options, keyof TransformKeyOptions>
|
|
94
|
+
] extends [never] ? unknown : NoUnknownOptions<Options, TransformKeyOptions>;
|
|
79
95
|
/** `To` as a plain case literal, or `'camel'` (the default) for `undefined` */
|
|
80
96
|
type CaseOrDefault<To> = To extends undefined ? 'camel' : PlainCase<To>;
|
|
81
97
|
/**
|
|
@@ -211,10 +227,33 @@ type FormatKey<K extends string, From extends StringCase, To extends StringCase>
|
|
|
211
227
|
/** Applies the detected case of `K` (`transformCase`) */
|
|
212
228
|
type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
|
|
213
229
|
/**
|
|
214
|
-
*
|
|
230
|
+
* One key `K` (not a union) transformed to one plain target case `To`. A `string` or
|
|
215
231
|
* template-literal key cannot be known exactly, so it becomes `string`.
|
|
216
232
|
*/
|
|
217
|
-
type
|
|
233
|
+
type TransformOneKey<K extends string, To extends StringCase> = IsStringLiteral<K> extends false ? string : K extends '' ? K : IsPreservedKey<K> extends true ? K : TransformDetectedKey<K, DetectedCase<K>, To>;
|
|
234
|
+
/**
|
|
235
|
+
* One key `K` transformed to each member of a union of plain target cases `To`.
|
|
236
|
+
*
|
|
237
|
+
* Use `TransformKey` instead. This type is exported only so that declaration files can
|
|
238
|
+
* refer to it: for a fixed key and a generic target case, as in
|
|
239
|
+
* `<C extends StringCase>(targetCase: C) => transformKey('created_by_id', { targetCase })`,
|
|
240
|
+
* the key type is written in terms of it. Without a name to refer to, the compiler
|
|
241
|
+
* writes out its recursive steps, and gets them wrong.
|
|
242
|
+
*/
|
|
243
|
+
type TransformKeyToEach<K extends string, To extends StringCase> = To extends StringCase ? TransformOneKey<K, To> : never;
|
|
244
|
+
/**
|
|
245
|
+
* The key a string key `K` becomes when transformed to `To` (default `'camel'`), as
|
|
246
|
+
* `transformKey` returns it. A `string` or template-literal key cannot be known exactly,
|
|
247
|
+
* so it becomes `string`. A union of keys or of target cases gives the union of their
|
|
248
|
+
* results, and a string enum member or branded literal stands for its case.
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```typescript
|
|
252
|
+
* type Column = TransformKey<'createdAt' | 'userID', 'snake'>;
|
|
253
|
+
* // => 'created_at' | 'user_id'
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
type TransformKey<K extends string, To extends StringCase = 'camel'> = K extends string ? TransformKeyToEach<K, PlainCase<To>> : never;
|
|
218
257
|
|
|
219
258
|
/** Primitive values, including branded ones such as `string & { __brand: 'UserId' }` */
|
|
220
259
|
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
@@ -403,6 +442,17 @@ type TransformResultForExcluding<T, Options> = ExcludeOf<Options> extends infer
|
|
|
403
442
|
* result: `unknown`.
|
|
404
443
|
*/
|
|
405
444
|
type TransformResultFor<T, Options> = IsAny<Options> extends true ? unknown : Options extends unknown ? IsAnyOption<Options, 'targetCase'> extends true ? unknown : IsAnyOption<Options, 'depth'> extends true ? unknown : 'exclude' extends keyof Options ? TransformResultForExcluding<T, Options> : TransformResultForOne<T, Options> : never;
|
|
445
|
+
/**
|
|
446
|
+
* The key type `transformKey` returns for an options object. Each member of a union of
|
|
447
|
+
* options objects gives its own key, and an optional `targetCase` may also be omitted,
|
|
448
|
+
* which gives the camelCase key (see `TransformResultForOne`). Options typed `any`, or a
|
|
449
|
+
* `targetCase` typed `any`, give the key in any of the four cases.
|
|
450
|
+
*
|
|
451
|
+
* Not exported: declaration files of code around `transformKey` refer to `TransformKey`
|
|
452
|
+
* instead, because options with a generic type do not match the overload that returns
|
|
453
|
+
* it.
|
|
454
|
+
*/
|
|
455
|
+
type TransformKeyFor<K extends string, Options> = Options extends unknown ? TransformKey<K, TargetCaseOf<Options> | (IsOptional<Options, 'targetCase'> extends true ? 'camel' : never)> : never;
|
|
406
456
|
|
|
407
457
|
/**
|
|
408
458
|
* Transforms the keys of `data` to camelCase, at every nesting level.
|
|
@@ -467,5 +517,39 @@ declare function transform<T, From extends StringCase, To extends StringCase>(da
|
|
|
467
517
|
*/
|
|
468
518
|
declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sourceCase'>): K;
|
|
469
519
|
|
|
470
|
-
|
|
471
|
-
|
|
520
|
+
/**
|
|
521
|
+
* Transforms one key to camelCase, the way `transform` transforms every key. A string
|
|
522
|
+
* that is not a key in one of the four cases, such as `'Hello World'`, `'USER_ID'` or a
|
|
523
|
+
* UUID, is returned unchanged (see "Key Preservation Rules" in the readme).
|
|
524
|
+
*
|
|
525
|
+
* The result type is the transformed key: `transformKey('user_id')` is typed `'userId'`.
|
|
526
|
+
*/
|
|
527
|
+
declare function transformKey<K extends string>(key: K, options?: undefined): TransformKey<K>;
|
|
528
|
+
/**
|
|
529
|
+
* Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
|
|
530
|
+
* every key. A string that is not a key in one of the four cases, such as
|
|
531
|
+
* `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
|
|
532
|
+
* Rules" in the readme).
|
|
533
|
+
*
|
|
534
|
+
* The result type is the transformed key: `transformKey('userID', { targetCase: 'snake' })`
|
|
535
|
+
* is typed `'user_id'`, a union of keys or target cases gives the union of the results,
|
|
536
|
+
* and a `string` key gives `string`.
|
|
537
|
+
*
|
|
538
|
+
* `Options` captures the options object as a whole, as `transform` does, so each member
|
|
539
|
+
* of a union of options objects gets its own result, and an optional `targetCase` also
|
|
540
|
+
* gives the camelCase key. `NoUnknownKeyOptions` rejects option names other than
|
|
541
|
+
* `targetCase`, and otherwise leaves the parameter `TransformKeyOptions` itself, so a
|
|
542
|
+
* value with no property in common with it, such as `'snake'`, is rejected as well. An
|
|
543
|
+
* options object that may itself be `undefined` does not match this overload, and gets
|
|
544
|
+
* a `string` result type from the last one.
|
|
545
|
+
*/
|
|
546
|
+
declare function transformKey<K extends string, Options extends TransformKeyOptions = TransformKeyOptions>(key: K, options: Options & NoUnknownKeyOptions<Options>): TransformKeyFor<K, Options>;
|
|
547
|
+
/**
|
|
548
|
+
* Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
|
|
549
|
+
* every key, typed as a `string`: for an options object that may be `undefined`, or one
|
|
550
|
+
* with properties other than `targetCase`.
|
|
551
|
+
*/
|
|
552
|
+
declare function transformKey(key: string, options?: TransformKeyOptions): string;
|
|
553
|
+
|
|
554
|
+
export { transform, transformKey };
|
|
555
|
+
export type { StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
|
package/dist/index.d.mts
CHANGED
|
@@ -27,14 +27,19 @@ type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab';
|
|
|
27
27
|
type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : never;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Options for the transform function
|
|
30
|
+
* Options for the transformKey function. The transform function takes them as well.
|
|
31
31
|
*/
|
|
32
|
-
interface
|
|
32
|
+
interface TransformKeyOptions {
|
|
33
33
|
/**
|
|
34
34
|
* The target casing format to transform to
|
|
35
35
|
* @default 'camel'
|
|
36
36
|
*/
|
|
37
37
|
targetCase?: StringCase | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Options for the transform function
|
|
41
|
+
*/
|
|
42
|
+
interface TransformOptions extends TransformKeyOptions {
|
|
38
43
|
/**
|
|
39
44
|
* How many nesting levels to transform: `1` transforms only the top-level keys, `2`
|
|
40
45
|
* also the keys one level down, and so on. The values of objects, Maps and Sets are one
|
|
@@ -69,13 +74,24 @@ interface TransformOptions {
|
|
|
69
74
|
sourceCase?: StringCase | undefined;
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
72
|
-
* Rejects option names
|
|
73
|
-
* whole, so without this an object literal would
|
|
74
|
-
* as `depht` included. The error names the
|
|
77
|
+
* Rejects option names that are not in `Known` (by default, the options of `transform`).
|
|
78
|
+
* The options object is inferred as a whole, so without this an object literal would
|
|
79
|
+
* accept any extra property, typos such as `depht` included. The error names the
|
|
80
|
+
* unknown option.
|
|
75
81
|
*/
|
|
76
|
-
type NoUnknownOptions<Options> = {
|
|
77
|
-
[K in Exclude<keyof Options, keyof
|
|
82
|
+
type NoUnknownOptions<Options, Known = TransformOptions> = {
|
|
83
|
+
[K in Exclude<keyof Options, keyof Known>]: `Unknown option: ${K & string}`;
|
|
78
84
|
};
|
|
85
|
+
/**
|
|
86
|
+
* `NoUnknownOptions` for `transformKey`, but `unknown` when every option name is known.
|
|
87
|
+
* The options parameter is then `TransformKeyOptions` itself rather than an
|
|
88
|
+
* intersection, so TypeScript checks the argument for a property in common with it, as
|
|
89
|
+
* it does for every type whose properties are all optional: `'snake'`, an array, or a
|
|
90
|
+
* stored options object with only a misspelled option is rejected.
|
|
91
|
+
*/
|
|
92
|
+
type NoUnknownKeyOptions<Options> = [
|
|
93
|
+
Exclude<keyof Options, keyof TransformKeyOptions>
|
|
94
|
+
] extends [never] ? unknown : NoUnknownOptions<Options, TransformKeyOptions>;
|
|
79
95
|
/** `To` as a plain case literal, or `'camel'` (the default) for `undefined` */
|
|
80
96
|
type CaseOrDefault<To> = To extends undefined ? 'camel' : PlainCase<To>;
|
|
81
97
|
/**
|
|
@@ -211,10 +227,33 @@ type FormatKey<K extends string, From extends StringCase, To extends StringCase>
|
|
|
211
227
|
/** Applies the detected case of `K` (`transformCase`) */
|
|
212
228
|
type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
|
|
213
229
|
/**
|
|
214
|
-
*
|
|
230
|
+
* One key `K` (not a union) transformed to one plain target case `To`. A `string` or
|
|
215
231
|
* template-literal key cannot be known exactly, so it becomes `string`.
|
|
216
232
|
*/
|
|
217
|
-
type
|
|
233
|
+
type TransformOneKey<K extends string, To extends StringCase> = IsStringLiteral<K> extends false ? string : K extends '' ? K : IsPreservedKey<K> extends true ? K : TransformDetectedKey<K, DetectedCase<K>, To>;
|
|
234
|
+
/**
|
|
235
|
+
* One key `K` transformed to each member of a union of plain target cases `To`.
|
|
236
|
+
*
|
|
237
|
+
* Use `TransformKey` instead. This type is exported only so that declaration files can
|
|
238
|
+
* refer to it: for a fixed key and a generic target case, as in
|
|
239
|
+
* `<C extends StringCase>(targetCase: C) => transformKey('created_by_id', { targetCase })`,
|
|
240
|
+
* the key type is written in terms of it. Without a name to refer to, the compiler
|
|
241
|
+
* writes out its recursive steps, and gets them wrong.
|
|
242
|
+
*/
|
|
243
|
+
type TransformKeyToEach<K extends string, To extends StringCase> = To extends StringCase ? TransformOneKey<K, To> : never;
|
|
244
|
+
/**
|
|
245
|
+
* The key a string key `K` becomes when transformed to `To` (default `'camel'`), as
|
|
246
|
+
* `transformKey` returns it. A `string` or template-literal key cannot be known exactly,
|
|
247
|
+
* so it becomes `string`. A union of keys or of target cases gives the union of their
|
|
248
|
+
* results, and a string enum member or branded literal stands for its case.
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```typescript
|
|
252
|
+
* type Column = TransformKey<'createdAt' | 'userID', 'snake'>;
|
|
253
|
+
* // => 'created_at' | 'user_id'
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
type TransformKey<K extends string, To extends StringCase = 'camel'> = K extends string ? TransformKeyToEach<K, PlainCase<To>> : never;
|
|
218
257
|
|
|
219
258
|
/** Primitive values, including branded ones such as `string & { __brand: 'UserId' }` */
|
|
220
259
|
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
@@ -403,6 +442,17 @@ type TransformResultForExcluding<T, Options> = ExcludeOf<Options> extends infer
|
|
|
403
442
|
* result: `unknown`.
|
|
404
443
|
*/
|
|
405
444
|
type TransformResultFor<T, Options> = IsAny<Options> extends true ? unknown : Options extends unknown ? IsAnyOption<Options, 'targetCase'> extends true ? unknown : IsAnyOption<Options, 'depth'> extends true ? unknown : 'exclude' extends keyof Options ? TransformResultForExcluding<T, Options> : TransformResultForOne<T, Options> : never;
|
|
445
|
+
/**
|
|
446
|
+
* The key type `transformKey` returns for an options object. Each member of a union of
|
|
447
|
+
* options objects gives its own key, and an optional `targetCase` may also be omitted,
|
|
448
|
+
* which gives the camelCase key (see `TransformResultForOne`). Options typed `any`, or a
|
|
449
|
+
* `targetCase` typed `any`, give the key in any of the four cases.
|
|
450
|
+
*
|
|
451
|
+
* Not exported: declaration files of code around `transformKey` refer to `TransformKey`
|
|
452
|
+
* instead, because options with a generic type do not match the overload that returns
|
|
453
|
+
* it.
|
|
454
|
+
*/
|
|
455
|
+
type TransformKeyFor<K extends string, Options> = Options extends unknown ? TransformKey<K, TargetCaseOf<Options> | (IsOptional<Options, 'targetCase'> extends true ? 'camel' : never)> : never;
|
|
406
456
|
|
|
407
457
|
/**
|
|
408
458
|
* Transforms the keys of `data` to camelCase, at every nesting level.
|
|
@@ -467,5 +517,39 @@ declare function transform<T, From extends StringCase, To extends StringCase>(da
|
|
|
467
517
|
*/
|
|
468
518
|
declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sourceCase'>): K;
|
|
469
519
|
|
|
470
|
-
|
|
471
|
-
|
|
520
|
+
/**
|
|
521
|
+
* Transforms one key to camelCase, the way `transform` transforms every key. A string
|
|
522
|
+
* that is not a key in one of the four cases, such as `'Hello World'`, `'USER_ID'` or a
|
|
523
|
+
* UUID, is returned unchanged (see "Key Preservation Rules" in the readme).
|
|
524
|
+
*
|
|
525
|
+
* The result type is the transformed key: `transformKey('user_id')` is typed `'userId'`.
|
|
526
|
+
*/
|
|
527
|
+
declare function transformKey<K extends string>(key: K, options?: undefined): TransformKey<K>;
|
|
528
|
+
/**
|
|
529
|
+
* Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
|
|
530
|
+
* every key. A string that is not a key in one of the four cases, such as
|
|
531
|
+
* `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
|
|
532
|
+
* Rules" in the readme).
|
|
533
|
+
*
|
|
534
|
+
* The result type is the transformed key: `transformKey('userID', { targetCase: 'snake' })`
|
|
535
|
+
* is typed `'user_id'`, a union of keys or target cases gives the union of the results,
|
|
536
|
+
* and a `string` key gives `string`.
|
|
537
|
+
*
|
|
538
|
+
* `Options` captures the options object as a whole, as `transform` does, so each member
|
|
539
|
+
* of a union of options objects gets its own result, and an optional `targetCase` also
|
|
540
|
+
* gives the camelCase key. `NoUnknownKeyOptions` rejects option names other than
|
|
541
|
+
* `targetCase`, and otherwise leaves the parameter `TransformKeyOptions` itself, so a
|
|
542
|
+
* value with no property in common with it, such as `'snake'`, is rejected as well. An
|
|
543
|
+
* options object that may itself be `undefined` does not match this overload, and gets
|
|
544
|
+
* a `string` result type from the last one.
|
|
545
|
+
*/
|
|
546
|
+
declare function transformKey<K extends string, Options extends TransformKeyOptions = TransformKeyOptions>(key: K, options: Options & NoUnknownKeyOptions<Options>): TransformKeyFor<K, Options>;
|
|
547
|
+
/**
|
|
548
|
+
* Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
|
|
549
|
+
* every key, typed as a `string`: for an options object that may be `undefined`, or one
|
|
550
|
+
* with properties other than `targetCase`.
|
|
551
|
+
*/
|
|
552
|
+
declare function transformKey(key: string, options?: TransformKeyOptions): string;
|
|
553
|
+
|
|
554
|
+
export { transform, transformKey };
|
|
555
|
+
export type { StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
|
package/dist/index.d.ts
CHANGED
|
@@ -27,14 +27,19 @@ type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab';
|
|
|
27
27
|
type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : never;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Options for the transform function
|
|
30
|
+
* Options for the transformKey function. The transform function takes them as well.
|
|
31
31
|
*/
|
|
32
|
-
interface
|
|
32
|
+
interface TransformKeyOptions {
|
|
33
33
|
/**
|
|
34
34
|
* The target casing format to transform to
|
|
35
35
|
* @default 'camel'
|
|
36
36
|
*/
|
|
37
37
|
targetCase?: StringCase | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Options for the transform function
|
|
41
|
+
*/
|
|
42
|
+
interface TransformOptions extends TransformKeyOptions {
|
|
38
43
|
/**
|
|
39
44
|
* How many nesting levels to transform: `1` transforms only the top-level keys, `2`
|
|
40
45
|
* also the keys one level down, and so on. The values of objects, Maps and Sets are one
|
|
@@ -69,13 +74,24 @@ interface TransformOptions {
|
|
|
69
74
|
sourceCase?: StringCase | undefined;
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
72
|
-
* Rejects option names
|
|
73
|
-
* whole, so without this an object literal would
|
|
74
|
-
* as `depht` included. The error names the
|
|
77
|
+
* Rejects option names that are not in `Known` (by default, the options of `transform`).
|
|
78
|
+
* The options object is inferred as a whole, so without this an object literal would
|
|
79
|
+
* accept any extra property, typos such as `depht` included. The error names the
|
|
80
|
+
* unknown option.
|
|
75
81
|
*/
|
|
76
|
-
type NoUnknownOptions<Options> = {
|
|
77
|
-
[K in Exclude<keyof Options, keyof
|
|
82
|
+
type NoUnknownOptions<Options, Known = TransformOptions> = {
|
|
83
|
+
[K in Exclude<keyof Options, keyof Known>]: `Unknown option: ${K & string}`;
|
|
78
84
|
};
|
|
85
|
+
/**
|
|
86
|
+
* `NoUnknownOptions` for `transformKey`, but `unknown` when every option name is known.
|
|
87
|
+
* The options parameter is then `TransformKeyOptions` itself rather than an
|
|
88
|
+
* intersection, so TypeScript checks the argument for a property in common with it, as
|
|
89
|
+
* it does for every type whose properties are all optional: `'snake'`, an array, or a
|
|
90
|
+
* stored options object with only a misspelled option is rejected.
|
|
91
|
+
*/
|
|
92
|
+
type NoUnknownKeyOptions<Options> = [
|
|
93
|
+
Exclude<keyof Options, keyof TransformKeyOptions>
|
|
94
|
+
] extends [never] ? unknown : NoUnknownOptions<Options, TransformKeyOptions>;
|
|
79
95
|
/** `To` as a plain case literal, or `'camel'` (the default) for `undefined` */
|
|
80
96
|
type CaseOrDefault<To> = To extends undefined ? 'camel' : PlainCase<To>;
|
|
81
97
|
/**
|
|
@@ -211,10 +227,33 @@ type FormatKey<K extends string, From extends StringCase, To extends StringCase>
|
|
|
211
227
|
/** Applies the detected case of `K` (`transformCase`) */
|
|
212
228
|
type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
|
|
213
229
|
/**
|
|
214
|
-
*
|
|
230
|
+
* One key `K` (not a union) transformed to one plain target case `To`. A `string` or
|
|
215
231
|
* template-literal key cannot be known exactly, so it becomes `string`.
|
|
216
232
|
*/
|
|
217
|
-
type
|
|
233
|
+
type TransformOneKey<K extends string, To extends StringCase> = IsStringLiteral<K> extends false ? string : K extends '' ? K : IsPreservedKey<K> extends true ? K : TransformDetectedKey<K, DetectedCase<K>, To>;
|
|
234
|
+
/**
|
|
235
|
+
* One key `K` transformed to each member of a union of plain target cases `To`.
|
|
236
|
+
*
|
|
237
|
+
* Use `TransformKey` instead. This type is exported only so that declaration files can
|
|
238
|
+
* refer to it: for a fixed key and a generic target case, as in
|
|
239
|
+
* `<C extends StringCase>(targetCase: C) => transformKey('created_by_id', { targetCase })`,
|
|
240
|
+
* the key type is written in terms of it. Without a name to refer to, the compiler
|
|
241
|
+
* writes out its recursive steps, and gets them wrong.
|
|
242
|
+
*/
|
|
243
|
+
type TransformKeyToEach<K extends string, To extends StringCase> = To extends StringCase ? TransformOneKey<K, To> : never;
|
|
244
|
+
/**
|
|
245
|
+
* The key a string key `K` becomes when transformed to `To` (default `'camel'`), as
|
|
246
|
+
* `transformKey` returns it. A `string` or template-literal key cannot be known exactly,
|
|
247
|
+
* so it becomes `string`. A union of keys or of target cases gives the union of their
|
|
248
|
+
* results, and a string enum member or branded literal stands for its case.
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```typescript
|
|
252
|
+
* type Column = TransformKey<'createdAt' | 'userID', 'snake'>;
|
|
253
|
+
* // => 'created_at' | 'user_id'
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
type TransformKey<K extends string, To extends StringCase = 'camel'> = K extends string ? TransformKeyToEach<K, PlainCase<To>> : never;
|
|
218
257
|
|
|
219
258
|
/** Primitive values, including branded ones such as `string & { __brand: 'UserId' }` */
|
|
220
259
|
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
@@ -403,6 +442,17 @@ type TransformResultForExcluding<T, Options> = ExcludeOf<Options> extends infer
|
|
|
403
442
|
* result: `unknown`.
|
|
404
443
|
*/
|
|
405
444
|
type TransformResultFor<T, Options> = IsAny<Options> extends true ? unknown : Options extends unknown ? IsAnyOption<Options, 'targetCase'> extends true ? unknown : IsAnyOption<Options, 'depth'> extends true ? unknown : 'exclude' extends keyof Options ? TransformResultForExcluding<T, Options> : TransformResultForOne<T, Options> : never;
|
|
445
|
+
/**
|
|
446
|
+
* The key type `transformKey` returns for an options object. Each member of a union of
|
|
447
|
+
* options objects gives its own key, and an optional `targetCase` may also be omitted,
|
|
448
|
+
* which gives the camelCase key (see `TransformResultForOne`). Options typed `any`, or a
|
|
449
|
+
* `targetCase` typed `any`, give the key in any of the four cases.
|
|
450
|
+
*
|
|
451
|
+
* Not exported: declaration files of code around `transformKey` refer to `TransformKey`
|
|
452
|
+
* instead, because options with a generic type do not match the overload that returns
|
|
453
|
+
* it.
|
|
454
|
+
*/
|
|
455
|
+
type TransformKeyFor<K extends string, Options> = Options extends unknown ? TransformKey<K, TargetCaseOf<Options> | (IsOptional<Options, 'targetCase'> extends true ? 'camel' : never)> : never;
|
|
406
456
|
|
|
407
457
|
/**
|
|
408
458
|
* Transforms the keys of `data` to camelCase, at every nesting level.
|
|
@@ -467,5 +517,39 @@ declare function transform<T, From extends StringCase, To extends StringCase>(da
|
|
|
467
517
|
*/
|
|
468
518
|
declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sourceCase'>): K;
|
|
469
519
|
|
|
470
|
-
|
|
471
|
-
|
|
520
|
+
/**
|
|
521
|
+
* Transforms one key to camelCase, the way `transform` transforms every key. A string
|
|
522
|
+
* that is not a key in one of the four cases, such as `'Hello World'`, `'USER_ID'` or a
|
|
523
|
+
* UUID, is returned unchanged (see "Key Preservation Rules" in the readme).
|
|
524
|
+
*
|
|
525
|
+
* The result type is the transformed key: `transformKey('user_id')` is typed `'userId'`.
|
|
526
|
+
*/
|
|
527
|
+
declare function transformKey<K extends string>(key: K, options?: undefined): TransformKey<K>;
|
|
528
|
+
/**
|
|
529
|
+
* Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
|
|
530
|
+
* every key. A string that is not a key in one of the four cases, such as
|
|
531
|
+
* `'Hello World'`, `'USER_ID'` or a UUID, is returned unchanged (see "Key Preservation
|
|
532
|
+
* Rules" in the readme).
|
|
533
|
+
*
|
|
534
|
+
* The result type is the transformed key: `transformKey('userID', { targetCase: 'snake' })`
|
|
535
|
+
* is typed `'user_id'`, a union of keys or target cases gives the union of the results,
|
|
536
|
+
* and a `string` key gives `string`.
|
|
537
|
+
*
|
|
538
|
+
* `Options` captures the options object as a whole, as `transform` does, so each member
|
|
539
|
+
* of a union of options objects gets its own result, and an optional `targetCase` also
|
|
540
|
+
* gives the camelCase key. `NoUnknownKeyOptions` rejects option names other than
|
|
541
|
+
* `targetCase`, and otherwise leaves the parameter `TransformKeyOptions` itself, so a
|
|
542
|
+
* value with no property in common with it, such as `'snake'`, is rejected as well. An
|
|
543
|
+
* options object that may itself be `undefined` does not match this overload, and gets
|
|
544
|
+
* a `string` result type from the last one.
|
|
545
|
+
*/
|
|
546
|
+
declare function transformKey<K extends string, Options extends TransformKeyOptions = TransformKeyOptions>(key: K, options: Options & NoUnknownKeyOptions<Options>): TransformKeyFor<K, Options>;
|
|
547
|
+
/**
|
|
548
|
+
* Transforms one key to `targetCase` (default `'camel'`), the way `transform` transforms
|
|
549
|
+
* every key, typed as a `string`: for an options object that may be `undefined`, or one
|
|
550
|
+
* with properties other than `targetCase`.
|
|
551
|
+
*/
|
|
552
|
+
declare function transformKey(key: string, options?: TransformKeyOptions): string;
|
|
553
|
+
|
|
554
|
+
export { transform, transformKey };
|
|
555
|
+
export type { StringCase, TransformKey, TransformKeyOptions, TransformKeyToEach, TransformOptions, TransformResult, TransformResultFor, TransformResultForExcluding, TransformValue, TransformValueExcluding };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e=/^[a-z0-9_]+$/,t=/__+/,n=/^[a-z][a-zA-Z0-9]*$/,r=/^[A-Z][a-zA-Z0-9]*$/,o=/^[a-z0-9-]+$/,s=/-{2,}/,u=/[A-Z]/,a=/[a-z]/,
|
|
1
|
+
"use strict";const e=/^[a-z0-9_]+$/,t=/__+/,n=/^[a-z][a-zA-Z0-9]*$/,r=/^[A-Z][a-zA-Z0-9]*$/,o=/^[a-z0-9-]+$/,s=/-{2,}/,u=/[A-Z]/,a=/[a-z]/,i=/^[a-z0-9]+$/,c=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,l=/^[^.]+(?:\.[^.]+)*$/;function f(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 p(e){const t=e.indexOf(".");return-1===t?[e]:[e.slice(0,t),e.slice(t+1)]}const d=[];function g(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?h(t):d,scope:void 0}}function h(e){return 1===e.length?e:[...new Set(e)].sort()}function y(e,t){const n=[];let r;for(const t of e){const[e,o]=p(t);if("*"===e)n.push(o);else{null!=r||(r=new Map);const t=r.get(e);t?t.push(o):r.set(e,[o])}}const o=new Map;for(const[e,t]of null!=r?r:[])o.set(e,g(n.length?[...t,...n]:t));const{targetCase:s,depth:u}=t.plain.options,a={targetCase:s,depth:u,exclude:null},i={options:a,visitedRefs:[],named:o,other:g(n),root:t};return a.exclude=i,i}function w(e,t){const n="string"==typeof t?e.named.get(t):void 0;return null!=n?n:e.other}function b(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=y(e,t),t.states.set(n,r)),r}(t.pathsBelow,e.root)}const v="[object Object]";function C(e,t,n){"__proto__"===t?Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[t]=n}function m(e){return e>="a"&&e<="z"}function k(e){return e>="A"&&e<="Z"}function U(e,t){var n,r,o,s;if(!k(null!==(n=e[t])&&void 0!==n?n:""))return!1;const u=null!==(r=e[t-1])&&void 0!==r?r:"";if(m(u)||(a=u)>="0"&&a<="9")return!0;var a;if(!(k(u)&&m(null!==(o=e[t+1])&&void 0!==o?o:"")))return!1;return!("s"===e[t+1]&&!m(null!==(s=e[t+2])&&void 0!==s?s:""))}function z(e,t){if(!e)return e;let n="";for(let r=0;r<e.length;r++)U(e,r)&&(n+=t),n+=e[r].toLowerCase();return n}const $={snake:{camel:function(e){return e.toLowerCase().replace(/_([a-z0-9])/g,(e,t)=>t.toUpperCase())},pascal:function(e){return e.toLowerCase().replace(/_+([a-z0-9])/g,(e,t)=>t.toUpperCase()).replace(/^[a-z]/,e=>e.toUpperCase())},kebab:function(e){return e.replace(/_/g,"-")},snake:e=>e},camel:{snake:function(e){return z(e,"_")},pascal:function(e){return e?e.charAt(0).toUpperCase()+e.slice(1):e},kebab:function(e){return z(e,"-")},camel:e=>e},pascal:{snake:function(e){return z(e,"_")},camel:function(e){if(!e)return e;let t=1;for(;t<e.length&&!U(e,t);)t++;return e.slice(0,t).toLowerCase()+e.slice(t)},kebab:function(e){return z(e,"-")},pascal:e=>e},kebab:{snake:function(e){return e.replace(/-/g,"_")},camel:function(e){return e.toLowerCase().replace(/-+([a-z0-9])/g,(e,t)=>t.toUpperCase())},pascal:function(e){return e.toLowerCase().replace(/-+([a-z0-9])/g,(e,t)=>t.toUpperCase()).replace(/^[a-z]/,e=>e.toUpperCase())},kebab:e=>e}};function j(c){return"string"!=typeof c?null:function(e){if(e.includes("_")||e.includes("-")||e.includes(" "))return!1;if(e.length>0&&!/^[A-Za-z0-9]+$/.test(e))return!1;if(e.length<=1)return!0;const t=e[0],n=e.slice(1);return e===e.toLowerCase()||t===t.toUpperCase()&&n===n.toLowerCase()}(c)?"single":function(n){return"string"==typeof n&&e.test(n)&&!t.test(n)&&(n.includes("_")||i.test(n))}(c)?"snake":function(e){return"string"==typeof e&&n.test(e)&&u.test(e)}(c)?"camel":function(e){return"string"==typeof e&&r.test(e)&&a.test(e)}(c)?"pascal":function(e){return"string"==typeof e&&o.test(e)&&!s.test(e)&&(e.includes("-")||i.test(e))}(c)?"kebab":null}function A(e,t){if(!e)return e;const{string:n,shouldTransform:r,sourceCase:o,singleWord:s}=function(e){if("string"!=typeof e)throw new Error("Input must be a string");if(0===e.length)return{string:e,shouldTransform:!1,sourceCase:null,singleWord:!1};if(/[^A-Za-z0-9_-]/.test(e)||/^[^A-Za-z]/.test(e)||/[^A-Za-z0-9]$/.test(e)||c.test(e))return{string:e,shouldTransform:!1,sourceCase:null,singleWord:!1};const t=j(e);return t?"single"===t?{string:e,shouldTransform:!0,sourceCase:null,singleWord:!0}:{string:e,shouldTransform:!0,sourceCase:t,singleWord:!1}:{string:e,shouldTransform:!1,sourceCase:null,singleWord:!1}}(e);return r?s?function(e,t){switch(t){case"pascal":return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase();case"camel":return e.charAt(0).toLowerCase()+e.slice(1);case"snake":case"kebab":return e.toLowerCase();default:return e}}(n,t):o&&o!==t?$[o][t](n):n:n}const _=new Map;function x(e,t){var n;if(e.length>100)return A(e,t);let r=_.get(t);r||(r={current:new Map,previous:new Map},_.set(t,r));let o=r.current.get(e);return void 0===o&&(o=null!==(n=r.previous.get(e))&&void 0!==n?n:A(e,t),r.current.size>=1e3&&(r.previous=r.current,r.current=new Map),r.current.set(e,o)),o}function O(e,t,n=[],r=0){var o;if(null===e||"object"!=typeof e)return e;if(r>=t.depth)return e;const s=t.depth===1/0?0:r,u=null!==(o=n[s])&&void 0!==o?o:n[s]=new WeakMap;if(u.has(e))return u.get(e);if(Array.isArray(e)){const o=[];return u.set(e,o),function(e,t,n,r,o){for(const s of e)t.push(O(s,n,r,o))}(e,o,t,n,r),o}const a=r+1,i=function(e){try{return Object.prototype.toString.call(e)}catch(e){return v}}(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,i)){const r=new Map;return u.set(e,r),null===t.exclude?function(e,t,n,r,o){for(const[s,u]of e)t.set("string"==typeof s?A(s,n.targetCase):s,O(u,n,r,o))}(e,r,t,n,a):function(e,t,n,r){for(const[o,s]of e){const e=w(n,o),u=b(n,e);t.set("string"!=typeof o||e.keepKey?o:A(o,n.options.targetCase),O(s,u.options,u.visitedRefs,r))}}(e,r,t.exclude,a),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,i)){const r=new Set;return u.set(e,r),function(e,t,n,r,o){for(const s of e)t.add(O(s,n,r,o))}(e,r,t,n,a),r}if(i===v){const r=e,o={};if(u.set(e,o),null===t.exclude)for(const e in r)Object.prototype.hasOwnProperty.call(r,e)&&C(o,x(e,t.targetCase),O(r[e],t,n,a));else!function(e,t,n,r){for(const o in e)if(Object.prototype.hasOwnProperty.call(e,o)){const s=w(n,o),u=b(n,s);C(t,s.keepKey?o:x(o,n.options.targetCase),O(e[o],u.options,u.visitedRefs,r))}}(r,o,t.exclude,a);for(const e of Object.getOwnPropertySymbols(r))o[e]=r[e];return o}return e}const M=["camel","snake","pascal","kebab"];function T(e){const t=e||"camel";if(n=t,!M.includes(n))throw new TypeError(`Unsupported targetCase ${f(t)}. Use one of: ${M.join(", ")}.`);var n;return t}exports.transform=function(e,t){var n;const r=null==t?void 0:t.targetCase,o=null!==(n=null==t?void 0:t.depth)&&void 0!==n?n:1/0,s=T(r);if("number"!=typeof o||Number.isNaN(o))throw new TypeError(`Unsupported depth ${f(o)}. Use a number, or omit depth to transform every level.`);const u={targetCase:s,depth:o,exclude:null},a=null==t?void 0:t.exclude,i=void 0===a?null:function(e,t){if(null==e)return null;if(!Array.isArray(e))throw new TypeError(`Unsupported exclude ${f(e)}. Use an array of paths, such as ["metadata.*"].`);for(const t of e)if("string"!=typeof t||!l.test(t))throw new TypeError(`Unsupported exclude path ${f(t)}. Use non-empty keys joined with dots, such as "metadata.*".`);if(0===e.length)return null;const n={plain:{options:t,visitedRefs:[]},states:new Map};return y(h(e),n)}(a,u);return null===i?O(e,u):O(e,i.options,i.visitedRefs)},exports.transformKey=function(e,t){if("string"!=typeof e)throw new TypeError(`Unsupported key ${f(e)}. Use a string.`);if("string"==typeof t)throw new TypeError(`Unsupported options ${f(t)}. Use an options object: { targetCase: ${f(t)} }.`);return A(e,T(null==t?void 0:t.targetCase))};
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=/^[a-z0-9_]+$/,t=/__+/,n=/^[a-z][a-zA-Z0-9]*$/,r=/^[A-Z][a-zA-Z0-9]*$/,o=/^[a-z0-9-]+$/,s=/-{2,}/,u=/[A-Z]/,a=/[a-z]/,
|
|
1
|
+
const e=/^[a-z0-9_]+$/,t=/__+/,n=/^[a-z][a-zA-Z0-9]*$/,r=/^[A-Z][a-zA-Z0-9]*$/,o=/^[a-z0-9-]+$/,s=/-{2,}/,u=/[A-Z]/,a=/[a-z]/,i=/^[a-z0-9]+$/,c=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,l=/^[^.]+(?:\.[^.]+)*$/;function f(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 p(e){const t=e.indexOf(".");return-1===t?[e]:[e.slice(0,t),e.slice(t+1)]}const d=[];function g(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?h(t):d,scope:void 0}}function h(e){return 1===e.length?e:[...new Set(e)].sort()}function y(e,t){const n=[];let r;for(const t of e){const[e,o]=p(t);if("*"===e)n.push(o);else{null!=r||(r=new Map);const t=r.get(e);t?t.push(o):r.set(e,[o])}}const o=new Map;for(const[e,t]of null!=r?r:[])o.set(e,g(n.length?[...t,...n]:t));const{targetCase:s,depth:u}=t.plain.options,a={targetCase:s,depth:u,exclude:null},i={options:a,visitedRefs:[],named:o,other:g(n),root:t};return a.exclude=i,i}function w(e,t){const n="string"==typeof t?e.named.get(t):void 0;return null!=n?n:e.other}function b(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=y(e,t),t.states.set(n,r)),r}(t.pathsBelow,e.root)}const v="[object Object]";function C(e,t,n){"__proto__"===t?Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[t]=n}function m(e){return e>="a"&&e<="z"}function k(e){return e>="A"&&e<="Z"}function U(e,t){var n,r,o,s;if(!k(null!==(n=e[t])&&void 0!==n?n:""))return!1;const u=null!==(r=e[t-1])&&void 0!==r?r:"";if(m(u)||(a=u)>="0"&&a<="9")return!0;var a;if(!(k(u)&&m(null!==(o=e[t+1])&&void 0!==o?o:"")))return!1;return!("s"===e[t+1]&&!m(null!==(s=e[t+2])&&void 0!==s?s:""))}function z(e,t){if(!e)return e;let n="";for(let r=0;r<e.length;r++)U(e,r)&&(n+=t),n+=e[r].toLowerCase();return n}const $={snake:{camel:function(e){return e.toLowerCase().replace(/_([a-z0-9])/g,(e,t)=>t.toUpperCase())},pascal:function(e){return e.toLowerCase().replace(/_+([a-z0-9])/g,(e,t)=>t.toUpperCase()).replace(/^[a-z]/,e=>e.toUpperCase())},kebab:function(e){return e.replace(/_/g,"-")},snake:e=>e},camel:{snake:function(e){return z(e,"_")},pascal:function(e){return e?e.charAt(0).toUpperCase()+e.slice(1):e},kebab:function(e){return z(e,"-")},camel:e=>e},pascal:{snake:function(e){return z(e,"_")},camel:function(e){if(!e)return e;let t=1;for(;t<e.length&&!U(e,t);)t++;return e.slice(0,t).toLowerCase()+e.slice(t)},kebab:function(e){return z(e,"-")},pascal:e=>e},kebab:{snake:function(e){return e.replace(/-/g,"_")},camel:function(e){return e.toLowerCase().replace(/-+([a-z0-9])/g,(e,t)=>t.toUpperCase())},pascal:function(e){return e.toLowerCase().replace(/-+([a-z0-9])/g,(e,t)=>t.toUpperCase()).replace(/^[a-z]/,e=>e.toUpperCase())},kebab:e=>e}};function j(c){return"string"!=typeof c?null:function(e){if(e.includes("_")||e.includes("-")||e.includes(" "))return!1;if(e.length>0&&!/^[A-Za-z0-9]+$/.test(e))return!1;if(e.length<=1)return!0;const t=e[0],n=e.slice(1);return e===e.toLowerCase()||t===t.toUpperCase()&&n===n.toLowerCase()}(c)?"single":function(n){return"string"==typeof n&&e.test(n)&&!t.test(n)&&(n.includes("_")||i.test(n))}(c)?"snake":function(e){return"string"==typeof e&&n.test(e)&&u.test(e)}(c)?"camel":function(e){return"string"==typeof e&&r.test(e)&&a.test(e)}(c)?"pascal":function(e){return"string"==typeof e&&o.test(e)&&!s.test(e)&&(e.includes("-")||i.test(e))}(c)?"kebab":null}function A(e,t){if(!e)return e;const{string:n,shouldTransform:r,sourceCase:o,singleWord:s}=function(e){if("string"!=typeof e)throw new Error("Input must be a string");if(0===e.length)return{string:e,shouldTransform:!1,sourceCase:null,singleWord:!1};if(/[^A-Za-z0-9_-]/.test(e)||/^[^A-Za-z]/.test(e)||/[^A-Za-z0-9]$/.test(e)||c.test(e))return{string:e,shouldTransform:!1,sourceCase:null,singleWord:!1};const t=j(e);return t?"single"===t?{string:e,shouldTransform:!0,sourceCase:null,singleWord:!0}:{string:e,shouldTransform:!0,sourceCase:t,singleWord:!1}:{string:e,shouldTransform:!1,sourceCase:null,singleWord:!1}}(e);return r?s?function(e,t){switch(t){case"pascal":return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase();case"camel":return e.charAt(0).toLowerCase()+e.slice(1);case"snake":case"kebab":return e.toLowerCase();default:return e}}(n,t):o&&o!==t?$[o][t](n):n:n}const _=new Map;function O(e,t){var n;if(e.length>100)return A(e,t);let r=_.get(t);r||(r={current:new Map,previous:new Map},_.set(t,r));let o=r.current.get(e);return void 0===o&&(o=null!==(n=r.previous.get(e))&&void 0!==n?n:A(e,t),r.current.size>=1e3&&(r.previous=r.current,r.current=new Map),r.current.set(e,o)),o}function x(e,t,n=[],r=0){var o;if(null===e||"object"!=typeof e)return e;if(r>=t.depth)return e;const s=t.depth===1/0?0:r,u=null!==(o=n[s])&&void 0!==o?o:n[s]=new WeakMap;if(u.has(e))return u.get(e);if(Array.isArray(e)){const o=[];return u.set(e,o),function(e,t,n,r,o){for(const s of e)t.push(x(s,n,r,o))}(e,o,t,n,r),o}const a=r+1,i=function(e){try{return Object.prototype.toString.call(e)}catch(e){return v}}(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,i)){const r=new Map;return u.set(e,r),null===t.exclude?function(e,t,n,r,o){for(const[s,u]of e)t.set("string"==typeof s?A(s,n.targetCase):s,x(u,n,r,o))}(e,r,t,n,a):function(e,t,n,r){for(const[o,s]of e){const e=w(n,o),u=b(n,e);t.set("string"!=typeof o||e.keepKey?o:A(o,n.options.targetCase),x(s,u.options,u.visitedRefs,r))}}(e,r,t.exclude,a),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,i)){const r=new Set;return u.set(e,r),function(e,t,n,r,o){for(const s of e)t.add(x(s,n,r,o))}(e,r,t,n,a),r}if(i===v){const r=e,o={};if(u.set(e,o),null===t.exclude)for(const e in r)Object.prototype.hasOwnProperty.call(r,e)&&C(o,O(e,t.targetCase),x(r[e],t,n,a));else!function(e,t,n,r){for(const o in e)if(Object.prototype.hasOwnProperty.call(e,o)){const s=w(n,o),u=b(n,s);C(t,s.keepKey?o:O(o,n.options.targetCase),x(e[o],u.options,u.visitedRefs,r))}}(r,o,t.exclude,a);for(const e of Object.getOwnPropertySymbols(r))o[e]=r[e];return o}return e}const M=["camel","snake","pascal","kebab"];function T(e){const t=e||"camel";if(n=t,!M.includes(n))throw new TypeError(`Unsupported targetCase ${f(t)}. Use one of: ${M.join(", ")}.`);var n;return t}function L(e,t){var n;const r=null==t?void 0:t.targetCase,o=null!==(n=null==t?void 0:t.depth)&&void 0!==n?n:1/0,s=T(r);if("number"!=typeof o||Number.isNaN(o))throw new TypeError(`Unsupported depth ${f(o)}. Use a number, or omit depth to transform every level.`);const u={targetCase:s,depth:o,exclude:null},a=null==t?void 0:t.exclude,i=void 0===a?null:function(e,t){if(null==e)return null;if(!Array.isArray(e))throw new TypeError(`Unsupported exclude ${f(e)}. Use an array of paths, such as ["metadata.*"].`);for(const t of e)if("string"!=typeof t||!l.test(t))throw new TypeError(`Unsupported exclude path ${f(t)}. Use non-empty keys joined with dots, such as "metadata.*".`);if(0===e.length)return null;const n={plain:{options:t,visitedRefs:[]},states:new Map};return y(h(e),n)}(a,u);return null===i?x(e,u):x(e,i.options,i.visitedRefs)}function S(e,t){if("string"!=typeof e)throw new TypeError(`Unsupported key ${f(e)}. Use a string.`);if("string"==typeof t)throw new TypeError(`Unsupported options ${f(t)}. Use an options object: { targetCase: ${f(t)} }.`);return A(e,T(null==t?void 0:t.targetCase))}export{L as transform,S as transformKey};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deep-case-crafter",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Convert object keys between camelCase, snake_case, kebab-case and PascalCase, deeply (objects, arrays, Map, Set), with exact TypeScript types",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"test-coverage": "npm test -- --coverage",
|
|
62
62
|
"test:types": "tsc --noEmit",
|
|
63
63
|
"tsc": "tsc --noEmit",
|
|
64
|
-
"test:dist-types": "tsc -p dist-type-tests && tsc -p dist-type-tests/tsconfig.declarations.json && tsc -p dist-type-tests/tsconfig.loose.json && tsc -p dist-type-tests/tsconfig.exact.json",
|
|
64
|
+
"test:dist-types": "tsc -p dist-type-tests && tsc -p dist-type-tests/tsconfig.declarations.json && node dist-type-tests/checkDeclarations.mjs && tsc -p dist-type-tests/tsconfig.consumer.json && tsc -p dist-type-tests/tsconfig.loose.json && tsc -p dist-type-tests/tsconfig.exact.json",
|
|
65
65
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx src/ --max-warnings=0 --no-cache",
|
|
66
66
|
"lint:fix": "eslint --ext .js,.jsx,.ts,.tsx src/ --fix --max-warnings=0 --no-cache",
|
|
67
67
|
"format": "prettier --write src dist-type-tests benchmark .github readme.md CHANGELOG.md SECURITY.md",
|
package/readme.md
CHANGED
|
@@ -39,6 +39,9 @@ const body = transform(user, { targetCase: 'snake' });
|
|
|
39
39
|
returned untouched.
|
|
40
40
|
- **Keys that are data keep their names**: UUIDs always, and any other key whose path
|
|
41
41
|
you list in [`exclude`](#leaving-keys-unchanged), such as `'users_by_id.*'`.
|
|
42
|
+
- **Single keys too**: [`transformKey`](#transformkeykey-options) converts one key by
|
|
43
|
+
the same rules, `transformKey('userID', { targetCase: 'snake' })` → `'user_id'`, typed
|
|
44
|
+
`'user_id'`.
|
|
42
45
|
- No dependencies, with ES module and CommonJS builds.
|
|
43
46
|
|
|
44
47
|
## Installation
|
|
@@ -162,6 +165,24 @@ console.log(transform(strings, { targetCase: 'camel' }));
|
|
|
162
165
|
|
|
163
166
|
---
|
|
164
167
|
|
|
168
|
+
### 7. Convert a single key with `transformKey`
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { transformKey } from 'deep-case-crafter';
|
|
172
|
+
|
|
173
|
+
transformKey('user_id'); // 'userId'
|
|
174
|
+
transformKey('userID', { targetCase: 'snake' }); // 'user_id'
|
|
175
|
+
|
|
176
|
+
type SortField = 'createdAt' | 'userID';
|
|
177
|
+
|
|
178
|
+
function toSortParam(field: SortField) {
|
|
179
|
+
return transformKey(field, { targetCase: 'snake' });
|
|
180
|
+
// ^? 'created_at' | 'user_id'
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
165
186
|
## How it compares
|
|
166
187
|
|
|
167
188
|
Checked in October 2026 against
|
|
@@ -191,8 +212,11 @@ Where the others are better or different:
|
|
|
191
212
|
and `objectToSnake` take about 1.1 KB gzipped, deep-case-crafter's `transform` about
|
|
192
213
|
2.6 KB.
|
|
193
214
|
- **More output**: ts-case-convert and es-toolkit also convert to SCREAMING_SNAKE_CASE,
|
|
194
|
-
|
|
195
|
-
|
|
215
|
+
which deep-case-crafter does not.
|
|
216
|
+
- **Single strings**: like `transformKey`, ts-case-convert's `toSnake('userId')` and
|
|
217
|
+
es-toolkit's `snakeCase('userId')` return `'user_id'`. es-toolkit's functions also
|
|
218
|
+
convert text, such as `'Hello World'` → `'hello_world'`, which `transformKey` returns
|
|
219
|
+
unchanged.
|
|
196
220
|
- **Digits**: ts-case-convert separates digits by default (`addressLine1` →
|
|
197
221
|
`address_line_1`), so `address_line_1` → `addressLine1` → `address_line_1`
|
|
198
222
|
round-trips. Its `ts-case-convert/no-split-numbers` entry point keeps digits attached
|
|
@@ -358,6 +382,33 @@ type of the `exclude` paths, such as `['metadata.*']`.
|
|
|
358
382
|
untouched when it has a `Symbol.toStringTag` (as every class implementing `Map` or
|
|
359
383
|
`Set` does), and rebuilt as a plain object otherwise.
|
|
360
384
|
|
|
385
|
+
### `transformKey(key, options)`
|
|
386
|
+
|
|
387
|
+
Converts a single key by the same rules as the keys of `transform`'s result.
|
|
388
|
+
|
|
389
|
+
- **key**: The key to transform, a string. Anything else throws a `TypeError`.
|
|
390
|
+
- **options**:
|
|
391
|
+
- `targetCase` (optional): as for `transform` (default: `'camel'`). Pass it in an
|
|
392
|
+
object: `transformKey(key, 'snake')` throws a `TypeError`.
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
transformKey('HTTPResponse', { targetCase: 'snake' }); // 'http_response'
|
|
396
|
+
transformKey('Hello World', { targetCase: 'snake' }); // 'Hello World'
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
It converts names, not text: a string that is not a key in one of the four cases is
|
|
400
|
+
returned unchanged, such as `'Hello World'`, `'USER_ID'`, `'_id'` or a UUID (see
|
|
401
|
+
[Key Preservation Rules](#key-preservation-rules)).
|
|
402
|
+
|
|
403
|
+
The result type is the converted key. A string literal gives a literal, a union of keys
|
|
404
|
+
or of target cases gives the union of their results, and a `string` gives `string`.
|
|
405
|
+
`TransformKey<Key, TargetCase>` names it: `TransformKey<'userID', 'snake'>` is
|
|
406
|
+
`'user_id'`. Options that are not known exactly, such as an options object that may be
|
|
407
|
+
`undefined`, give a wider type: `string`, or the key in any of the four cases.
|
|
408
|
+
|
|
409
|
+
To use it in a callback, wrap it in a function:
|
|
410
|
+
`keys.map((key) => transformKey(key, { targetCase: 'snake' }))`.
|
|
411
|
+
|
|
361
412
|
---
|
|
362
413
|
|
|
363
414
|
## Advanced: explicit output type overload
|
|
@@ -399,9 +450,9 @@ Trailing digits are kept but treated as part of the word, so they do **not** by
|
|
|
399
450
|
themselves preserve a key — `address_1` becomes `address1`, and the single word
|
|
400
451
|
`item2` stays `item2`.
|
|
401
452
|
|
|
402
|
-
These rules apply to `Map` keys and to the result types as well. To
|
|
403
|
-
unchanged, such as IDs or the keys of free-form metadata, list their
|
|
404
|
-
[`exclude`](#leaving-keys-unchanged).
|
|
453
|
+
These rules apply to `Map` keys, to `transformKey` and to the result types as well. To
|
|
454
|
+
keep other keys unchanged, such as IDs or the keys of free-form metadata, list their
|
|
455
|
+
paths in [`exclude`](#leaving-keys-unchanged).
|
|
405
456
|
|
|
406
457
|
---
|
|
407
458
|
|
|
@@ -442,8 +493,8 @@ deep-case-crafter follows [semantic versioning](https://semver.org). The v3 API
|
|
|
442
493
|
stable. Key outputs change only to fix bugs, and every change is listed in the
|
|
443
494
|
[changelog](https://github.com/gyrgy/deep-case-crafter/blob/main/CHANGELOG.md): if you
|
|
444
495
|
store converted keys, such as database columns, check it before upgrading. A minor
|
|
445
|
-
version can add an option
|
|
446
|
-
|
|
496
|
+
version can add an option, which `Required<TransformOptions>` and
|
|
497
|
+
`Required<TransformKeyOptions>` then require.
|
|
447
498
|
|
|
448
499
|
### Upgrading to v3
|
|
449
500
|
|