deep-case-crafter 3.0.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 +287 -45
- package/dist/index.d.mts +287 -45
- package/dist/index.d.ts +287 -45
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +20 -21
- package/readme.md +222 -77
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
|
|
@@ -48,6 +53,20 @@ interface TransformOptions {
|
|
|
48
53
|
* @default Infinity
|
|
49
54
|
*/
|
|
50
55
|
depth?: number | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Keys to leave unchanged, each given as its path: the keys of the input that lead to
|
|
58
|
+
* it, joined with `.`. `'metadata.*'` keeps the keys of `metadata`, `'users_by_id.*'`
|
|
59
|
+
* the IDs of a record. `*` stands for any key. Array items and Set members add no key
|
|
60
|
+
* to a path; Map keys do, and `*` also matches a Map key that is not a string. A key
|
|
61
|
+
* that contains a `.` can only be matched by `*`. Only the keys themselves are kept:
|
|
62
|
+
* the values under them are transformed as usual.
|
|
63
|
+
*
|
|
64
|
+
* Pass the paths inline, or `as const`, to get a precise result type. Paths only known
|
|
65
|
+
* as `string[]` give an `unknown` result type.
|
|
66
|
+
*
|
|
67
|
+
* @default []
|
|
68
|
+
*/
|
|
69
|
+
exclude?: readonly string[] | undefined;
|
|
51
70
|
/**
|
|
52
71
|
* @deprecated Has no effect. Every key's case is detected from the key itself, both at
|
|
53
72
|
* runtime and in the result type, so there is nothing to declare.
|
|
@@ -55,13 +74,24 @@ interface TransformOptions {
|
|
|
55
74
|
sourceCase?: StringCase | undefined;
|
|
56
75
|
}
|
|
57
76
|
/**
|
|
58
|
-
* Rejects option names
|
|
59
|
-
* whole, so without this an object literal would
|
|
60
|
-
* 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.
|
|
61
81
|
*/
|
|
62
|
-
type NoUnknownOptions<Options> = {
|
|
63
|
-
[K in Exclude<keyof Options, keyof
|
|
82
|
+
type NoUnknownOptions<Options, Known = TransformOptions> = {
|
|
83
|
+
[K in Exclude<keyof Options, keyof Known>]: `Unknown option: ${K & string}`;
|
|
64
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>;
|
|
65
95
|
/** `To` as a plain case literal, or `'camel'` (the default) for `undefined` */
|
|
66
96
|
type CaseOrDefault<To> = To extends undefined ? 'camel' : PlainCase<To>;
|
|
67
97
|
/**
|
|
@@ -74,6 +104,39 @@ type TargetCaseOf<Options> = 'targetCase' extends keyof Options ? CaseOrDefault<
|
|
|
74
104
|
* `depth` is omitted.
|
|
75
105
|
*/
|
|
76
106
|
type DepthOf<Options> = 'depth' extends keyof Options ? Options['depth'] : undefined;
|
|
107
|
+
/**
|
|
108
|
+
* The `exclude` paths the runtime uses for one options object: `undefined` (none) when
|
|
109
|
+
* `exclude` is omitted.
|
|
110
|
+
*/
|
|
111
|
+
type ExcludeOf<Options> = 'exclude' extends keyof Options ? Options['exclude'] : undefined;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Type-level mirror of the `exclude` matching (`excludePaths.ts`).
|
|
115
|
+
*
|
|
116
|
+
* The paths that still match at a level are a union of strings: the rest of each
|
|
117
|
+
* `exclude` path below the keys that lead there. `'users_by_id.*' | 'metadata.*'` at the
|
|
118
|
+
* top becomes `'*'` below `users_by_id`. `never` stands for no path.
|
|
119
|
+
*/
|
|
120
|
+
/**
|
|
121
|
+
* A key type `K` as the paths name its keys: a single key as its string literal, so that
|
|
122
|
+
* a string enum member or a number matches the key a path names, and any other key type
|
|
123
|
+
* (`string`, a branded `string`, a template literal pattern) as it is.
|
|
124
|
+
*/
|
|
125
|
+
type KeyName<K extends string | number> = K extends string ? IsStringLiteral<K> extends true ? `${K}` : K : `${K}`;
|
|
126
|
+
/** The paths of `Paths` that end at this level: the keys they match keep their names */
|
|
127
|
+
type PathEnds<Paths extends string> = Paths extends `${string}.${string}` ? never : Paths;
|
|
128
|
+
/**
|
|
129
|
+
* The rest of each path of `Paths` that continues below the key `Key`, or below every
|
|
130
|
+
* key that no path names when `Key` is `'*'`
|
|
131
|
+
*/
|
|
132
|
+
type PathsBelow<Paths extends string, Key extends string> = Paths extends `${infer Head}.${infer Rest}` ? Head extends Key | '*' ? Rest : never : never;
|
|
133
|
+
/**
|
|
134
|
+
* The keys that a key type `K` which is not a single literal (an index signature) may
|
|
135
|
+
* stand for, as far as `Paths` is concerned: each key in it that a path continues below,
|
|
136
|
+
* and `'*'` for every other key. A key is in `K` when they share a value, as `'u1'` does
|
|
137
|
+
* with a branded `string`.
|
|
138
|
+
*/
|
|
139
|
+
type KeyCases<Paths extends string, K extends string> = (Paths extends `${infer Head}.${string}` ? Head extends '*' ? never : [Head & K] extends [never] ? never : Head : never) | '*';
|
|
77
140
|
|
|
78
141
|
/**
|
|
79
142
|
* Character classes used by the type-level key transformation. They spell out the
|
|
@@ -86,6 +149,10 @@ type LowerLetter = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | '
|
|
|
86
149
|
type UpperLetter = Uppercase<LowerLetter>;
|
|
87
150
|
/** ASCII digits */
|
|
88
151
|
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
|
152
|
+
/** The letters of hexadecimal digits */
|
|
153
|
+
type HexLetter = 'a' | 'b' | 'c' | 'd' | 'e' | 'f';
|
|
154
|
+
/** Hexadecimal digits, in either case */
|
|
155
|
+
type HexDigit = Digit | HexLetter | Uppercase<HexLetter>;
|
|
89
156
|
/** The word separators of snake_case and kebab-case */
|
|
90
157
|
type Separator = '_' | '-';
|
|
91
158
|
/** Every character a transformable key may contain (the runtime's `A-Z a-z 0-9 _ -`) */
|
|
@@ -109,12 +176,21 @@ declare const ACRONYM_PLURAL_SUFFIX = "s";
|
|
|
109
176
|
* (tail-recursive, so long keys compile).
|
|
110
177
|
*/
|
|
111
178
|
type HasOnlyKeyChars<S extends string> = S extends `${infer C}${infer Rest}` ? C extends KeyChar ? HasOnlyKeyChars<Rest> : false : true;
|
|
179
|
+
/** The canonical UUID form, where `x` stands for a hexadecimal digit (`UUID_REGEX`) */
|
|
180
|
+
type UuidPattern = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
|
|
181
|
+
/**
|
|
182
|
+
* `true` when `S` is a UUID in canonical form. `S` is compared with `UuidPattern` one
|
|
183
|
+
* character at a time, stopping at the first character that does not fit, so long keys
|
|
184
|
+
* stay cheap.
|
|
185
|
+
*/
|
|
186
|
+
type IsUuid<S extends string, Pattern extends string = UuidPattern> = Pattern extends `${infer P}${infer PatternRest}` ? S extends `${infer C}${infer Rest}` ? C extends (P extends 'x' ? HexDigit : P) ? IsUuid<Rest, PatternRest> : false : false : S extends '' ? true : false;
|
|
112
187
|
/**
|
|
113
188
|
* Keys the runtime leaves unchanged (`prepareStringTransformation`): keys containing a
|
|
114
|
-
* character outside `A-Z a-z 0-9 _ -`, keys not starting with a letter
|
|
115
|
-
* with a
|
|
189
|
+
* character outside `A-Z a-z 0-9 _ -`, keys not starting with a letter, keys ending with
|
|
190
|
+
* a separator, and UUIDs. A UUID that starts with a digit is already preserved, so only
|
|
191
|
+
* keys that the other rules do not preserve are compared with the UUID form.
|
|
116
192
|
*/
|
|
117
|
-
type IsPreservedKey<K extends string> = HasOnlyKeyChars<K> extends false ? true : K extends `${LowerLetter | UpperLetter}${string}` ? K extends `${string}${Separator}` ? true :
|
|
193
|
+
type IsPreservedKey<K extends string> = HasOnlyKeyChars<K> extends false ? true : K extends `${LowerLetter | UpperLetter}${string}` ? K extends `${string}${Separator}` ? true : IsUuid<K> : true;
|
|
118
194
|
/**
|
|
119
195
|
* The case the runtime detects for a key that is not preserved (`detectStringCase`), or
|
|
120
196
|
* `'none'` when it cannot be detected (the key is then left unchanged). A string is used
|
|
@@ -125,25 +201,21 @@ type DetectedCase<K extends string> = K extends `${string}${Separator}${string}`
|
|
|
125
201
|
type FirstChar<S extends string> = S extends `${infer C}${string}` ? C : '';
|
|
126
202
|
/**
|
|
127
203
|
* Whether a new word starts at `C`, followed by `Rest` (`isWordBoundary`): `C` is
|
|
128
|
-
* uppercase and follows a lowercase letter or digit, or it
|
|
129
|
-
*
|
|
130
|
-
* letter is a plural `s` not followed by another
|
|
204
|
+
* uppercase and follows a lowercase letter or digit, or it follows an uppercase letter
|
|
205
|
+
* and comes before a lowercase letter, so the capitals before it are an acronym or a
|
|
206
|
+
* one-letter word — unless that lowercase letter is a plural `s` not followed by another
|
|
207
|
+
* lowercase letter.
|
|
131
208
|
*/
|
|
132
|
-
type IsWordBoundary<C extends string, Prev extends string,
|
|
209
|
+
type IsWordBoundary<C extends string, Prev extends string, Rest extends string> = C extends UpperLetter ? Prev extends LowerLetter | Digit ? true : Prev 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;
|
|
133
210
|
/** camelCase/PascalCase to snake_case or kebab-case, acronym-aware (`toSeparatedCase`) */
|
|
134
|
-
type ToSeparatedCase<S extends string, Sep extends Separator, Prev extends string = '',
|
|
135
|
-
/**
|
|
136
|
-
* Lowercases the first word, up to the first word boundary, so a leading acronym is
|
|
137
|
-
* lowercased as a whole
|
|
138
|
-
*/
|
|
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;
|
|
211
|
+
type ToSeparatedCase<S extends string, Sep extends Separator, Prev extends string = '', Acc extends string = ''> = S extends `${infer C}${infer Rest}` ? ToSeparatedCase<Rest, Sep, C, `${Acc}${IsWordBoundary<C, Prev, Rest> extends true ? Sep : ''}${Lowercase<C>}`> : Acc;
|
|
140
212
|
/**
|
|
141
|
-
* PascalCase to camelCase (`pascalToCamelCase`):
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
213
|
+
* PascalCase to camelCase (`pascalToCamelCase`): lowercases the first word, up to the
|
|
214
|
+
* first word boundary, so a leading acronym is lowercased as a whole
|
|
215
|
+
* (`HTTPRequest` -> `httpRequest`) and a leading one-letter word on its own
|
|
216
|
+
* (`XAxis` -> `xAxis`)
|
|
145
217
|
*/
|
|
146
|
-
type PascalToCamelCase<S extends string> = S extends `${infer
|
|
218
|
+
type PascalToCamelCase<S extends string, Prev extends string = '', Acc extends string = ''> = S extends `${infer C}${infer Rest}` ? IsWordBoundary<C, Prev, Rest> extends true ? `${Acc}${S}` : PascalToCamelCase<Rest, C, `${Acc}${Lowercase<C>}`> : Acc;
|
|
147
219
|
/** snake_case/kebab-case to camelCase (`snakeToCamelCase`, `kebabToCamelCase`) */
|
|
148
220
|
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
221
|
/** Replaces every `From` separator with `To` (`snakeToKebabCase`, `kebabToSnakeCase`) */
|
|
@@ -155,10 +227,33 @@ type FormatKey<K extends string, From extends StringCase, To extends StringCase>
|
|
|
155
227
|
/** Applies the detected case of `K` (`transformCase`) */
|
|
156
228
|
type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
|
|
157
229
|
/**
|
|
158
|
-
*
|
|
230
|
+
* One key `K` (not a union) transformed to one plain target case `To`. A `string` or
|
|
159
231
|
* template-literal key cannot be known exactly, so it becomes `string`.
|
|
160
232
|
*/
|
|
161
|
-
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;
|
|
162
257
|
|
|
163
258
|
/** Primitive values, including branded ones such as `string & { __brand: 'UserId' }` */
|
|
164
259
|
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
@@ -203,6 +298,47 @@ type TransformValue<T, To extends StringCase, Depth extends number | undefined,
|
|
|
203
298
|
} : 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
299
|
[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>>;
|
|
205
300
|
} : T;
|
|
301
|
+
/**
|
|
302
|
+
* `TransformValue` while `exclude` paths still match: `Paths` is the rest of each of them
|
|
303
|
+
* below this level (see `excludeTypes.ts`). A key at which a path ends keeps its name,
|
|
304
|
+
* and the paths that continue below a key go on into its value. Arrays and Sets pass
|
|
305
|
+
* them on to their items, which add no key to a path. Values that no path reaches are
|
|
306
|
+
* transformed by `TransformValue`.
|
|
307
|
+
*
|
|
308
|
+
* Use `TransformResult` instead. This type is exported for the same reason as
|
|
309
|
+
* `TransformValue`.
|
|
310
|
+
*/
|
|
311
|
+
type TransformValueExcluding<T, To extends StringCase, Depth extends number | undefined, Level extends unknown[], Paths extends string> = 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[] ? TransformValueExcluding<Item, To, Depth, Level, Paths>[] : readonly TransformValueExcluding<Item, To, Depth, Level, Paths>[] : {
|
|
312
|
+
[I in keyof T]: TransformValueExcluding<T[I], To, Depth, Level, Paths>;
|
|
313
|
+
} : T extends ReadonlyMap<infer K, infer V> ? Map<ExcludingKey<K, To, PathEnds<Paths>>, TransformBelowMapKeys<V, To, Depth, NextLevel<Depth, Level>, MapPathsBelow<K, Paths>>> : T extends ReadonlySet<infer V> ? Set<TransformValueExcluding<V, To, Depth, NextLevel<Depth, Level>, Paths>> : T extends UntouchedValue ? T : T extends object ? {
|
|
314
|
+
[K in keyof T as ExcludingKey<K, To, PathEnds<Paths>>]: K extends symbol ? T[K] : TransformBelowKey<T[K], To, Depth, NextLevel<Depth, Level>, Paths, KeyName<K & (string | number)>>;
|
|
315
|
+
} : T;
|
|
316
|
+
/**
|
|
317
|
+
* A key of an object, or a member of a Map's key type: it keeps its name where a path
|
|
318
|
+
* of `Ends` ends at it, and is transformed otherwise. A key type that is not a single
|
|
319
|
+
* literal keeps its name only where `*` ends.
|
|
320
|
+
*/
|
|
321
|
+
type ExcludingKey<K, To extends StringCase, Ends extends string> = K extends string ? '*' extends Ends ? K : IsStringLiteral<K> extends true ? [KeyName<K> & Ends] extends [never] ? TransformKey<K, To> : K : TransformKey<K, To> : K;
|
|
322
|
+
/** `T` with the paths `Paths` that go on into it: by `TransformValue` once none is left */
|
|
323
|
+
type TransformBelow<T, To extends StringCase, Depth extends number | undefined, Level extends unknown[], Paths extends string> = [Paths] extends [never] ? TransformValue<T, To, Depth, Level> : TransformValueExcluding<T, To, Depth, Level, Paths>;
|
|
324
|
+
/**
|
|
325
|
+
* The value `T` under the key `Key`. A key type that is not a single literal may stand
|
|
326
|
+
* for several keys, which the paths may treat differently: each gives its own result.
|
|
327
|
+
*/
|
|
328
|
+
type TransformBelowKey<T, To extends StringCase, Depth extends number | undefined, Level extends unknown[], Paths extends string, Key extends string> = IsStringLiteral<Key> extends true ? TransformBelow<T, To, Depth, Level, PathsBelow<Paths, Key>> : TransformBelowEach<T, To, Depth, Level, Paths, KeyCases<Paths, Key>>;
|
|
329
|
+
/** `T` under each of `Keys` (distributes over them) */
|
|
330
|
+
type TransformBelowEach<T, To extends StringCase, Depth extends number | undefined, Level extends unknown[], Paths extends string, Keys extends string> = Keys extends unknown ? TransformBelow<T, To, Depth, Level, PathsBelow<Paths, Keys>> : never;
|
|
331
|
+
/**
|
|
332
|
+
* The paths below the keys of a Map with key type `K`, one tuple per set of them: a Map
|
|
333
|
+
* has one value type for all of its keys, while the paths below each key may differ.
|
|
334
|
+
* Only `*` matches a key that is not a string, and a key type that may also hold strings
|
|
335
|
+
* (`unknown`, `{}`) stands for every string key as well.
|
|
336
|
+
*/
|
|
337
|
+
type MapPathsBelow<K, Paths extends string> = K extends string ? IsStringLiteral<K> extends true ? [PathsBelow<Paths, KeyName<K>>] : EachPathsBelow<Paths, KeyCases<Paths, K>> : string extends K ? EachPathsBelow<Paths, KeyCases<Paths, string>> : [PathsBelow<Paths, '*'>];
|
|
338
|
+
/** The paths below each of `Keys`, as a tuple each (distributes over them) */
|
|
339
|
+
type EachPathsBelow<Paths extends string, Keys extends string> = Keys extends unknown ? [PathsBelow<Paths, Keys>] : never;
|
|
340
|
+
/** The value type of a Map, below each set of paths of `Below` (see `MapPathsBelow`) */
|
|
341
|
+
type TransformBelowMapKeys<V, To extends StringCase, Depth extends number | undefined, Level extends unknown[], Below> = Below extends [infer Paths extends string] ? TransformBelow<V, To, Depth, Level, Paths> : never;
|
|
206
342
|
/**
|
|
207
343
|
* Applies a numeric literal `Depth`: a negative depth transforms nothing, and a
|
|
208
344
|
* fractional depth is not supported precisely, so its result is `unknown`.
|
|
@@ -219,23 +355,60 @@ type IsNumberLiteral<N extends number> = [N & 0] extends [never] ? true : [N & 1
|
|
|
219
355
|
* `undefined`, `unknown` for a depth that is only known to be a `number`.
|
|
220
356
|
*/
|
|
221
357
|
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;
|
|
358
|
+
/**
|
|
359
|
+
* `TransformToDepth` (and `TransformWithDepth`) for the `exclude` paths `Paths`. Kept
|
|
360
|
+
* apart, so that results without `exclude` take no extra step.
|
|
361
|
+
*/
|
|
362
|
+
type TransformToDepthExcluding<T, To extends StringCase, Depth extends number | undefined, Paths extends string> = Depth extends unknown ? [Depth] extends [undefined] ? TransformValueExcluding<T, To, undefined, [], Paths> : IsNumberLiteral<Depth & number> extends true ? `${Depth & number}` extends `-${string}` ? T : `${Depth & number}` extends `${string}${'.' | 'e'}${string}` ? unknown : TransformValueExcluding<T, To, Depth & number, [], Paths> : unknown : never;
|
|
363
|
+
/**
|
|
364
|
+
* The result for an `exclude` tuple, collecting its paths into `Paths`. A union element
|
|
365
|
+
* stands for several tuples, which give their own results. A path with an empty key makes
|
|
366
|
+
* `transform` throw (`never`), and paths that are not all known (`string[]`, a rest
|
|
367
|
+
* element, a `string` element) give `unknown`.
|
|
368
|
+
*/
|
|
369
|
+
type TransformExcluding<T, To extends StringCase, Depth extends number | undefined, Exclude extends readonly string[], Paths extends string = never> = Exclude extends readonly [] ? [Paths] extends [never] ? TransformToDepth<T, To, Depth> : TransformToDepthExcluding<T, To, Depth, Paths> : Exclude extends readonly [
|
|
370
|
+
infer Path extends string,
|
|
371
|
+
...infer Rest extends readonly string[]
|
|
372
|
+
] ? Path extends unknown ? IsStringLiteral<Path> extends true ? Path extends '' | `.${string}` | `${string}.` | `${string}..${string}` ? never : TransformExcluding<T, To, Depth, Rest, Paths | Path> : unknown : never : unknown;
|
|
373
|
+
/**
|
|
374
|
+
* Includes `true` when a member of `Exclude` is an array that is not a tuple (`string[]`,
|
|
375
|
+
* or a tuple with a rest element), or when `Exclude` is `any`: paths that cannot be known.
|
|
376
|
+
* Their result is `unknown`, and so is the result of a union that includes them, so
|
|
377
|
+
* checking this first saves working out the other members.
|
|
378
|
+
*/
|
|
379
|
+
type HasUnknownPaths<Exclude> = Exclude extends readonly string[] ? number extends Exclude['length'] ? true : false : false;
|
|
380
|
+
/**
|
|
381
|
+
* The result for each member of `Exclude` (distributes over them): no path for an
|
|
382
|
+
* `undefined` one.
|
|
383
|
+
*/
|
|
384
|
+
type TransformExcludingEach<T, To extends StringCase, Depth extends number | undefined, Exclude extends readonly string[] | undefined> = true extends HasUnknownPaths<Exclude> ? unknown : Exclude extends readonly string[] ? TransformExcluding<T, To, Depth, Exclude> : TransformToDepth<T, To, Depth>;
|
|
222
385
|
/**
|
|
223
386
|
* The exact type `transform` returns for `data` of type `T`, transformed to `To` (default
|
|
224
|
-
* `'camel'`) for `Depth` nesting levels (default: all of them)
|
|
387
|
+
* `'camel'`) for `Depth` nesting levels (default: all of them), with the keys at the
|
|
388
|
+
* `Exclude` paths left unchanged (the type of the `exclude` option; default: none).
|
|
225
389
|
*
|
|
226
|
-
* `To` and `
|
|
227
|
-
* values, the result is the union of the possible results. A depth that is not
|
|
228
|
-
* literal (`number`, including `Infinity`) cannot be known, so the result is
|
|
229
|
-
* as it is for
|
|
390
|
+
* `To`, `Depth` and `Exclude` distribute over unions: when they are only known to be one
|
|
391
|
+
* of several values, the result is the union of the possible results. A depth that is not
|
|
392
|
+
* a numeric literal (`number`, including `Infinity`) cannot be known, so the result is
|
|
393
|
+
* `unknown`, as it is for paths that are not a tuple of string literals (`string[]`), and
|
|
394
|
+
* for a `To`, `Depth` or `Exclude` typed `any`. String enum members and branded literals
|
|
230
395
|
* are accepted for `To`.
|
|
231
396
|
*
|
|
232
397
|
* @example
|
|
233
398
|
* ```typescript
|
|
234
399
|
* type User = TransformResult<{ user_id: number; created_at: Date }>;
|
|
235
400
|
* // => { userId: number; createdAt: Date }
|
|
401
|
+
*
|
|
402
|
+
* type Page = TransformResult<
|
|
403
|
+
* { page_items: Record<string, { item_id: number }> },
|
|
404
|
+
* 'camel',
|
|
405
|
+
* undefined,
|
|
406
|
+
* ['page_items.*']
|
|
407
|
+
* >;
|
|
408
|
+
* // => { pageItems: { [x: string]: { itemId: number } } }
|
|
236
409
|
* ```
|
|
237
410
|
*/
|
|
238
|
-
type TransformResult<T, To extends StringCase = 'camel', Depth extends number | undefined = undefined> = IsAny<To> extends true ? unknown :
|
|
411
|
+
type TransformResult<T, To extends StringCase = 'camel', Depth extends number | undefined = undefined, Exclude extends readonly string[] | undefined = undefined> = IsAny<To | Depth | Exclude> extends true ? unknown : [Exclude] extends [undefined] ? To extends unknown ? TransformToDepth<T, PlainCase<To>, Depth> : never : To extends unknown ? TransformExcludingEach<T, PlainCase<To>, Depth, Exclude> : never;
|
|
239
412
|
/** `true` when `Options` may omit `Key` (an optional property) */
|
|
240
413
|
type IsOptional<Options, Key extends PropertyKey> = Key extends keyof Options ? Record<never, never> extends Pick<Options, Key> ? true : false : false;
|
|
241
414
|
/** `true` when the option `Key` of `Options` is typed `any` */
|
|
@@ -246,17 +419,40 @@ type IsAnyOption<Options, Key extends PropertyKey> = Key extends keyof Options ?
|
|
|
246
419
|
*/
|
|
247
420
|
type HasAnyTargetCase<Options> = Options extends unknown ? IsAnyOption<Options, 'targetCase'> : never;
|
|
248
421
|
/**
|
|
249
|
-
* The result for one options object
|
|
250
|
-
* omitted, which uses its default; those results are
|
|
251
|
-
* `strictNullChecks`, `undefined` disappears from the
|
|
422
|
+
* The result for one options object, with the `exclude` paths `Exclude`. An optional
|
|
423
|
+
* `targetCase` or `depth` may also be omitted, which uses its default; those results are
|
|
424
|
+
* added explicitly because, without `strictNullChecks`, `undefined` disappears from the
|
|
425
|
+
* property's type.
|
|
252
426
|
*/
|
|
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);
|
|
427
|
+
type TransformResultForOne<T, Options, Exclude extends readonly string[] | undefined = undefined, To extends StringCase = TargetCaseOf<Options>, Depth extends number | undefined = DepthOf<Options> extends infer D extends number | undefined ? D : never> = TransformResult<T, To, Depth, Exclude> | (IsOptional<Options, 'targetCase'> extends true ? TransformResult<T, 'camel', Depth, Exclude> : never) | (IsOptional<Options, 'depth'> extends true ? TransformResult<T, To, undefined, Exclude> : never) | (IsOptional<Options, 'targetCase'> extends true ? IsOptional<Options, 'depth'> extends true ? TransformResult<T, 'camel', undefined, Exclude> : never : never);
|
|
428
|
+
/**
|
|
429
|
+
* The result for one options object with an `exclude` option, which may also be omitted
|
|
430
|
+
* when it is optional (see `TransformResultForOne`). Options whose paths cannot be known,
|
|
431
|
+
* such as `TransformOptions` itself, give `unknown` straight away.
|
|
432
|
+
*
|
|
433
|
+
* Use `TransformResult` instead. This type is exported only so that declaration files
|
|
434
|
+
* can refer to it, as they do to `TransformResultFor`: the result of a generic wrapper
|
|
435
|
+
* whose `exclude` paths are a type parameter is written in terms of it.
|
|
436
|
+
*/
|
|
437
|
+
type TransformResultForExcluding<T, Options> = ExcludeOf<Options> extends infer Exclude extends readonly string[] | undefined ? true extends HasUnknownPaths<Exclude> ? unknown : TransformResultForOne<T, Options, Exclude> | (IsOptional<Options, 'exclude'> extends true ? TransformResultForOne<T, Options> : never) : never;
|
|
254
438
|
/**
|
|
255
439
|
* The result type for an options object. Each member of a union of options objects gives
|
|
256
|
-
* its own result, so its `targetCase` and `
|
|
257
|
-
* with a `targetCase` or `
|
|
440
|
+
* its own result, so its `targetCase`, `depth` and `exclude` stay paired. Options typed
|
|
441
|
+
* `any`, or with a `targetCase`, `depth` or `exclude` typed `any`, say nothing about the
|
|
442
|
+
* result: `unknown`.
|
|
258
443
|
*/
|
|
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;
|
|
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;
|
|
260
456
|
|
|
261
457
|
/**
|
|
262
458
|
* Transforms the keys of `data` to camelCase, at every nesting level.
|
|
@@ -268,7 +464,7 @@ type TransformResultFor<T, Options> = IsAny<Options> extends true ? unknown : Op
|
|
|
268
464
|
declare function transform<T>(data: T, options?: undefined): TransformResult<T>;
|
|
269
465
|
/**
|
|
270
466
|
* Transforms the keys of `data` to `targetCase` (default `'camel'`), at every nesting
|
|
271
|
-
* level unless `depth` is set.
|
|
467
|
+
* level unless `depth` is set, except the keys at the `exclude` paths.
|
|
272
468
|
*
|
|
273
469
|
* Every key of the result type is typed the way the runtime transforms it, except in
|
|
274
470
|
* values TypeScript cannot tell apart from plain objects (see "Result types" in the
|
|
@@ -281,12 +477,22 @@ declare function transform<T>(data: T, options?: undefined): TransformResult<T>;
|
|
|
281
477
|
* option names. An options object that may itself be `undefined` matches neither this
|
|
282
478
|
* overload nor the one above, and gets an `unknown` result type from the last one.
|
|
283
479
|
*
|
|
480
|
+
* The constraint of `Options` makes it capture an inline `exclude` array as a tuple of
|
|
481
|
+
* string literals: `[...Paths]` keeps the strings literal, and `readonly [string?]`
|
|
482
|
+
* makes TypeScript 4.7 read the array as a tuple, an empty one included; any array of
|
|
483
|
+
* strings is still accepted, and so is `undefined`: with `exactOptionalPropertyTypes`, an
|
|
484
|
+
* optional property only accepts it when its type includes it. The parameter type does
|
|
485
|
+
* not mention `exclude` itself: intersecting the captured tuple with these types would
|
|
486
|
+
* make every call compare array types member by member.
|
|
487
|
+
*
|
|
284
488
|
* The type parameter defaults only apply when type arguments are passed explicitly, and
|
|
285
489
|
* give an `unknown` result type. An explicit `any` as the second type argument was the
|
|
286
490
|
* output type before `To` existed, so it still gives `any` (`To` is `any` there, while
|
|
287
491
|
* the options keep their default type); a `targetCase` typed `any` gives `unknown`.
|
|
288
492
|
*/
|
|
289
|
-
declare function transform<T, To extends StringCase | undefined = StringCase | undefined, Depth extends number | undefined = number | undefined, Options extends TransformOptions
|
|
493
|
+
declare function transform<T, To extends StringCase | undefined = StringCase | undefined, Depth extends number | undefined = number | undefined, Options extends TransformOptions & {
|
|
494
|
+
exclude?: readonly [...Paths] | readonly [string?] | readonly string[] | undefined;
|
|
495
|
+
} = TransformOptions, Paths extends readonly string[] = readonly string[]>(data: T, options: Options & {
|
|
290
496
|
targetCase?: To;
|
|
291
497
|
depth?: Depth;
|
|
292
498
|
} & NoUnknownOptions<Options>): IsAny<To> extends true ? true extends HasAnyTargetCase<Options> ? unknown : To : TransformResultFor<T, Options>;
|
|
@@ -294,12 +500,14 @@ declare function transform<T, To extends StringCase | undefined = StringCase | u
|
|
|
294
500
|
* The 2.x form with explicit `<Data, SourceCase, TargetCase>` type arguments.
|
|
295
501
|
*
|
|
296
502
|
* @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
|
|
503
|
+
* and the result type is inferred, `depth` and `exclude` included. This form does not
|
|
504
|
+
* accept `depth` or `exclude`.
|
|
298
505
|
*/
|
|
299
506
|
declare function transform<T, From extends StringCase, To extends StringCase>(data: T, options: {
|
|
300
507
|
sourceCase: From;
|
|
301
508
|
targetCase: To;
|
|
302
509
|
depth?: undefined;
|
|
510
|
+
exclude?: undefined;
|
|
303
511
|
}): TransformResult<T, To>;
|
|
304
512
|
/**
|
|
305
513
|
* Transform data to a specified output type.
|
|
@@ -309,5 +517,39 @@ declare function transform<T, From extends StringCase, To extends StringCase>(da
|
|
|
309
517
|
*/
|
|
310
518
|
declare function transform<T, K>(data: T, options?: Omit<TransformOptions, 'sourceCase'>): K;
|
|
311
519
|
|
|
312
|
-
|
|
313
|
-
|
|
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
|
|
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
|
|
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};
|