1 | import {PackageJson} from 'type-fest';
|
2 |
|
3 | export type FlagType = 'string' | 'boolean' | 'number';
|
4 |
|
5 | /**
|
6 | Callback function to determine if a flag is required during runtime.
|
7 |
|
8 | @param flags - Contains the flags converted to camel-case excluding aliases.
|
9 | @param input - Contains the non-flag arguments.
|
10 |
|
11 | @returns True if the flag is required, otherwise false.
|
12 | */
|
13 | export type IsRequiredPredicate = (flags: Readonly<AnyFlags>, input: readonly string[]) => boolean;
|
14 |
|
15 | export interface Flag<Type extends FlagType, Default, IsMultiple = false> {
|
16 | readonly type?: Type;
|
17 | readonly alias?: string;
|
18 | readonly default?: Default;
|
19 | readonly isRequired?: boolean | IsRequiredPredicate;
|
20 | readonly isMultiple?: IsMultiple;
|
21 | }
|
22 |
|
23 | type StringFlag = Flag<'string', string> | Flag<'string', string[], true>;
|
24 | type BooleanFlag = Flag<'boolean', boolean> | Flag<'boolean', boolean[], true>;
|
25 | type NumberFlag = Flag<'number', number> | Flag<'number', number[], true>;
|
26 | type AnyFlag = StringFlag | BooleanFlag | NumberFlag;
|
27 | type AnyFlags = Record<string, AnyFlag>;
|
28 |
|
29 | export interface Options<Flags extends AnyFlags> {
|
30 | /**
|
31 | Pass in [`import.meta`](https://nodejs.org/dist/latest/docs/api/esm.html#esm_import_meta). This is used to find the correct package.json file.
|
32 | */
|
33 | readonly importMeta: ImportMeta;
|
34 |
|
35 | /**
|
36 | Define argument flags.
|
37 |
|
38 | The key is the flag name in camel-case and the value is an object with any of:
|
39 |
|
40 | - `type`: Type of value. (Possible values: `string` `boolean` `number`)
|
41 | - `alias`: Usually used to define a short flag alias.
|
42 | - `default`: Default value when the flag is not specified.
|
43 | - `isRequired`: Determine if the flag is required.
|
44 | If it's only known at runtime whether the flag is required or not you can pass a Function instead of a boolean, which based on the given flags and other non-flag arguments should decide if the flag is required.
|
45 | - `isMultiple`: Indicates a flag can be set multiple times. Values are turned into an array. (Default: false)
|
46 | Multiple values are provided by specifying the flag multiple times, for example, `$ foo -u rainbow -u cat`. Space- or comma-separated values are *not* supported.
|
47 |
|
48 | Note that flags are always defined using a camel-case key (`myKey`), but will match arguments in kebab-case (`--my-key`).
|
49 |
|
50 | @example
|
51 | ```
|
52 | flags: {
|
53 | unicorn: {
|
54 | type: 'string',
|
55 | alias: 'u',
|
56 | default: ['rainbow', 'cat'],
|
57 | isMultiple: true,
|
58 | isRequired: (flags, input) => {
|
59 | if (flags.otherFlag) {
|
60 | return true;
|
61 | }
|
62 |
|
63 | return false;
|
64 | }
|
65 | }
|
66 | }
|
67 | ```
|
68 | */
|
69 | readonly flags?: Flags;
|
70 |
|
71 | /**
|
72 | Description to show above the help text. Default: The package.json `"description"` property.
|
73 |
|
74 | Set it to `false` to disable it altogether.
|
75 | */
|
76 | readonly description?: string | false;
|
77 |
|
78 | /**
|
79 | The help text you want shown.
|
80 |
|
81 | The input is reindented and starting/ending newlines are trimmed which means you can use a [template literal](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/template_strings) without having to care about using the correct amount of indent.
|
82 |
|
83 | The description will be shown above your help text automatically.
|
84 |
|
85 | Set it to `false` to disable it altogether.
|
86 | */
|
87 | readonly help?: string | false;
|
88 |
|
89 | /**
|
90 | Set a custom version output. Default: The package.json `"version"` property.
|
91 |
|
92 | Set it to `false` to disable it altogether.
|
93 | */
|
94 | readonly version?: string | false;
|
95 |
|
96 | /**
|
97 | Automatically show the help text when the `--help` flag is present. Useful to set this value to `false` when a CLI manages child CLIs with their own help text.
|
98 |
|
99 | This option is only considered when there is only one argument in `process.argv`.
|
100 | */
|
101 | readonly autoHelp?: boolean;
|
102 |
|
103 | /**
|
104 | Automatically show the version text when the `--version` flag is present. Useful to set this value to `false` when a CLI manages child CLIs with their own version text.
|
105 |
|
106 | This option is only considered when there is only one argument in `process.argv`.
|
107 | */
|
108 | readonly autoVersion?: boolean;
|
109 |
|
110 | /**
|
111 | `package.json` as an `Object`. Default: Closest `package.json` upwards.
|
112 |
|
113 | _You most likely don't need this option._
|
114 | */
|
115 | readonly pkg?: Record<string, unknown>;
|
116 |
|
117 | /**
|
118 | Custom arguments object.
|
119 |
|
120 | @default process.argv.slice(2)
|
121 | */
|
122 | readonly argv?: readonly string[];
|
123 |
|
124 | /**
|
125 | Infer the argument type.
|
126 |
|
127 | By default, the argument `5` in `$ foo 5` becomes a string. Enabling this would infer it as a number.
|
128 |
|
129 | @default false
|
130 | */
|
131 | readonly inferType?: boolean;
|
132 |
|
133 | /**
|
134 | Value of `boolean` flags not defined in `argv`.
|
135 |
|
136 | If set to `undefined`, the flags not defined in `argv` will be excluded from the result. The `default` value set in `boolean` flags take precedence over `booleanDefault`.
|
137 |
|
138 | _Note: If used in conjunction with `isMultiple`, the default flag value is set to `[]`._
|
139 |
|
140 | __Caution: Explicitly specifying `undefined` for `booleanDefault` has different meaning from omitting key itself.__
|
141 |
|
142 | @example
|
143 | ```
|
144 | import meow from 'meow';
|
145 |
|
146 | const cli = meow(`
|
147 | Usage
|
148 | $ foo
|
149 |
|
150 | Options
|
151 | --rainbow, -r Include a rainbow
|
152 | --unicorn, -u Include a unicorn
|
153 | --no-sparkles Exclude sparkles
|
154 |
|
155 | Examples
|
156 | $ foo
|
157 | 🌈 unicorns✨🌈
|
158 | `, {
|
159 | importMeta: import.meta,
|
160 | booleanDefault: undefined,
|
161 | flags: {
|
162 | rainbow: {
|
163 | type: 'boolean',
|
164 | default: true,
|
165 | alias: 'r'
|
166 | },
|
167 | unicorn: {
|
168 | type: 'boolean',
|
169 | default: false,
|
170 | alias: 'u'
|
171 | },
|
172 | cake: {
|
173 | type: 'boolean',
|
174 | alias: 'c'
|
175 | },
|
176 | sparkles: {
|
177 | type: 'boolean',
|
178 | default: true
|
179 | }
|
180 | }
|
181 | });
|
182 |
|
183 | //{
|
184 | // flags: {
|
185 | // rainbow: true,
|
186 | // unicorn: false,
|
187 | // sparkles: true
|
188 | // },
|
189 | // unnormalizedFlags: {
|
190 | // rainbow: true,
|
191 | // r: true,
|
192 | // unicorn: false,
|
193 | // u: false,
|
194 | // sparkles: true
|
195 | // },
|
196 | // …
|
197 | //}
|
198 | ```
|
199 | */
|
200 | // eslint-disable-next-line @typescript-eslint/ban-types
|
201 | readonly booleanDefault?: boolean | null | undefined;
|
202 |
|
203 | /**
|
204 | Whether to use [hard-rejection](https://github.com/sindresorhus/hard-rejection) or not. Disabling this can be useful if you need to handle `process.on('unhandledRejection')` yourself.
|
205 |
|
206 | @default true
|
207 | */
|
208 | readonly hardRejection?: boolean;
|
209 |
|
210 | /**
|
211 | Whether to allow unknown flags or not.
|
212 |
|
213 | @default true
|
214 | */
|
215 | readonly allowUnknownFlags?: boolean;
|
216 | }
|
217 |
|
218 | type TypedFlag<Flag extends AnyFlag> =
|
219 | Flag extends {type: 'number'}
|
220 | ? number
|
221 | : Flag extends {type: 'string'}
|
222 | ? string
|
223 | : Flag extends {type: 'boolean'}
|
224 | ? boolean
|
225 | : unknown;
|
226 |
|
227 | type PossiblyOptionalFlag<Flag extends AnyFlag, FlagType> =
|
228 | Flag extends {isRequired: true}
|
229 | ? FlagType
|
230 | : Flag extends {default: any}
|
231 | ? FlagType
|
232 | : FlagType | undefined;
|
233 |
|
234 | export type TypedFlags<Flags extends AnyFlags> = {
|
235 | [F in keyof Flags]: Flags[F] extends {isMultiple: true}
|
236 | ? PossiblyOptionalFlag<Flags[F], Array<TypedFlag<Flags[F]>>>
|
237 | : PossiblyOptionalFlag<Flags[F], TypedFlag<Flags[F]>>
|
238 | };
|
239 |
|
240 | export interface Result<Flags extends AnyFlags> {
|
241 | /**
|
242 | Non-flag arguments.
|
243 | */
|
244 | input: string[];
|
245 |
|
246 | /**
|
247 | Flags converted to camelCase excluding aliases.
|
248 | */
|
249 | flags: TypedFlags<Flags> & Record<string, unknown>;
|
250 |
|
251 | /**
|
252 | Flags converted camelCase including aliases.
|
253 | */
|
254 | unnormalizedFlags: TypedFlags<Flags> & Record<string, unknown>;
|
255 |
|
256 | /**
|
257 | The `package.json` object.
|
258 | */
|
259 | pkg: PackageJson;
|
260 |
|
261 | /**
|
262 | The help text used with `--help`.
|
263 | */
|
264 | help: string;
|
265 |
|
266 | /**
|
267 | Show the help text and exit with code.
|
268 |
|
269 | @param exitCode - The exit code to use. Default: `2`.
|
270 | */
|
271 | showHelp: (exitCode?: number) => never;
|
272 |
|
273 | /**
|
274 | Show the version text and exit.
|
275 | */
|
276 | showVersion: () => void;
|
277 | }
|
278 | /**
|
279 | @param helpMessage - Shortcut for the `help` option.
|
280 |
|
281 | @example
|
282 | ```
|
283 | #!/usr/bin/env node
|
284 | import meow from 'meow';
|
285 | import foo from './index.js';
|
286 |
|
287 | const cli = meow(`
|
288 | Usage
|
289 | $ foo <input>
|
290 |
|
291 | Options
|
292 | --rainbow, -r Include a rainbow
|
293 |
|
294 | Examples
|
295 | $ foo unicorns --rainbow
|
296 | 🌈 unicorns 🌈
|
297 | `, {
|
298 | importMeta: import.meta,
|
299 | flags: {
|
300 | rainbow: {
|
301 | type: 'boolean',
|
302 | alias: 'r'
|
303 | }
|
304 | }
|
305 | });
|
306 |
|
307 | //{
|
308 | // input: ['unicorns'],
|
309 | // flags: {rainbow: true},
|
310 | // ...
|
311 | //}
|
312 |
|
313 | foo(cli.input[0], cli.flags);
|
314 | ```
|
315 | */
|
316 | export default function meow<Flags extends AnyFlags>(helpMessage: string, options?: Options<Flags>): Result<Flags>;
|
317 | export default function meow<Flags extends AnyFlags>(options?: Options<Flags>): Result<Flags>;
|