1 | import {CamelCase, PascalCase} from 'type-fest';
|
2 |
|
3 | // eslint-disable-next-line @typescript-eslint/ban-types
|
4 | type EmptyTuple = [];
|
5 |
|
6 | /**
|
7 | Return a default type if input type is nil.
|
8 |
|
9 | @template T - Input type.
|
10 | @template U - Default type.
|
11 | */
|
12 | type WithDefault<T, U extends T> = T extends undefined | void | null ? U : T;
|
13 |
|
14 | /**
|
15 | Check if an element is included in a tuple.
|
16 |
|
17 | TODO: Remove this once https://github.com/sindresorhus/type-fest/pull/217 is merged.
|
18 | */
|
19 | type IsInclude<List extends readonly unknown[], Target> = List extends undefined
|
20 | ? false
|
21 | : List extends Readonly<EmptyTuple>
|
22 | ? false
|
23 | : List extends readonly [infer First, ...infer Rest]
|
24 | ? First extends Target
|
25 | ? true
|
26 | : IsInclude<Rest, Target>
|
27 | : boolean;
|
28 |
|
29 | /**
|
30 | Append a segment to dot-notation path.
|
31 | */
|
32 | type AppendPath<S extends string, Last extends string> = S extends ''
|
33 | ? Last
|
34 | : `${S}.${Last}`;
|
35 |
|
36 | /**
|
37 | Convert keys of an object to camelcase strings.
|
38 | */
|
39 | type CamelCaseKeys<
|
40 | T extends Record<string, any> | readonly any[],
|
41 | Deep extends boolean,
|
42 | IsPascalCase extends boolean,
|
43 | Exclude extends readonly unknown[],
|
44 | StopPaths extends readonly string[],
|
45 | Path extends string = ''
|
46 | > = T extends readonly any[]
|
47 | // Handle arrays or tuples.
|
48 | ? {
|
49 | [P in keyof T]: CamelCaseKeys<
|
50 | T[P],
|
51 | Deep,
|
52 | IsPascalCase,
|
53 | Exclude,
|
54 | StopPaths
|
55 | >;
|
56 | }
|
57 | : T extends Record<string, any>
|
58 | // Handle objects.
|
59 | ? {
|
60 | [P in keyof T & string as [IsInclude<Exclude, P>] extends [true]
|
61 | ? P
|
62 | : [IsPascalCase] extends [true]
|
63 | ? PascalCase<P>
|
64 | : CamelCase<P>]: [IsInclude<StopPaths, AppendPath<Path, P>>] extends [
|
65 | true
|
66 | ]
|
67 | ? T[P]
|
68 | : [Deep] extends [true]
|
69 | ? T[P] extends Record<string, any>
|
70 | ? CamelCaseKeys<
|
71 | T[P],
|
72 | Deep,
|
73 | IsPascalCase,
|
74 | Exclude,
|
75 | StopPaths,
|
76 | AppendPath<Path, P>
|
77 | >
|
78 | : T[P]
|
79 | : T[P];
|
80 | }
|
81 | // Return anything else as-is.
|
82 | : T;
|
83 |
|
84 | declare namespace camelcaseKeys {
|
85 | interface Options {
|
86 | /**
|
87 | Recurse nested objects and objects in arrays.
|
88 |
|
89 | @default false
|
90 | */
|
91 | readonly deep?: boolean;
|
92 |
|
93 | /**
|
94 | Exclude keys from being camel-cased.
|
95 |
|
96 | If this option can be statically determined, it's recommended to add `as const` to it.
|
97 |
|
98 | @default []
|
99 | */
|
100 | readonly exclude?: ReadonlyArray<string | RegExp>;
|
101 |
|
102 | /**
|
103 | Exclude children at the given object paths in dot-notation from being camel-cased. For example, with an object like `{a: {b: '🦄'}}`, the object path to reach the unicorn is `'a.b'`.
|
104 |
|
105 | If this option can be statically determined, it's recommended to add `as const` to it.
|
106 |
|
107 | @default []
|
108 |
|
109 | @example
|
110 | ```
|
111 | camelcaseKeys({
|
112 | a_b: 1,
|
113 | a_c: {
|
114 | c_d: 1,
|
115 | c_e: {
|
116 | e_f: 1
|
117 | }
|
118 | }
|
119 | }, {
|
120 | deep: true,
|
121 | stopPaths: [
|
122 | 'a_c.c_e'
|
123 | ]
|
124 | }),
|
125 | // {
|
126 | // aB: 1,
|
127 | // aC: {
|
128 | // cD: 1,
|
129 | // cE: {
|
130 | // e_f: 1
|
131 | // }
|
132 | // }
|
133 | // }
|
134 | ```
|
135 | */
|
136 | readonly stopPaths?: readonly string[];
|
137 |
|
138 | /**
|
139 | Uppercase the first character as in `bye-bye` → `ByeBye`.
|
140 |
|
141 | @default false
|
142 | */
|
143 | readonly pascalCase?: boolean;
|
144 | }
|
145 | }
|
146 |
|
147 | /**
|
148 | Convert object keys to camel case using [`camelcase`](https://github.com/sindresorhus/camelcase).
|
149 |
|
150 | @param input - Object or array of objects to camel-case.
|
151 |
|
152 | @example
|
153 | ```
|
154 | import camelcaseKeys = require('camelcase-keys');
|
155 |
|
156 | // Convert an object
|
157 | camelcaseKeys({'foo-bar': true});
|
158 | //=> {fooBar: true}
|
159 |
|
160 | // Convert an array of objects
|
161 | camelcaseKeys([{'foo-bar': true}, {'bar-foo': false}]);
|
162 | //=> [{fooBar: true}, {barFoo: false}]
|
163 |
|
164 | camelcaseKeys({'foo-bar': true, nested: {unicorn_rainbow: true}}, {deep: true});
|
165 | //=> {fooBar: true, nested: {unicornRainbow: true}}
|
166 |
|
167 | // Convert object keys to pascal case
|
168 | camelcaseKeys({'foo-bar': true, nested: {unicorn_rainbow: true}}, {deep: true, pascalCase: true});
|
169 | //=> {FooBar: true, Nested: {UnicornRainbow: true}}
|
170 |
|
171 | import minimist = require('minimist');
|
172 |
|
173 | const argv = minimist(process.argv.slice(2));
|
174 | //=> {_: [], 'foo-bar': true}
|
175 |
|
176 | camelcaseKeys(argv);
|
177 | //=> {_: [], fooBar: true}
|
178 | ```
|
179 | */
|
180 | declare function camelcaseKeys<
|
181 | T extends Record<string, any> | readonly any[],
|
182 | Options extends camelcaseKeys.Options
|
183 | >(
|
184 | input: T,
|
185 | options?: Options
|
186 | ): CamelCaseKeys<
|
187 | T,
|
188 | WithDefault<Options['deep'], false>,
|
189 | WithDefault<Options['pascalCase'], false>,
|
190 | WithDefault<Options['exclude'], EmptyTuple>,
|
191 | WithDefault<Options['stopPaths'], EmptyTuple>
|
192 | >;
|
193 |
|
194 | export = camelcaseKeys;
|