deep-case-crafter 2.1.0 → 3.0.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 +249 -180
- package/dist/index.d.mts +249 -180
- package/dist/index.d.ts +249 -180
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +9 -4
- package/readme.md +187 -40
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `true` when `T` is `any`: a conditional type checked against `any` takes both branches,
|
|
3
|
+
* so only `any` turns `true | false` into `boolean`.
|
|
4
|
+
*
|
|
5
|
+
* Two shorter checks are not used. `0 extends 1 & T` is `false` since TypeScript 5.4 for
|
|
6
|
+
* a type parameter whose constraint excludes `1` (such as `To` in `transform`), even when
|
|
7
|
+
* `any` is passed for it. `unknown extends T` is `true` without `strictNullChecks` for an
|
|
8
|
+
* object type whose properties are all optional (such as `{ targetCase?: 'kebab' }`).
|
|
9
|
+
*/
|
|
10
|
+
type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;
|
|
11
|
+
/**
|
|
12
|
+
* `true` when `S` is a single string literal such as `'user_id'`, and `false` for
|
|
13
|
+
* `string` and template-literal patterns such as `` `user_${string}` ``.
|
|
14
|
+
*/
|
|
15
|
+
type IsStringLiteral<S extends string> = Record<never, never> extends Record<S, never> ? false : true;
|
|
16
|
+
|
|
1
17
|
/**
|
|
2
18
|
* Represents the supported string case formats for transformation
|
|
3
19
|
*/
|
|
4
20
|
type StringCase = 'camel' | 'pascal' | 'snake' | 'kebab';
|
|
21
|
+
/**
|
|
22
|
+
* The plain literal of a target case: a string enum member or a branded literal such as
|
|
23
|
+
* `'snake' & { __brand: 'Case' }` becomes `'snake'`, so it compares equal to the case
|
|
24
|
+
* detected from a key. Only literals are produced, never `To` itself: TypeScript 4.7
|
|
25
|
+
* narrows `To` to `To & StringCase` in a true branch, which is `never` for an enum member.
|
|
26
|
+
*/
|
|
27
|
+
type PlainCase<To> = To extends 'camel' ? 'camel' : To extends 'snake' ? 'snake' : To extends 'kebab' ? 'kebab' : To extends 'pascal' ? 'pascal' : never;
|
|
5
28
|
|
|
6
29
|
/**
|
|
7
30
|
* Options for the transform function
|
|
@@ -11,234 +34,280 @@ interface TransformOptions {
|
|
|
11
34
|
* The target casing format to transform to
|
|
12
35
|
* @default 'camel'
|
|
13
36
|
*/
|
|
14
|
-
targetCase?: StringCase;
|
|
37
|
+
targetCase?: StringCase | undefined;
|
|
15
38
|
/**
|
|
16
|
-
*
|
|
39
|
+
* How many nesting levels to transform: `1` transforms only the top-level keys, `2`
|
|
40
|
+
* also the keys one level down, and so on. The values of objects, Maps and Sets are one
|
|
41
|
+
* level deeper; array items stay on their array's level. Deeper values are returned
|
|
42
|
+
* as they are.
|
|
17
43
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* - Ensures consistent results if input has mixed casing
|
|
44
|
+
* Pass a numeric literal to get a precise result type. A depth that is only known to be
|
|
45
|
+
* a `number` (a variable, or `Infinity`) gives an `unknown` result type; omit `depth`
|
|
46
|
+
* to transform every level.
|
|
22
47
|
*
|
|
23
|
-
*
|
|
24
|
-
* - Automatic case detection is used at runtime
|
|
25
|
-
* - TypeScript types will be less precise (string keys)
|
|
26
|
-
* - Works with mixed case inputs (each key detected separately)
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* // With source specified - precise types
|
|
30
|
-
* const result = transform(data, { targetCase: 'camel', sourceCase: 'snake' });
|
|
31
|
-
* // result has exact transformed types
|
|
32
|
-
*
|
|
33
|
-
* // Without source - flexible runtime behavior
|
|
34
|
-
* const result = transform(data, { targetCase: 'camel' });
|
|
35
|
-
* // TypeScript types are more general
|
|
36
|
-
*
|
|
37
|
-
* @default undefined (automatic detection)
|
|
48
|
+
* @default Infinity
|
|
38
49
|
*/
|
|
39
|
-
|
|
50
|
+
depth?: number | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated Has no effect. Every key's case is detected from the key itself, both at
|
|
53
|
+
* runtime and in the result type, so there is nothing to declare.
|
|
54
|
+
*/
|
|
55
|
+
sourceCase?: StringCase | undefined;
|
|
40
56
|
}
|
|
41
|
-
|
|
42
57
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
58
|
+
* Rejects option names `transform` does not know. The options object is inferred as a
|
|
59
|
+
* whole, so without this an object literal would accept any extra property, typos such
|
|
60
|
+
* as `depht` included. The error names the unknown option.
|
|
45
61
|
*/
|
|
46
|
-
type
|
|
62
|
+
type NoUnknownOptions<Options> = {
|
|
63
|
+
[K in Exclude<keyof Options, keyof TransformOptions>]: `Unknown option: ${K & string}`;
|
|
64
|
+
};
|
|
65
|
+
/** `To` as a plain case literal, or `'camel'` (the default) for `undefined` */
|
|
66
|
+
type CaseOrDefault<To> = To extends undefined ? 'camel' : PlainCase<To>;
|
|
47
67
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
68
|
+
* The target case the runtime uses for one options object: `'camel'` when `targetCase`
|
|
69
|
+
* is omitted or `undefined`.
|
|
50
70
|
*/
|
|
51
|
-
type
|
|
52
|
-
infer First extends string,
|
|
53
|
-
...infer Rest extends string[]
|
|
54
|
-
] ? `${Capitalize<First>}${SnakeWordsToPascal<Rest>}` : '';
|
|
55
|
-
type SnakeToWords<S extends string> = S extends `${infer P}_${infer R}` ? [P, ...SnakeToWords<R>] : [S];
|
|
56
|
-
type SnakeToPascal<S extends string> = S extends `${infer P}_${infer R}` ? SnakeWordsToPascal<[P, ...SnakeToWords<R>]> : Capitalize<S>;
|
|
71
|
+
type TargetCaseOf<Options> = 'targetCase' extends keyof Options ? CaseOrDefault<Options['targetCase']> : 'camel';
|
|
57
72
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
73
|
+
* The depth the runtime uses for one options object: `undefined` (every level) when
|
|
74
|
+
* `depth` is omitted.
|
|
60
75
|
*/
|
|
61
|
-
type
|
|
76
|
+
type DepthOf<Options> = 'depth' extends keyof Options ? Options['depth'] : undefined;
|
|
77
|
+
|
|
62
78
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* Inserts `Sep` before an uppercase char when the previous char is a lowercase
|
|
68
|
-
* letter or digit, or when it ends an acronym run that starts a new word (the two
|
|
69
|
-
* preceding chars are uppercase and the next is lowercase). All output is lowercased.
|
|
79
|
+
* Character classes used by the type-level key transformation. They spell out the
|
|
80
|
+
* ASCII characters explicitly, because TypeScript's `Uppercase`/`Lowercase` are
|
|
81
|
+
* Unicode-aware and would treat a letter like `é` as an ordinary letter.
|
|
70
82
|
*/
|
|
71
|
-
|
|
72
|
-
type
|
|
73
|
-
|
|
74
|
-
type
|
|
75
|
-
|
|
76
|
-
type
|
|
83
|
+
/** Lowercase ASCII letters */
|
|
84
|
+
type LowerLetter = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
|
|
85
|
+
/** Uppercase ASCII letters */
|
|
86
|
+
type UpperLetter = Uppercase<LowerLetter>;
|
|
87
|
+
/** ASCII digits */
|
|
88
|
+
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
|
89
|
+
/** The word separators of snake_case and kebab-case */
|
|
90
|
+
type Separator = '_' | '-';
|
|
91
|
+
/** Every character a transformable key may contain (the runtime's `A-Z a-z 0-9 _ -`) */
|
|
92
|
+
type KeyChar = LowerLetter | UpperLetter | Digit | Separator;
|
|
93
|
+
|
|
77
94
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
95
|
+
* The plural suffix that stays with the acronym before it instead of starting a new
|
|
96
|
+
* word: `allowedURLs` becomes `allowed_urls`, not `allowed_ur_ls`.
|
|
80
97
|
*/
|
|
81
|
-
|
|
98
|
+
declare const ACRONYM_PLURAL_SUFFIX = "s";
|
|
99
|
+
|
|
82
100
|
/**
|
|
83
|
-
* Type
|
|
84
|
-
*
|
|
101
|
+
* Type-level mirror of the runtime key transformation (`transformCase`).
|
|
102
|
+
*
|
|
103
|
+
* Every step below follows a runtime function, so the type of a transformed key is
|
|
104
|
+
* exactly the key the runtime produces. The parity test (`typeRuntimeParity.test.ts`)
|
|
105
|
+
* checks this for thousands of keys.
|
|
85
106
|
*/
|
|
86
|
-
type CamelToPascal<S extends string> = Capitalize<S>;
|
|
87
107
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
108
|
+
* `true` when every character of `S` may appear in a transformable key
|
|
109
|
+
* (tail-recursive, so long keys compile).
|
|
90
110
|
*/
|
|
91
|
-
type
|
|
111
|
+
type HasOnlyKeyChars<S extends string> = S extends `${infer C}${infer Rest}` ? C extends KeyChar ? HasOnlyKeyChars<Rest> : false : true;
|
|
92
112
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
113
|
+
* Keys the runtime leaves unchanged (`prepareStringTransformation`): keys containing a
|
|
114
|
+
* character outside `A-Z a-z 0-9 _ -`, keys not starting with a letter and keys ending
|
|
115
|
+
* with a separator.
|
|
95
116
|
*/
|
|
96
|
-
type
|
|
117
|
+
type IsPreservedKey<K extends string> = HasOnlyKeyChars<K> extends false ? true : K extends `${LowerLetter | UpperLetter}${string}` ? K extends `${string}${Separator}` ? true : false : true;
|
|
97
118
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
119
|
+
* The case the runtime detects for a key that is not preserved (`detectStringCase`), or
|
|
120
|
+
* `'none'` when it cannot be detected (the key is then left unchanged). A string is used
|
|
121
|
+
* rather than `null`, which every type accepts without `strictNullChecks`.
|
|
100
122
|
*/
|
|
101
|
-
type
|
|
123
|
+
type DetectedCase<K extends string> = K extends `${string}${Separator}${string}` ? K extends Lowercase<K> ? K extends `${string}-${string}` ? K extends `${string}_${string}` | `${string}--${string}` ? 'none' : 'kebab' : K extends `${string}__${string}` ? 'none' : 'snake' : 'none' : K extends `${infer First}${infer Rest}` ? Rest extends Lowercase<Rest> ? 'single' : First extends LowerLetter ? 'camel' : K extends Uppercase<K> ? 'none' : 'pascal' : 'none';
|
|
124
|
+
/** The first character of `S`, or `''` */
|
|
125
|
+
type FirstChar<S extends string> = S extends `${infer C}${string}` ? C : '';
|
|
102
126
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
127
|
+
* Whether a new word starts at `C`, followed by `Rest` (`isWordBoundary`): `C` is
|
|
128
|
+
* uppercase and follows a lowercase letter or digit, or it ends an acronym that starts a
|
|
129
|
+
* new word (two uppercase letters before it, a lowercase letter after it) — unless that
|
|
130
|
+
* letter is a plural `s` not followed by another lowercase letter.
|
|
105
131
|
*/
|
|
106
|
-
type
|
|
132
|
+
type IsWordBoundary<C extends string, Prev extends string, PrevPrev extends string, Rest extends string> = C extends UpperLetter ? Prev extends LowerLetter | Digit ? true : Prev extends UpperLetter ? PrevPrev extends UpperLetter ? Rest extends `${infer Next}${infer AfterNext}` ? Next extends LowerLetter ? Next extends typeof ACRONYM_PLURAL_SUFFIX ? FirstChar<AfterNext> extends LowerLetter ? true : false : true : false : false : false : false : false;
|
|
133
|
+
/** camelCase/PascalCase to snake_case or kebab-case, acronym-aware (`toSeparatedCase`) */
|
|
134
|
+
type ToSeparatedCase<S extends string, Sep extends Separator, Prev extends string = '', PrevPrev extends string = '', Acc extends string = ''> = S extends `${infer C}${infer Rest}` ? ToSeparatedCase<Rest, Sep, C, Prev, `${Acc}${IsWordBoundary<C, Prev, PrevPrev, Rest> extends true ? Sep : ''}${Lowercase<C>}`> : Acc;
|
|
107
135
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
136
|
+
* Lowercases the first word, up to the first word boundary, so a leading acronym is
|
|
137
|
+
* lowercased as a whole
|
|
110
138
|
*/
|
|
111
|
-
type
|
|
139
|
+
type LowercaseFirstWord<S extends string, Prev extends string = '', PrevPrev extends string = '', Acc extends string = ''> = S extends `${infer C}${infer Rest}` ? IsWordBoundary<C, Prev, PrevPrev, Rest> extends true ? `${Acc}${S}` : LowercaseFirstWord<Rest, C, Prev, `${Acc}${Lowercase<C>}`> : Acc;
|
|
112
140
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
141
|
+
* PascalCase to camelCase (`pascalToCamelCase`): two capitals followed by a lowercase
|
|
142
|
+
* letter start with a one-letter word (`XAxis` -> `xAxis`), unless that letter is a
|
|
143
|
+
* plural `s` ending the acronym (`IDs` -> `ids`); otherwise the first word is lowercased
|
|
144
|
+
* as a whole (`HTTPRequest` -> `httpRequest`)
|
|
115
145
|
*/
|
|
116
|
-
type
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
type
|
|
146
|
+
type PascalToCamelCase<S extends string> = S extends `${infer A}${infer B}${infer C}${infer Rest}` ? [A, B, C] extends [UpperLetter, UpperLetter, LowerLetter] ? C extends typeof ACRONYM_PLURAL_SUFFIX ? FirstChar<Rest> extends LowerLetter ? Uncapitalize<S> : LowercaseFirstWord<S> : Uncapitalize<S> : LowercaseFirstWord<S> : LowercaseFirstWord<S>;
|
|
147
|
+
/** snake_case/kebab-case to camelCase (`snakeToCamelCase`, `kebabToCamelCase`) */
|
|
148
|
+
type SeparatedToCamelCase<S extends string, Sep extends Separator, Acc extends string = ''> = S extends `${infer Word}${Sep}${infer Next}${infer Rest}` ? SeparatedToCamelCase<Rest, Sep, `${Acc}${Word}${Uppercase<Next>}`> : `${Acc}${S}`;
|
|
149
|
+
/** Replaces every `From` separator with `To` (`snakeToKebabCase`, `kebabToSnakeCase`) */
|
|
150
|
+
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}`;
|
|
151
|
+
/** A single word in the target case (`transformSingleWord`) */
|
|
152
|
+
type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : Lowercase<K>;
|
|
153
|
+
/** A key of the detected case `From` in the target case (`formatterMap`) */
|
|
154
|
+
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' ? '_' : '-'>;
|
|
155
|
+
/** Applies the detected case of `K` (`transformCase`) */
|
|
156
|
+
type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
|
|
121
157
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
158
|
+
* The key a string key `K` becomes when transformed to `To`. A `string` or
|
|
159
|
+
* template-literal key cannot be known exactly, so it becomes `string`.
|
|
124
160
|
*/
|
|
125
|
-
type
|
|
161
|
+
type TransformKey<K extends string, To extends StringCase> = IsStringLiteral<K> extends false ? string : K extends '' ? K : IsPreservedKey<K> extends true ? K : TransformDetectedKey<K, DetectedCase<K>, To>;
|
|
162
|
+
|
|
163
|
+
/** Primitive values, including branded ones such as `string & { __brand: 'UserId' }` */
|
|
164
|
+
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
126
165
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
166
|
+
* Values the runtime returns untouched that TypeScript can recognise: functions and
|
|
167
|
+
* classes, `Date`, `RegExp`, and built-ins that declare a `Symbol.toStringTag` (typed
|
|
168
|
+
* arrays, `ArrayBuffer`, `DataView`, `Promise`, `WeakMap`, `WeakSet`, ...).
|
|
169
|
+
*
|
|
170
|
+
* `Error`, `URL` and other host objects are returned untouched as well, but their types
|
|
171
|
+
* cannot be told apart from plain objects (an `Error` is any `{ name, message }`).
|
|
129
172
|
*/
|
|
130
|
-
type
|
|
173
|
+
type UntouchedValue = Function | Date | RegExp | {
|
|
174
|
+
readonly [Symbol.toStringTag]: string;
|
|
175
|
+
};
|
|
131
176
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
177
|
+
* `true` once `Level` (the nesting level so far, as a tuple) reaches `Depth`.
|
|
178
|
+
* `Depth` is `undefined` when every level is transformed. The `undefined` check comes
|
|
179
|
+
* first and is not distributive, so it also holds without `strictNullChecks`; comparing
|
|
180
|
+
* `Depth extends Level['length']` also accepts a branded depth such as `1 & Brand`.
|
|
135
181
|
*/
|
|
136
|
-
type
|
|
137
|
-
type ForbiddenEndChars = '_' | '-' | '$' | '&' | '%' | '#' | '@' | '!' | '*' | '+' | '=' | '~' | '`' | '^' | '(' | ')' | '[' | ']' | '{' | '}' | '|' | '\\' | ':' | ';' | '"' | "'" | '<' | '>' | ',' | '.' | '?' | '/' | `${number}`;
|
|
138
|
-
type PreservedKey<K extends string> = K extends `${infer First}${string}` ? First extends ForbiddenStartChars ? true : K extends `${string}${infer Last}` ? Last extends ForbiddenEndChars ? true : K extends `${string}__${string}` | `${string}--${string}` ? true : false : false : false;
|
|
182
|
+
type IsDepthReached<Depth extends number | undefined, Level extends unknown[]> = [Depth] extends [undefined] ? false : Depth extends Level['length'] ? true : false;
|
|
139
183
|
/**
|
|
140
|
-
*
|
|
184
|
+
* The nesting level of the values of an object, Map or Set. With unlimited depth the
|
|
185
|
+
* level stays `[]`, so recursive data types reuse the same instantiation.
|
|
141
186
|
*/
|
|
142
|
-
type
|
|
187
|
+
type NextLevel<Depth extends number | undefined, Level extends unknown[]> = [
|
|
188
|
+
Depth
|
|
189
|
+
] extends [undefined] ? Level : [...Level, unknown];
|
|
143
190
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
191
|
+
* Transforms `T` the way `internalTransform` does: arrays keep their nesting level,
|
|
192
|
+
* while the values of objects, Maps and Sets are one level deeper.
|
|
193
|
+
*
|
|
194
|
+
* Arrays are typed as arrays of transformed items, which lets TypeScript defer
|
|
195
|
+
* recursive types such as a JSON value type; only tuples are mapped position by position.
|
|
196
|
+
*
|
|
197
|
+
* Use `TransformResult` instead. This type is exported only so that declaration files
|
|
198
|
+
* can refer to it: the result of a generic wrapper around `transform` is written in terms
|
|
199
|
+
* of it, and an unexported recursive type would have to be expanded there without end.
|
|
146
200
|
*/
|
|
147
|
-
type
|
|
148
|
-
[
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}>]: SnakeToCamelRecursive<T[K]>;
|
|
152
|
-
} : T;
|
|
153
|
-
type SnakeToPascalRecursive<T> = T extends Array<infer U> ? Array<SnakeToPascalRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? SnakeToPascal<K> : K, SnakeToPascalRecursive<V>> : T extends Set<infer V> ? Set<SnakeToPascalRecursive<V>> : T extends object ? {
|
|
154
|
-
[K in keyof T as TransformKey<K & string, {
|
|
155
|
-
sourceCase: 'snake';
|
|
156
|
-
targetCase: 'pascal';
|
|
157
|
-
}>]: SnakeToPascalRecursive<T[K]>;
|
|
158
|
-
} : T;
|
|
159
|
-
type SnakeToKebabRecursive<T> = T extends Array<infer U> ? Array<SnakeToKebabRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? SnakeToKebab<K> : K, SnakeToKebabRecursive<V>> : T extends Set<infer V> ? Set<SnakeToKebabRecursive<V>> : T extends object ? {
|
|
160
|
-
[K in keyof T as TransformKey<K & string, {
|
|
161
|
-
sourceCase: 'snake';
|
|
162
|
-
targetCase: 'kebab';
|
|
163
|
-
}>]: SnakeToKebabRecursive<T[K]>;
|
|
164
|
-
} : T;
|
|
165
|
-
type CamelToSnakeRecursive<T> = T extends Array<infer U> ? Array<CamelToSnakeRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? CamelToSnake<K> : K, CamelToSnakeRecursive<V>> : T extends Set<infer V> ? Set<CamelToSnakeRecursive<V>> : T extends object ? {
|
|
166
|
-
[K in keyof T as TransformKey<K & string, {
|
|
167
|
-
sourceCase: 'camel';
|
|
168
|
-
targetCase: 'snake';
|
|
169
|
-
}>]: CamelToSnakeRecursive<T[K]>;
|
|
170
|
-
} : T;
|
|
171
|
-
type CamelToPascalRecursive<T> = T extends Array<infer U> ? Array<CamelToPascalRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? CamelToPascal<K> : K, CamelToPascalRecursive<V>> : T extends Set<infer V> ? Set<CamelToPascalRecursive<V>> : T extends object ? {
|
|
172
|
-
[K in keyof T as TransformKey<K & string, {
|
|
173
|
-
sourceCase: 'camel';
|
|
174
|
-
targetCase: 'pascal';
|
|
175
|
-
}>]: CamelToPascalRecursive<T[K]>;
|
|
176
|
-
} : T;
|
|
177
|
-
type CamelToKebabRecursive<T> = T extends Array<infer U> ? Array<CamelToKebabRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? CamelToKebab<K> : K, CamelToKebabRecursive<V>> : T extends Set<infer V> ? Set<CamelToKebabRecursive<V>> : T extends object ? {
|
|
178
|
-
[K in keyof T as TransformKey<K & string, {
|
|
179
|
-
sourceCase: 'camel';
|
|
180
|
-
targetCase: 'kebab';
|
|
181
|
-
}>]: CamelToKebabRecursive<T[K]>;
|
|
182
|
-
} : T;
|
|
183
|
-
type PascalToCamelRecursive<T> = T extends Array<infer U> ? Array<PascalToCamelRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? PascalToCamel<K> : K, PascalToCamelRecursive<V>> : T extends Set<infer V> ? Set<PascalToCamelRecursive<V>> : T extends object ? {
|
|
184
|
-
[K in keyof T as TransformKey<K & string, {
|
|
185
|
-
sourceCase: 'pascal';
|
|
186
|
-
targetCase: 'camel';
|
|
187
|
-
}>]: PascalToCamelRecursive<T[K]>;
|
|
188
|
-
} : T;
|
|
189
|
-
type PascalToSnakeRecursive<T> = T extends Array<infer U> ? Array<PascalToSnakeRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? PascalToSnake<K> : K, PascalToSnakeRecursive<V>> : T extends Set<infer V> ? Set<PascalToSnakeRecursive<V>> : T extends object ? {
|
|
190
|
-
[K in keyof T as TransformKey<K & string, {
|
|
191
|
-
sourceCase: 'pascal';
|
|
192
|
-
targetCase: 'snake';
|
|
193
|
-
}>]: PascalToSnakeRecursive<T[K]>;
|
|
194
|
-
} : T;
|
|
195
|
-
type PascalToKebabRecursive<T> = T extends Array<infer U> ? Array<PascalToKebabRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? PascalToKebab<K> : K, PascalToKebabRecursive<V>> : T extends Set<infer V> ? Set<PascalToKebabRecursive<V>> : T extends object ? {
|
|
196
|
-
[K in keyof T as TransformKey<K & string, {
|
|
197
|
-
sourceCase: 'pascal';
|
|
198
|
-
targetCase: 'kebab';
|
|
199
|
-
}>]: PascalToKebabRecursive<T[K]>;
|
|
200
|
-
} : T;
|
|
201
|
-
type KebabToCamelRecursive<T> = T extends Array<infer U> ? Array<KebabToCamelRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? KebabToCamel<K> : K, KebabToCamelRecursive<V>> : T extends Set<infer V> ? Set<KebabToCamelRecursive<V>> : T extends object ? {
|
|
202
|
-
[K in keyof T as TransformKey<K & string, {
|
|
203
|
-
sourceCase: 'kebab';
|
|
204
|
-
targetCase: 'camel';
|
|
205
|
-
}>]: KebabToCamelRecursive<T[K]>;
|
|
206
|
-
} : T;
|
|
207
|
-
type KebabToPascalRecursive<T> = T extends Array<infer U> ? Array<KebabToPascalRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? KebabToPascal<K> : K, KebabToPascalRecursive<V>> : T extends Set<infer V> ? Set<KebabToPascalRecursive<V>> : T extends object ? {
|
|
208
|
-
[K in keyof T as TransformKey<K & string, {
|
|
209
|
-
sourceCase: 'kebab';
|
|
210
|
-
targetCase: 'pascal';
|
|
211
|
-
}>]: KebabToPascalRecursive<T[K]>;
|
|
212
|
-
} : T;
|
|
213
|
-
type KebabToSnakeRecursive<T> = T extends Array<infer U> ? Array<KebabToSnakeRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? KebabToSnake<K> : K, KebabToSnakeRecursive<V>> : T extends Set<infer V> ? Set<KebabToSnakeRecursive<V>> : T extends object ? {
|
|
214
|
-
[K in keyof T as TransformKey<K & string, {
|
|
215
|
-
sourceCase: 'kebab';
|
|
216
|
-
targetCase: 'snake';
|
|
217
|
-
}>]: KebabToSnakeRecursive<T[K]>;
|
|
201
|
+
type TransformValue<T, To extends StringCase, Depth extends number | undefined, Level extends unknown[]> = IsAny<T> extends true ? T : IsDepthReached<Depth, Level> extends true ? T : T extends Primitive ? T : T extends readonly (infer Item)[] ? number extends T['length'] ? T extends unknown[] ? TransformValue<Item, To, Depth, Level>[] : readonly TransformValue<Item, To, Depth, Level>[] : {
|
|
202
|
+
[I in keyof T]: TransformValue<T[I], To, Depth, Level>;
|
|
203
|
+
} : T extends ReadonlyMap<infer K, infer V> ? Map<K extends string ? TransformKey<K, To> : K, TransformValue<V, To, Depth, NextLevel<Depth, Level>>> : T extends ReadonlySet<infer V> ? Set<TransformValue<V, To, Depth, NextLevel<Depth, Level>>> : T extends UntouchedValue ? T : T extends object ? {
|
|
204
|
+
[K in keyof T as K extends string ? TransformKey<K, To> : K]: K extends symbol ? T[K] : TransformValue<T[K], To, Depth, NextLevel<Depth, Level>>;
|
|
218
205
|
} : T;
|
|
219
206
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
207
|
+
* Applies a numeric literal `Depth`: a negative depth transforms nothing, and a
|
|
208
|
+
* fractional depth is not supported precisely, so its result is `unknown`.
|
|
209
|
+
*/
|
|
210
|
+
type TransformWithDepth<T, To extends StringCase, Depth extends number> = `${Depth}` extends `-${string}` ? T : `${Depth}` extends `${string}${'.' | 'e'}${string}` ? unknown : TransformValue<T, To, Depth, []>;
|
|
211
|
+
/**
|
|
212
|
+
* `true` for a numeric literal, branded or not (`2`, `2 & Brand`), and `false` for
|
|
213
|
+
* `number` and a branded `number`: a literal shares no value with at least one of two
|
|
214
|
+
* different literals, while `number` shares one with both.
|
|
215
|
+
*/
|
|
216
|
+
type IsNumberLiteral<N extends number> = [N & 0] extends [never] ? true : [N & 1] extends [never] ? true : false;
|
|
217
|
+
/**
|
|
218
|
+
* The result for one `Depth` (distributes over a union of depths): every level for
|
|
219
|
+
* `undefined`, `unknown` for a depth that is only known to be a `number`.
|
|
220
|
+
*/
|
|
221
|
+
type TransformToDepth<T, To extends StringCase, Depth extends number | undefined> = Depth extends unknown ? [Depth] extends [undefined] ? TransformValue<T, To, undefined, []> : IsNumberLiteral<Depth & number> extends true ? TransformWithDepth<T, To, Depth & number> : unknown : never;
|
|
222
|
+
/**
|
|
223
|
+
* The exact type `transform` returns for `data` of type `T`, transformed to `To` (default
|
|
224
|
+
* `'camel'`) for `Depth` nesting levels (default: all of them).
|
|
225
|
+
*
|
|
226
|
+
* `To` and `Depth` distribute over unions: when they are only known to be one of several
|
|
227
|
+
* values, the result is the union of the possible results. A depth that is not a numeric
|
|
228
|
+
* literal (`number`, including `Infinity`) cannot be known, so the result is `unknown`,
|
|
229
|
+
* as it is for a `To` or `Depth` typed `any`. String enum members and branded literals
|
|
230
|
+
* are accepted for `To`.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* type User = TransformResult<{ user_id: number; created_at: Date }>;
|
|
235
|
+
* // => { userId: number; createdAt: Date }
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
type TransformResult<T, To extends StringCase = 'camel', Depth extends number | undefined = undefined> = IsAny<To> extends true ? unknown : IsAny<Depth> extends true ? unknown : To extends unknown ? TransformToDepth<T, PlainCase<To>, Depth> : never;
|
|
239
|
+
/** `true` when `Options` may omit `Key` (an optional property) */
|
|
240
|
+
type IsOptional<Options, Key extends PropertyKey> = Key extends keyof Options ? Record<never, never> extends Pick<Options, Key> ? true : false : false;
|
|
241
|
+
/** `true` when the option `Key` of `Options` is typed `any` */
|
|
242
|
+
type IsAnyOption<Options, Key extends PropertyKey> = Key extends keyof Options ? IsAny<Options[Key]> : false;
|
|
243
|
+
/**
|
|
244
|
+
* Includes `true` when a member of a union of options objects has a `targetCase` typed
|
|
245
|
+
* `any` (check it with `true extends HasAnyTargetCase<Options>`)
|
|
246
|
+
*/
|
|
247
|
+
type HasAnyTargetCase<Options> = Options extends unknown ? IsAnyOption<Options, 'targetCase'> : never;
|
|
248
|
+
/**
|
|
249
|
+
* The result for one options object. An optional `targetCase` or `depth` may also be
|
|
250
|
+
* omitted, which uses its default; those results are added explicitly because, without
|
|
251
|
+
* `strictNullChecks`, `undefined` disappears from the property's type.
|
|
252
|
+
*/
|
|
253
|
+
type TransformResultForOne<T, Options, To extends StringCase = TargetCaseOf<Options>, Depth extends number | undefined = DepthOf<Options> extends infer D extends number | undefined ? D : never> = TransformResult<T, To, Depth> | (IsOptional<Options, 'targetCase'> extends true ? TransformResult<T, 'camel', Depth> : never) | (IsOptional<Options, 'depth'> extends true ? TransformResult<T, To, undefined> : never) | (IsOptional<Options, 'targetCase'> extends true ? IsOptional<Options, 'depth'> extends true ? TransformResult<T, 'camel', undefined> : never : never);
|
|
254
|
+
/**
|
|
255
|
+
* The result type for an options object. Each member of a union of options objects gives
|
|
256
|
+
* its own result, so its `targetCase` and `depth` stay paired. Options typed `any`, or
|
|
257
|
+
* with a `targetCase` or `depth` typed `any`, say nothing about the result: `unknown`.
|
|
222
258
|
*/
|
|
223
|
-
type
|
|
259
|
+
type TransformResultFor<T, Options> = IsAny<Options> extends true ? unknown : Options extends unknown ? IsAnyOption<Options, 'targetCase'> extends true ? unknown : IsAnyOption<Options, 'depth'> extends true ? unknown : TransformResultForOne<T, Options> : never;
|
|
224
260
|
|
|
225
|
-
|
|
226
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Transforms the keys of `data` to camelCase, at every nesting level.
|
|
263
|
+
*
|
|
264
|
+
* Every key of the result type is typed the way the runtime transforms it, except in
|
|
265
|
+
* values TypeScript cannot tell apart from plain objects (see "Result types" in the
|
|
266
|
+
* readme).
|
|
267
|
+
*/
|
|
268
|
+
declare function transform<T>(data: T, options?: undefined): TransformResult<T>;
|
|
269
|
+
/**
|
|
270
|
+
* Transforms the keys of `data` to `targetCase` (default `'camel'`), at every nesting
|
|
271
|
+
* level unless `depth` is set.
|
|
272
|
+
*
|
|
273
|
+
* Every key of the result type is typed the way the runtime transforms it, except in
|
|
274
|
+
* values TypeScript cannot tell apart from plain objects (see "Result types" in the
|
|
275
|
+
* readme).
|
|
276
|
+
*
|
|
277
|
+
* `To` and `Depth` capture the literal values of `targetCase` and `depth`. `Options`
|
|
278
|
+
* captures the options object as a whole, so a value that may be `undefined` (which the
|
|
279
|
+
* runtime treats as omitted) is reflected in the result type, and each member of a union
|
|
280
|
+
* of options objects gets its own result. `NoUnknownOptions` keeps rejecting unknown
|
|
281
|
+
* option names. An options object that may itself be `undefined` matches neither this
|
|
282
|
+
* overload nor the one above, and gets an `unknown` result type from the last one.
|
|
283
|
+
*
|
|
284
|
+
* The type parameter defaults only apply when type arguments are passed explicitly, and
|
|
285
|
+
* give an `unknown` result type. An explicit `any` as the second type argument was the
|
|
286
|
+
* output type before `To` existed, so it still gives `any` (`To` is `any` there, while
|
|
287
|
+
* the options keep their default type); a `targetCase` typed `any` gives `unknown`.
|
|
288
|
+
*/
|
|
289
|
+
declare function transform<T, To extends StringCase | undefined = StringCase | undefined, Depth extends number | undefined = number | undefined, Options extends TransformOptions = TransformOptions>(data: T, options: Options & {
|
|
290
|
+
targetCase?: To;
|
|
291
|
+
depth?: Depth;
|
|
292
|
+
} & NoUnknownOptions<Options>): IsAny<To> extends true ? true extends HasAnyTargetCase<Options> ? unknown : To : TransformResultFor<T, Options>;
|
|
293
|
+
/**
|
|
294
|
+
* The 2.x form with explicit `<Data, SourceCase, TargetCase>` type arguments.
|
|
295
|
+
*
|
|
296
|
+
* @deprecated `sourceCase` has no effect: leave out the type arguments and `sourceCase`,
|
|
297
|
+
* and the result type is inferred, `depth` included. This form does not accept `depth`.
|
|
298
|
+
*/
|
|
299
|
+
declare function transform<T, From extends StringCase, To extends StringCase>(data: T, options: {
|
|
227
300
|
sourceCase: From;
|
|
228
301
|
targetCase: To;
|
|
229
|
-
|
|
302
|
+
depth?: undefined;
|
|
303
|
+
}): TransformResult<T, To>;
|
|
230
304
|
/**
|
|
231
305
|
* Transform data to a specified output type.
|
|
232
306
|
*
|
|
233
307
|
* Note: When using this overload, type safety is not enforced between the input, options, and output type.
|
|
234
|
-
* The 'sourceCase' option is
|
|
235
|
-
*/
|
|
236
|
-
declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sourceCase'>
|
|
237
|
-
depth?: number;
|
|
238
|
-
}): K;
|
|
239
|
-
declare function transform<T>(data: T, options?: TransformOptions & {
|
|
240
|
-
depth?: number;
|
|
241
|
-
}): unknown;
|
|
308
|
+
* The deprecated 'sourceCase' option is not accepted here, as it has no effect.
|
|
309
|
+
*/
|
|
310
|
+
declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sourceCase'>): K;
|
|
242
311
|
|
|
243
312
|
export { transform };
|
|
244
|
-
export type { StringCase, TransformOptions };
|
|
313
|
+
export type { StringCase, TransformOptions, TransformResult, TransformResultFor, TransformValue };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e=
|
|
1
|
+
"use strict";const e="[object Object]";function t(e,t,r){"__proto__"===t?Object.defineProperty(e,t,{value:r,enumerable:!0,writable:!0,configurable:!0}):e[t]=r}function r(e){return e>="a"&&e<="z"}function n(e){return e>="A"&&e<="Z"}function o(e,t){var o,s,u,a,c;if(!n(null!==(o=e[t])&&void 0!==o?o:""))return!1;const i=null!==(s=e[t-1])&&void 0!==s?s:"";if(r(i)||(l=i)>="0"&&l<="9")return!0;var l;if(!(n(i)&&n(null!==(u=e[t-2])&&void 0!==u?u:"")&&r(null!==(a=e[t+1])&&void 0!==a?a:"")))return!1;return!("s"===e[t+1]&&!r(null!==(c=e[t+2])&&void 0!==c?c:""))}function s(e,t){if(!e)return e;let r="";for(let n=0;n<e.length;n++)o(e,n)&&(r+=t),r+=e[n].toLowerCase();return r}const u={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 s(e,"_")},pascal:function(e){return e?e.charAt(0).toUpperCase()+e.slice(1):e},kebab:function(e){return s(e,"-")},camel:e=>e},pascal:{snake:function(e){return s(e,"_")},camel:function(e){var t,s,u;if(!e)return e;const a=null!==(t=e[2])&&void 0!==t?t:"";if(n(e[0])&&n(null!==(s=e[1])&&void 0!==s?s:"")&&r(a)&&("s"!==a||r(null!==(u=e[3])&&void 0!==u?u:"")))return e[0].toLowerCase()+e.slice(1);let c=1;for(;c<e.length&&!o(e,c);)c++;return e.slice(0,c).toLowerCase()+e.slice(c)},kebab:function(e){return s(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}};const a=/^[a-z0-9_]+$/,c=/__+/,i=/^[a-z][a-zA-Z0-9]*$/,l=/^[A-Z][a-zA-Z0-9]*$/,f=/^[a-z0-9-]+$/,p=/-{2,}/,g=/[A-Z]/,d=/[a-z]/,b=/^[a-z0-9]+$/;function h(e){return"string"!=typeof e?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],r=e.slice(1);return e===e.toLowerCase()||t===t.toUpperCase()&&r===r.toLowerCase()}(e)?"single":function(e){return"string"==typeof e&&a.test(e)&&!c.test(e)&&(e.includes("_")||b.test(e))}(e)?"snake":function(e){return"string"==typeof e&&i.test(e)&&g.test(e)}(e)?"camel":function(e){return"string"==typeof e&&l.test(e)&&d.test(e)}(e)?"pascal":function(e){return"string"==typeof e&&f.test(e)&&!p.test(e)&&(e.includes("-")||b.test(e))}(e)?"kebab":null}function y(e,t){if(!e)return e;const{string:r,shouldTransform:n,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))return{string:e,shouldTransform:!1,sourceCase:null,singleWord:!1};const t=h(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 n?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}}(r,t):o&&o!==t?u[o][t](r):r:r}const C=new Map;function v(e,t){var r;if(e.length>100)return y(e,t);let n=C.get(t);n||(n={current:new Map,previous:new Map},C.set(t,n));let o=n.current.get(e);return void 0===o&&(o=null!==(r=n.previous.get(e))&&void 0!==r?r:y(e,t),n.current.size>=1e3&&(n.previous=n.current,n.current=new Map),n.current.set(e,o)),o}function w(r,n,o=[],s=0){var u;if(null===r||"object"!=typeof r)return r;if(s>=n.depth)return r;const a=n.depth===1/0?0:s,c=null!==(u=o[a])&&void 0!==u?u:o[a]=new WeakMap;if(c.has(r))return c.get(r);if(Array.isArray(r)){const e=[];return c.set(r,e),function(e,t,r,n,o){for(const s of e)t.push(w(s,r,n,o))}(r,e,n,o,s),e}const i=s+1,l=function(t){try{return Object.prototype.toString.call(t)}catch(t){return e}}(r);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}}(r,l)){const e=new Map;return c.set(r,e),function(e,t,r,n,o){for(const[s,u]of e)t.set("string"==typeof s?y(s,r.targetCase):s,w(u,r,n,o))}(r,e,n,o,i),e}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}}(r,l)){const e=new Set;return c.set(r,e),function(e,t,r,n,o){for(const s of e)t.add(w(s,r,n,o))}(r,e,n,o,i),e}if(l===e){const e=r,s={};c.set(r,s);for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&t(s,v(r,n.targetCase),w(e[r],n,o,i));for(const t of Object.getOwnPropertySymbols(e))s[t]=e[t];return s}return r}const m=["camel","snake","pascal","kebab"];function z(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)}exports.transform=function(e,t){var r;const n=(null==t?void 0:t.targetCase)||"camel",o=null!==(r=null==t?void 0:t.depth)&&void 0!==r?r:1/0;if(s=n,!m.includes(s))throw new TypeError(`Unsupported targetCase ${z(n)}. Use one of: ${m.join(", ")}.`);var s;if("number"!=typeof o||Number.isNaN(o))throw new TypeError(`Unsupported depth ${z(o)}. Use a number, or omit depth to transform every level.`);return w(e,{targetCase:n,depth:o})};
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=
|
|
1
|
+
const e="[object Object]";function t(e,t,r){"__proto__"===t?Object.defineProperty(e,t,{value:r,enumerable:!0,writable:!0,configurable:!0}):e[t]=r}function r(e){return e>="a"&&e<="z"}function n(e){return e>="A"&&e<="Z"}function o(e,t){var o,s,u,a,c;if(!n(null!==(o=e[t])&&void 0!==o?o:""))return!1;const i=null!==(s=e[t-1])&&void 0!==s?s:"";if(r(i)||(l=i)>="0"&&l<="9")return!0;var l;if(!(n(i)&&n(null!==(u=e[t-2])&&void 0!==u?u:"")&&r(null!==(a=e[t+1])&&void 0!==a?a:"")))return!1;return!("s"===e[t+1]&&!r(null!==(c=e[t+2])&&void 0!==c?c:""))}function s(e,t){if(!e)return e;let r="";for(let n=0;n<e.length;n++)o(e,n)&&(r+=t),r+=e[n].toLowerCase();return r}const u={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 s(e,"_")},pascal:function(e){return e?e.charAt(0).toUpperCase()+e.slice(1):e},kebab:function(e){return s(e,"-")},camel:e=>e},pascal:{snake:function(e){return s(e,"_")},camel:function(e){var t,s,u;if(!e)return e;const a=null!==(t=e[2])&&void 0!==t?t:"";if(n(e[0])&&n(null!==(s=e[1])&&void 0!==s?s:"")&&r(a)&&("s"!==a||r(null!==(u=e[3])&&void 0!==u?u:"")))return e[0].toLowerCase()+e.slice(1);let c=1;for(;c<e.length&&!o(e,c);)c++;return e.slice(0,c).toLowerCase()+e.slice(c)},kebab:function(e){return s(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}};const a=/^[a-z0-9_]+$/,c=/__+/,i=/^[a-z][a-zA-Z0-9]*$/,l=/^[A-Z][a-zA-Z0-9]*$/,f=/^[a-z0-9-]+$/,p=/-{2,}/,g=/[A-Z]/,d=/[a-z]/,b=/^[a-z0-9]+$/;function h(e){return"string"!=typeof e?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],r=e.slice(1);return e===e.toLowerCase()||t===t.toUpperCase()&&r===r.toLowerCase()}(e)?"single":function(e){return"string"==typeof e&&a.test(e)&&!c.test(e)&&(e.includes("_")||b.test(e))}(e)?"snake":function(e){return"string"==typeof e&&i.test(e)&&g.test(e)}(e)?"camel":function(e){return"string"==typeof e&&l.test(e)&&d.test(e)}(e)?"pascal":function(e){return"string"==typeof e&&f.test(e)&&!p.test(e)&&(e.includes("-")||b.test(e))}(e)?"kebab":null}function y(e,t){if(!e)return e;const{string:r,shouldTransform:n,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))return{string:e,shouldTransform:!1,sourceCase:null,singleWord:!1};const t=h(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 n?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}}(r,t):o&&o!==t?u[o][t](r):r:r}const C=new Map;function v(e,t){var r;if(e.length>100)return y(e,t);let n=C.get(t);n||(n={current:new Map,previous:new Map},C.set(t,n));let o=n.current.get(e);return void 0===o&&(o=null!==(r=n.previous.get(e))&&void 0!==r?r:y(e,t),n.current.size>=1e3&&(n.previous=n.current,n.current=new Map),n.current.set(e,o)),o}function w(r,n,o=[],s=0){var u;if(null===r||"object"!=typeof r)return r;if(s>=n.depth)return r;const a=n.depth===1/0?0:s,c=null!==(u=o[a])&&void 0!==u?u:o[a]=new WeakMap;if(c.has(r))return c.get(r);if(Array.isArray(r)){const e=[];return c.set(r,e),function(e,t,r,n,o){for(const s of e)t.push(w(s,r,n,o))}(r,e,n,o,s),e}const i=s+1,l=function(t){try{return Object.prototype.toString.call(t)}catch(t){return e}}(r);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}}(r,l)){const e=new Map;return c.set(r,e),function(e,t,r,n,o){for(const[s,u]of e)t.set("string"==typeof s?y(s,r.targetCase):s,w(u,r,n,o))}(r,e,n,o,i),e}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}}(r,l)){const e=new Set;return c.set(r,e),function(e,t,r,n,o){for(const s of e)t.add(w(s,r,n,o))}(r,e,n,o,i),e}if(l===e){const e=r,s={};c.set(r,s);for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&t(s,v(r,n.targetCase),w(e[r],n,o,i));for(const t of Object.getOwnPropertySymbols(e))s[t]=e[t];return s}return r}const m=["camel","snake","pascal","kebab"];function z(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 k(e,t){var r;const n=(null==t?void 0:t.targetCase)||"camel",o=null!==(r=null==t?void 0:t.depth)&&void 0!==r?r:1/0;if(s=n,!m.includes(s))throw new TypeError(`Unsupported targetCase ${z(n)}. Use one of: ${m.join(", ")}.`);var s;if("number"!=typeof o||Number.isNaN(o))throw new TypeError(`Unsupported depth ${z(o)}. Use a number, or omit depth to transform every level.`);return w(e,{targetCase:n,depth:o})}export{k as transform};
|