cleye 1.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/README.md ADDED
@@ -0,0 +1,536 @@
1
+
2
+ <p align="center">
3
+ <img width="110" src=".github/logo.png">
4
+ </p>
5
+ <h1 align="center">cleye</h1>
6
+
7
+ The intuitive command-line interface (CLI) development tool.
8
+
9
+ ### Features
10
+ - Minimal API surface
11
+ - Powerful flag parsing
12
+ - Strongly typed parameters and flags
13
+ - Command support
14
+ - Help documentation generation (customizable too!)
15
+
16
+ → Try it out online
17
+
18
+ <sub>Support this project by starring and sharing it. [Follow me](https://github.com/privatenumber) to see what other cool projects I'm working on.</sub>
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm i cleye
24
+ ```
25
+
26
+ ## About
27
+ _Cleye_ makes it very easy to develop command-line scripts in Node.js. It handles argv parsing to give you strongly typed parameters + flags and generates `--help` documentation based on the provided information.
28
+
29
+ Here's an example script that simply logs: `Good morning/evening <name>!`:
30
+
31
+ _greet.js:_
32
+ ```ts
33
+ import { cli } from 'cleye'
34
+
35
+ // Parse argv
36
+ const argv = cli({
37
+ name: 'greet.js',
38
+
39
+ // Define parameters
40
+ parameters: [
41
+ '<first name>', // First name is required
42
+ '[last name]' // Last name is optional
43
+ ],
44
+
45
+ // Define flags/options
46
+ flags: {
47
+
48
+ // Parses `--time` as a string
49
+ time: {
50
+ type: String,
51
+ description: 'Time of day to greet (morning or evening)',
52
+ default: 'morning'
53
+ }
54
+ }
55
+ })
56
+
57
+ const name = [argv._.firstName, argv._.lastName].filter(Boolean).join(' ')
58
+
59
+ if (argv.flags.time === 'morning') {
60
+ console.log(`Good morning ${name}!`)
61
+ } else {
62
+ console.log(`Good evening ${name}!`)
63
+ }
64
+ ```
65
+
66
+ 🛠 In development, type hints are provided on parsed flags and parameters:
67
+ <p align="center">
68
+ <img src=".github/typed-flags.png" width="600">
69
+ </p>
70
+
71
+ 📖 Generated help documentation can be viewed with the `--help` flag:
72
+
73
+ ```sh
74
+ $ node greet.js --help
75
+
76
+ greet.js
77
+
78
+ Usage:
79
+ greet.js [flags...] <first name> [last name]
80
+
81
+ Flags:
82
+ -h, --help Show help
83
+ --time <string> Time of day to greet (morning or evening) (default: "morning")
84
+ ```
85
+
86
+ ✅ Run the script to see it in action:
87
+
88
+ ```sh
89
+ $ node greet.js John Doe --time evening
90
+
91
+ Good evening John Doe!
92
+ ```
93
+
94
+ ## Examples
95
+ Want to dive right into some code? Check out some of these examples:
96
+
97
+ - [**greet.js**](/examples/greet/index.ts): Working example from above
98
+ - [**npm install**](/examples/npm/index.ts): Reimplementation of [`npm install`](https://docs.npmjs.com/cli/install/)'s CLI
99
+ - [**tsc**](/examples/tsc/index.ts): Reimplementation of TypeScript [`tsc`](https://www.typescriptlang.org/docs/handbook/compiler-options.html)'s CLI
100
+ - [**snap-tweet**](/examples/snap-tweet/index.ts): Reimplementation of [`snap-tweet`](https://github.com/privatenumber/snap-tweet)'s CLI
101
+ - [**pkg-size**](/examples/pkg-size/index.ts): Reimplementation of [`pkg-size`](https://github.com/pkg-size/pkg-size)'s CLI
102
+
103
+ ## Usage
104
+
105
+ ### Arguments
106
+ Arguments are values passed into the script that are not associated with any flags/options.
107
+
108
+ For example, in the following command, the first argument is `file-a.txt` and the second is `file-b.txt`:
109
+
110
+ ```
111
+ $ my-script file-a.txt file-b.txt
112
+ ```
113
+
114
+ Arguments can be accessed from the `_` array-property of the returned object.
115
+
116
+ Example:
117
+
118
+ ```ts
119
+ const argv = cli({ /* ... */ })
120
+
121
+ // $ my-script file-a.txt file-b.txt
122
+
123
+ argv._ // => ["file-a.txt", "file-b.txt"] (string[])
124
+ ```
125
+
126
+ #### Parameters
127
+ Parameters (aka _positional arguments_) are the names that map against argument values. Think of parameters as variable names and arguments as values associated with the variables.
128
+
129
+ Parameters can be defined in the `parameters` array-property to make specific arguments accessible by name. This is useful for writing more readable code, enforcing validation, and generating help documentation.
130
+
131
+ Parameters are defined in the following formats:
132
+ - **Required parameters** are indicated by angle brackets (eg. `<parameter name>`).
133
+ - **Optional parameters** are indicated by square brackets (eg. `[parameter name]`).
134
+ - **Spread parameters** are indicated by `...` suffix (eg. `<parameter name...>` or `[parameter name...]`).
135
+
136
+ Note, required parameters cannot come after optional parameters, and spread parameters must be last.
137
+
138
+ Parameters can be accessed in camelCase on the `_` property of the returned object.
139
+
140
+ Example:
141
+
142
+ ```ts
143
+ const argv = cli({
144
+ parameters: [
145
+ '<required parameter>',
146
+ '[optional parameter]',
147
+ '[optional spread...]'
148
+ ]
149
+ })
150
+
151
+ // $ my-script a b c d
152
+
153
+ argv._.requiredParameter // => "a" (string)
154
+ argv._.optionalParameter // => "b" (string | undefined)
155
+ argv._.optionalSpread // => ["c", "d"] (string[])
156
+ ```
157
+
158
+ ### Flags
159
+ Flags (aka Options) are key-value pairs passed into the script in the format `--flag-name <value>`.
160
+
161
+ For example, in the following command, `--file-a` has value `data.json` and `--file-b` has value `file.txt`:
162
+
163
+ ```
164
+ $ my-script --file-a data.json --file-b=file.txt
165
+ ```
166
+
167
+ #### Parsing features
168
+ _Cleye_'s flag parsing is powered by [`type-flag`](https://github.com/privatenumber/type-flag) and comes with many features:
169
+
170
+ - Array & Custom types
171
+ - Flag delimiters: `--flag value`, `--flag=value`, `--flag:value`, and `--flag.value`
172
+ - Combined aliases: `-abcd 2` → `-a -b -c -d 2`
173
+ - Early termination: Pass in `--` to end flag parsing
174
+ - Unknown flags: Unexpected flags stored in `unknownFlags`
175
+
176
+
177
+ Read the [_type-flag_ docs](https://github.com/privatenumber/type-flag) to learn more.
178
+
179
+ #### Defining flags
180
+ Flags can be specified in the `flag` object-property, where the key is the flag name, and the value is a flag type function or an object that describes the flag.
181
+
182
+ The flag name is recommended to be in camelCase as it will be interpreted to parse kebab-case equivalents.
183
+
184
+ The flag type function can be any function that accepts a string and returns the parsed value. Default JavaScript constructors should cover most use-cases: [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/String), [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/Number), [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/Boolean), etc.
185
+
186
+ The flag description object can be used to store additional information about the flag, such as `alias`, `default`, and `description`. To accept multiple values for a flag, wrap the type function in an array.
187
+
188
+ All of the provided information will be used to generate better help documentation.
189
+
190
+ Example:
191
+
192
+ ```ts
193
+ const argv = cli({
194
+ flags: {
195
+ someBoolean: Boolean,
196
+
197
+ someString: {
198
+ type: String,
199
+ description: 'Some string flag',
200
+ default: 'n/a'
201
+ },
202
+
203
+ someNumber: {
204
+ // Wrap the type function in an array to allow multiple values
205
+ type: [Number],
206
+ alias: 'n',
207
+ description: 'Array of numbers. (eg. -n 1 -n 2 -n 3)'
208
+ }
209
+ }
210
+ })
211
+
212
+ // $ my-script --some-boolean --some-string hello --some-number 1 -n 2
213
+
214
+ argv.flags.someBoolean // => true (boolean | undefined)
215
+ argv.flags.someString // => "hello" (string)
216
+ argv.flags.someNumber // => [1, 2] (number[])
217
+ ```
218
+
219
+ #### Custom flag types & validation
220
+ Custom flag types can be created to validate flags and narrow types. Simply create a new function that accepts a string and returns the parsed value.
221
+
222
+ Here's an example with a custom `Size` type that narrows the flag type to `"small" | "medium" | "large"`:
223
+
224
+ ```ts
225
+ const possibleSizes = ['small', 'medium', 'large'] as const
226
+
227
+ type Sizes = typeof possibleSizes[number] // => "small" | "medium" | "large"
228
+
229
+ // Custom type function
230
+ function Size(size: Sizes) {
231
+ if (!possibleSizes.includes(size)) {
232
+ throw new Error(`Invalid size: "${size}"`)
233
+ }
234
+
235
+ return size
236
+ }
237
+
238
+ const argv = cli({
239
+ flags: {
240
+ size: {
241
+ type: Size,
242
+ description: 'Size of the pizza (small, medium, large)'
243
+ }
244
+ }
245
+ })
246
+
247
+ // $ my-script --size large
248
+
249
+ argv.flags.size // => "large" ("small" | "medium" | "large")
250
+ ```
251
+
252
+ #### Default flags
253
+ By default, _Cleye_ will try to handle the `--help, -h` and `--version` flags.
254
+
255
+ ##### Help flag
256
+ Handling `--help, -h` is enabled by default.
257
+
258
+ To disable it, set `help` to `false`. The help documentation can still be manually displayed by calling `.showHelp(helpOptions)` on the returned object.
259
+
260
+ ##### Version flag
261
+ To enable handling `--version`, specify the `version` property.
262
+
263
+ ```ts
264
+ cli({
265
+ version: '1.2.3'
266
+ })
267
+ ```
268
+
269
+ ```sh
270
+ $ my-script --version
271
+ 1.2.3
272
+ ```
273
+
274
+ The version is also shown in the help documentation. To opt out of handling `--version` while still showing the version in `--help`, pass the version into `help.version`.
275
+
276
+ ### Commands
277
+ Commands allow organizing multiple "scripts" into a single script. An example of this is the [`npm install`](https://docs.npmjs.com/cli/install/) command, which is essentially an "install" script inside the "npm" script, adjacent to other commands like [`npm run`](https://docs.npmjs.com/cli/run-script/).
278
+
279
+ #### Defining commands
280
+ A command can be created by importing the `command` function and initializing it with a name. The rest of the options are the same as the `cli` function.
281
+
282
+ Pass the created command into `cli` option's `commands` array-property to register it:
283
+
284
+ _npm.js_
285
+ ```ts
286
+ import { cli, command } from 'cleye'
287
+
288
+ const argv = cli({
289
+ name: 'npm',
290
+
291
+ version: '1.2.3',
292
+
293
+ commands: [
294
+ command({
295
+ // Command name
296
+ name: 'install',
297
+
298
+ parameters: ['<package name>'],
299
+
300
+ flags: {
301
+ noSave: Boolean,
302
+ saveDev: Boolean
303
+ }
304
+ })
305
+ ]
306
+ })
307
+
308
+ // $ npm install lodash
309
+
310
+ argv.command // => "install" (string)
311
+ argv._.packageName // => "lodash" (string)
312
+ ```
313
+
314
+ Depending on the command given, the resulting type can be narrowed:
315
+ <p align="center">
316
+ <img src=".github/command-type-narrowing.png" width="420">
317
+ </p>
318
+
319
+ #### Command callback
320
+
321
+ When a CLI app has many commands, it's recommended to organize each command in its own file. With this structure, parsed output handling for each command is better placed where they are respectively defined rather than the single `cli` output point. This can be done by passing a callback function into the `command` function (callbacks are supported in the `cli` function too).
322
+
323
+ Example:
324
+
325
+ _install-command.js_ (`install` command using callback)
326
+ ```ts
327
+ import { command } from 'cleye'
328
+
329
+ export const installCommand = command({
330
+ // Command name
331
+ name: 'install',
332
+
333
+ parameters: ['<package name>'],
334
+
335
+ flags: {
336
+ noSave: Boolean,
337
+ saveDev: Boolean
338
+ }
339
+ }, (argv) => {
340
+ // $ npm install lodash
341
+
342
+ argv._.packageName // => "lodash" (string)
343
+ })
344
+ ```
345
+
346
+ _npm.js_ (CLI entry file)
347
+ ```ts
348
+ import { installCommand } from './install-command.js'
349
+
350
+ cli({
351
+ name: 'npm',
352
+
353
+ commands: [
354
+ installCommand
355
+ ]
356
+ })
357
+ ```
358
+
359
+ ### Help documentation
360
+ _Cleye_ uses all information provided to generate rich help documentation. The more information you give, the better the docs!
361
+
362
+ #### Help customization
363
+ The help document can be customized by passing a `render(nodes, renderers) => string` function to `help.render`.
364
+
365
+ The `nodes` parameter contains an array of nodes that will be used to render the document. The `renderers` parameter is an object of functions used to render the document. Each node has properties `type` and `data`, where `type` corresponds to a property in `renderers` and `data` is passed into the render function.
366
+
367
+ Default renderers can be found in [`/src/render-help/renderers.ts`](/src/render-help/renderers.ts).
368
+
369
+ Here's an example that adds an extra sentence at the end and also updates the flags table to use the `=` operator (`--flag <value>` → `--flag=<value>`):
370
+
371
+ ```ts
372
+ cli({
373
+ // ...,
374
+
375
+ help: {
376
+ render(nodes, renderers) {
377
+ /* Modify nodes... */
378
+
379
+ // Add some text at end of document
380
+ nodes.push('\nCheckout Cleye: https://github.com/privatenumber/cleye')
381
+
382
+ /* Extend renderers... */
383
+
384
+ // Make all flag examples use `=` as the separator
385
+ renderers.flagOperator = () => '='
386
+
387
+ /* Render nodes and return help */
388
+ return renderers.render(nodes)
389
+ }
390
+ }
391
+ })
392
+ ```
393
+
394
+ #### Responsive tables
395
+ _Cleye_'s "Flags" table in the help document is responsive and wraps cell text content based on the column & terminal width. It also has [breakpoints to display more vertically-optimized tables](/src/render-help/render-flags.ts#L4) for narrower viewports.
396
+
397
+ This feature is powered by [terminal-columns](https://github.com/privatenumber/terminal-columns) and can be configured via the `renderers.table` renderer.
398
+
399
+ <table>
400
+ <tr>
401
+ <th>Normal width</th>
402
+ <th>Narrow width</th>
403
+ </tr>
404
+ <tr>
405
+ <th><img src=".github/responsive-normal.png" width="420"></th>
406
+ <th><img src=".github/responsive-narrow.png" width="300"></th>
407
+ </tr>
408
+ </table>
409
+
410
+ ## API
411
+
412
+ ### cli(options, callback?, argvs?)
413
+
414
+ Return type:
415
+ ```ts
416
+ type ParsedArgv = {
417
+ // Parsed arguments
418
+ _: string[] & Parameters
419
+
420
+ // Parsed flags
421
+ flags: {
422
+ [flagName: string]: InferredType
423
+ }
424
+
425
+ // Unexpected flags
426
+ unknownFlags: {
427
+ [flagName: string]: (string | boolean)[]
428
+ }
429
+
430
+ // Method to print version
431
+ showVersion: () => void
432
+
433
+ // Method to print help
434
+ showHelp: (options: HelpOptions) => void
435
+ }
436
+ ```
437
+
438
+ Function to parse argvs by declaring parameters and flags.
439
+
440
+ #### options
441
+
442
+ Options object to configure `cli`.
443
+ ##### name
444
+
445
+ Type: `string`
446
+
447
+ Name of the script used in the help documentation.
448
+
449
+ ##### version
450
+
451
+ Type: `string`
452
+
453
+ Version of the script used in the help documentation.
454
+
455
+ Passing this in enables auto-handling `--version`. To provide a version for the documentation without auto-handling `--version`, pass it into [`help.version`](#version-1).
456
+
457
+ ##### parameters
458
+
459
+ Type: `string[]`
460
+
461
+ Parameter names to map onto arguments. Also used for validation and help documentation.
462
+
463
+ Parameters must be defined in the following formats:
464
+ | Format | Description |
465
+ | - | - |
466
+ | `<parameter name>` | Required parameter |
467
+ | `[parameter name]` | Optional parameter |
468
+ | `<parameter name...>` | Required spread parameter (1 or more) |
469
+ | `[parameter name...]` | Optional spread parameter (0 or more) |
470
+
471
+ Required parameters must be defined before optional parameters, and spread parameters must be defined at the end.
472
+
473
+ ##### flags
474
+
475
+ Type: An object that maps the flag name (in camelCase) to a flag type function or an object describing the flag:
476
+
477
+ | Property | Type | Description |
478
+ | - | - | - |
479
+ | `type` | `Function` | Flag value parsing function. |
480
+ | `alias` | `string` | Single character alias for the flag. |
481
+ | `default` | `any` | Default value for the flag. |
482
+ | `description` | `string` | Description of the flag shown in `--help`. |
483
+ | `placeholder` | `string` | Placeholder for the flag value shown in `--help`. |
484
+
485
+ ##### help
486
+
487
+ Type: `false` or an object with the following properties.
488
+
489
+ | Property | Type | Description |
490
+ | - | - | - |
491
+ | `version` | `string` | Version shown in `--help`. |
492
+ | `description` | `string` | Description shown in `--help`. |
493
+ | `usage` | `string \| string[]` | Usage code examples shown in `--help`. |
494
+ | `examples` | `string \| string[]` | Example code snippets shown in `--help`. |
495
+ | `render` | `(nodes, renderers) => string` | Function to customize the help document. |
496
+
497
+ Handling `--help, -h` is enabled by default. To disable it, pass in `false`.
498
+
499
+ ##### commands
500
+
501
+ Type: `Command[]`
502
+
503
+ Array of [commands](#commandoptions-callback) to register.
504
+
505
+
506
+ #### callback(parsed)
507
+
508
+ Type:
509
+
510
+ Optional callback function that is called when the script is invoked without a command.
511
+
512
+ #### argvs
513
+
514
+ Type: `string[]`
515
+
516
+ Default: `process.argv.slice(2)`
517
+
518
+ The raw parameters array to parse.
519
+
520
+ ### command(options, callback?)
521
+
522
+ #### options
523
+
524
+ | Property | Type | Description |
525
+ | - | - | - |
526
+ | `name` | `string` | Required name used to invoke the command. |
527
+ | `alias` | `string \| string[]` | Aliases used to invoke the command. |
528
+ | `parameters` | `string[]` | Parameters for the command. Same as [`parameters`](#parameters-1). |
529
+ | `flags` | `Flags` | Flags for the command. Same as [`flags`](#flags-1). |
530
+ | `help` | `false \| HelpOptions` | Help options for the command. Same as [`help`](#help-1). |
531
+
532
+ #### callback(parsed)
533
+
534
+ Type:
535
+
536
+ Optional callback function that is called when the command is invoked.
@@ -0,0 +1,203 @@
1
+ import { Flags as Flags$1, TypeFlag } from 'type-flag';
2
+ import { Options } from 'terminal-columns';
3
+
4
+ declare type CommandOptions<Parameters = string[]> = {
5
+ /**
6
+ Name of the command used to invoke it. Also displayed in `--help` output.
7
+ */
8
+ name: string;
9
+ /**
10
+ Aliases for the command used to invoke it. Also displayed in `--help` output.
11
+ */
12
+ alias?: string | string[];
13
+ /**
14
+ Parameters accepted by the command. Parameters must be in the following formats:
15
+
16
+ - Required parameter: `<parameter name>`
17
+ - Optional parameter: `[parameter name]`
18
+ - Required spread parameter: `<parameter name...>`
19
+ - Optional spread parameter: `[parameter name...]`
20
+ */
21
+ parameters?: Parameters;
22
+ /**
23
+ Flags accepted by the command
24
+ */
25
+ flags?: Flags;
26
+ /**
27
+ Options to configure the help documentation. Pass in `false` to disable handling `--help, -h`.
28
+ */
29
+ help?: false | HelpOptions;
30
+ };
31
+ declare function command<Options extends CommandOptions<[...Parameters]>, Parameters extends string[]>(options: Readonly<Options> & CommandOptions<[...Parameters]>, callback?: CallbackFunction<ParseArgv<Options, Parameters>>): Command<Options, ParseArgv<Options, Parameters, Options['name']>>;
32
+ declare type Command<Options extends CommandOptions = CommandOptions, ParsedType = any> = {
33
+ readonly options: Options;
34
+ readonly callback?: CallbackFunction<any>;
35
+ [parsedType]: ParsedType;
36
+ };
37
+
38
+ declare type FlagData = {
39
+ name: string;
40
+ flag: Flags[string];
41
+ flagFormatted: string;
42
+ aliasesEnabled: boolean;
43
+ aliasFormatted: string | undefined;
44
+ };
45
+
46
+ declare type TypeFunction = (value: any) => any;
47
+ declare type HelpDocumentNodeOrString<Type extends PropertyKey> = string | HelpDocumentNode<Type>;
48
+ declare class Renderers {
49
+ text(text: string): string;
50
+ bold(text: string): string;
51
+ indentText({ text, spaces }: {
52
+ text: string;
53
+ spaces: number;
54
+ }): string;
55
+ heading(text: string): string;
56
+ section({ title, body, indentBody, }: {
57
+ title?: string;
58
+ body?: string;
59
+ indentBody?: number;
60
+ }): string;
61
+ table({ tableData, tableOptions, tableBreakpoints, }: {
62
+ tableData: string[][];
63
+ tableOptions?: Options;
64
+ tableBreakpoints?: Record<string, Options>;
65
+ }): string;
66
+ flagParameter(typeFunction: TypeFunction | readonly [TypeFunction]): string;
67
+ flagOperator(): string;
68
+ flagName({ flag, flagFormatted, aliasesEnabled, aliasFormatted, }: FlagData): string;
69
+ flagDefault(value: any): string;
70
+ flagDescription({ flag }: FlagData): string;
71
+ render(nodes: (HelpDocumentNodeOrString<keyof this> | HelpDocumentNodeOrString<keyof this>[])): string;
72
+ }
73
+
74
+ declare const parsedType: unique symbol;
75
+ declare type Flags = Flags$1<{
76
+ /**
77
+ Description to be used in help output
78
+
79
+ @example
80
+ ```
81
+ description: 'Unit of output (metric, imperial)',
82
+ ```
83
+ */
84
+ description?: string;
85
+ /**
86
+ Placeholder label to be used in help output
87
+
88
+ @example Required value
89
+ ```
90
+ placeholder: '<locale>'
91
+ ```
92
+ */
93
+ placeholder?: string;
94
+ }>;
95
+ declare type CallbackFunction<Parsed> = (parsed: {
96
+ [Key in keyof Parsed]: Parsed[Key];
97
+ }) => void;
98
+ declare type HasVersion<Options extends {
99
+ flags?: Flags;
100
+ }> = (Options extends {
101
+ version: string;
102
+ } ? Options['flags'] & {
103
+ version: BooleanConstructor;
104
+ } : Options['flags']);
105
+ declare type HasHelp<Options extends {
106
+ flags?: Flags;
107
+ }> = (Options extends {
108
+ help: false;
109
+ } ? Options['flags'] : Options['flags'] & {
110
+ help: BooleanConstructor;
111
+ });
112
+ declare type HasHelpOrVersion<Options> = HasVersion<Options> & HasHelp<Options>;
113
+ declare type HelpDocumentNode<Types extends PropertyKey = keyof Renderers> = {
114
+ id?: string;
115
+ type: Types;
116
+ data: any;
117
+ };
118
+ declare type HelpOptions = {
119
+ /**
120
+ Version of the script displayed in `--help` output. Use to avoid enabling `--version` flag.
121
+ */
122
+ version?: string;
123
+ /**
124
+ Description of the script or command to display in `--help` output.
125
+ */
126
+ description?: string;
127
+ /**
128
+ Usage code examples to display in `--help` output.
129
+ */
130
+ usage?: false | string | string[];
131
+ /**
132
+ Example code snippets to display in `--help` output.
133
+ */
134
+ examples?: string | string[];
135
+ /**
136
+ Function to customize the help document before it is logged.
137
+ */
138
+ render?: (nodes: HelpDocumentNode<keyof Renderers>[], renderers: Renderers) => string;
139
+ };
140
+ declare type CliOptions<Commands = Command[], Parameters extends string[] = string[]> = {
141
+ /**
142
+ Name of the script displayed in `--help` output.
143
+ */
144
+ name?: string;
145
+ /**
146
+ Version of the script displayed in `--version` and `--help` outputs.
147
+ */
148
+ version?: string;
149
+ /**
150
+ Parameters accepted by the script. Parameters must be in the following formats:
151
+
152
+ - Required parameter: `<parameter name>`
153
+ - Optional parameter: `[parameter name]`
154
+ - Required spread parameter: `<parameter name...>`
155
+ - Optional spread parameter: `[parameter name...]`
156
+ */
157
+ parameters?: Parameters;
158
+ /**
159
+ Commands to register to the script.
160
+ */
161
+ commands?: Commands;
162
+ /**
163
+ Flags accepted by the script
164
+ */
165
+ flags?: Flags;
166
+ /**
167
+ Options to configure the help documentation. Pass in `false` to disable handling `--help, -h`.
168
+ */
169
+ help?: false | HelpOptions;
170
+ };
171
+ declare type kebabToCamel<Word extends string> = (Word extends `${infer Prefix}-${infer Suffix}` | `${infer Prefix} ${infer Suffix}` ? `${Prefix}${Capitalize<kebabToCamel<Suffix>>}` : Word);
172
+ declare type StripBrackets<Parameter extends string> = (Parameter extends `<${infer ParameterName}>` | `[${infer ParameterName}]` ? (ParameterName extends `${infer SpreadName}...` ? SpreadName : ParameterName) : never);
173
+ declare type ParameterType<Parameter extends string> = (Parameter extends `<${infer _ParameterName}...>` | `[${infer _ParameterName}...]` ? string[] : Parameter extends `<${infer _ParameterName}>` ? string : Parameter extends `[${infer _ParameterName}]` ? string | undefined : never);
174
+ declare type WithCommand<Options extends {
175
+ flags?: Flags;
176
+ }, Command extends string | undefined = undefined> = {
177
+ command: Command;
178
+ } & Options;
179
+ declare type TypeFlagWrapper<Options extends {
180
+ flags?: Flags;
181
+ }, Parameters extends string[]> = TypeFlag<HasHelpOrVersion<Options>> & {
182
+ _: {
183
+ [Parameter in Parameters[number] as kebabToCamel<StripBrackets<Parameter>>]: ParameterType<Parameter>;
184
+ };
185
+ showHelp: (options?: HelpOptions) => void;
186
+ showVersion: () => void;
187
+ };
188
+ declare type ParseArgv<Options extends {
189
+ flags?: Flags;
190
+ }, Parameters extends string[], Command extends string | undefined = ''> = (Command extends '' ? TypeFlagWrapper<Options, Parameters> : WithCommand<TypeFlagWrapper<Options, Parameters>, Command>);
191
+
192
+ declare function cli<Options extends CliOptions<undefined, [...Parameters]>, Parameters extends string[]>(options: Options & CliOptions<undefined, [...Parameters]>, callback?: CallbackFunction<ParseArgv<Options, Parameters>>, argv?: string[]): {
193
+ [Key in keyof ParseArgv<Options, Parameters, undefined>]: ParseArgv<Options, Parameters, undefined>[Key];
194
+ };
195
+ declare function cli<Options extends CliOptions<[...Commands], [...Parameters]>, Commands extends Command[], Parameters extends string[]>(options: Options & CliOptions<[...Commands], [...Parameters]>, callback?: CallbackFunction<ParseArgv<Options, Parameters>>, argv?: string[]): ({
196
+ [Key in keyof ParseArgv<Options, Parameters, undefined>]: ParseArgv<Options, Parameters, undefined>[Key];
197
+ } | {
198
+ [KeyA in keyof Commands]: (Commands[KeyA] extends Command ? ({
199
+ [KeyB in keyof Commands[KeyA][typeof parsedType]]: Commands[KeyA][typeof parsedType][KeyB];
200
+ }) : never);
201
+ }[number]);
202
+
203
+ export { cli, command };
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ var M=Object.create;var y=Object.defineProperty,W=Object.defineProperties,Z=Object.getOwnPropertyDescriptor,z=Object.getOwnPropertyDescriptors,G=Object.getOwnPropertyNames,F=Object.getOwnPropertySymbols,Q=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty,X=Object.prototype.propertyIsEnumerable;var D=(t,e,n)=>e in t?y(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,c=(t,e)=>{for(var n in e||(e={}))k.call(e,n)&&D(t,n,e[n]);if(F)for(var n of F(e))X.call(e,n)&&D(t,n,e[n]);return t},O=(t,e)=>W(t,z(e)),T=t=>y(t,"__esModule",{value:!0});var Y=(t,e)=>{for(var n in e)y(t,n,{get:e[n],enumerable:!0})},E=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of G(e))!k.call(t,r)&&(n||r!=="default")&&y(t,r,{get:()=>e[r],enumerable:!(a=Z(e,r))||a.enumerable});return t},A=(t,e)=>E(T(y(t!=null?M(Q(t)):{},"default",!e&&t&&t.__esModule?{get:()=>t.default,enumerable:!0}:{value:t,enumerable:!0})),t),ee=(t=>(e,n)=>t&&t.get(e)||(n=E(T({}),e,1),t&&t.set(e,n),n))(typeof WeakMap!="undefined"?new WeakMap:0);var ue={};Y(ue,{cli:()=>V,command:()=>_});var q=A(require("type-flag"));var S=t=>t.replace(/[-_ ](\w)/g,(e,n)=>n.toUpperCase()),B=t=>t.replace(/\B([A-Z])/g,"-$1").toLowerCase();var te={"> 80":[{width:"content-width",paddingLeft:2,paddingRight:8},{width:"auto"}],"> 40":[{width:"auto",paddingLeft:2,paddingRight:8,preprocess:t=>t.trim()},{width:"100%",paddingLeft:2,paddingBottom:1}],"> 0":{stdoutColumns:1e3,columns:[{width:"content-width",paddingLeft:2,paddingRight:8},{width:"content-width"}]}};function I(t){let e=!1,a=Object.keys(t).sort((r,s)=>r.localeCompare(s)).map(r=>{let s=t[r],i="alias"in s;return i&&(e=!0),{name:r,flag:s,flagFormatted:`--${B(r)}`,aliasesEnabled:e,aliasFormatted:i?`-${s.alias}`:void 0}}).map(r=>(r.aliasesEnabled=e,[{type:"flagName",data:r},{type:"flagDescription",data:r}]));return{type:"table",data:{tableData:a,tableBreakpoints:te}}}var R=t=>{var e;return!t||((e=t.version)!=null?e:t.help?t.help.version:void 0)},j=t=>{var n;let e="parent"in t&&((n=t.parent)==null?void 0:n.name);return(e?`${e} `:"")+t.name};function ne(t){var a;let e=[];t.name&&e.push(j(t));let n=(a=R(t))!=null?a:"parent"in t&&R(t.parent);if(n&&e.push(`v${n}`),e.length!==0)return{id:"name",type:"text",data:`${e.join(" ")}
2
+ `}}function re(t){let{help:e}=t;if(!(!e||!e.description))return{id:"description",type:"text",data:`${e.description}
3
+ `}}function ae(t){var n;let e=t.help||{};if("usage"in e)return e.usage?{id:"usage",type:"section",data:{title:"Usage:",body:Array.isArray(e.usage)?e.usage.join(`
4
+ `):e.usage}}:void 0;if(t.name){let a=[],r=[j(t)];if(t.flags&&Object.keys(t.flags).length>0&&r.push("[flags...]"),t.parameters&&t.parameters.length>0&&r.push(t.parameters.join(" ")),r.length>1&&a.push(r.join(" ")),"commands"in t&&((n=t.commands)==null?void 0:n.length)&&a.push(`${t.name} <command>`),a.length>0)return{id:"usage",type:"section",data:{title:"Usage:",body:a.join(`
5
+ `)}}}}function se(t){var a;if(!("commands"in t)||!((a=t.commands)==null?void 0:a.length))return;let e=t.commands.map(r=>[r.options.name,r.options.help?r.options.help.description:""]);return{id:"commands",type:"section",data:{title:"Commands:",body:{type:"table",data:{tableData:e,tableOptions:[{width:"content-width",paddingLeft:2,paddingRight:8}]}},indentBody:0}}}function ie(t){if(!(!t.flags||Object.keys(t.flags).length===0))return{id:"flags",type:"section",data:{title:"Flags:",body:I(t.flags),indentBody:0}}}function oe(t){let{help:e}=t;if(!e||!e.examples||e.examples.length===0)return;let{examples:n}=e;if(Array.isArray(n)&&(n=n.join(`
6
+ `)),n)return{id:"examples",type:"section",data:{title:"Examples:",body:n}}}function me(t){if(!("alias"in t)||!t.alias)return;let{alias:e}=t,n=Array.isArray(e)?e.join(", "):e;return{id:"aliases",type:"section",data:{title:"Aliases:",body:n}}}var $=t=>[ne,re,ae,se,ie,oe,me].map(e=>e(t)).filter(e=>Boolean(e));var K=A(require("tty")),C=A(require("terminal-columns")),pe=K.default.WriteStream.prototype.hasColors(),b=class{text(e){return e}bold(e){return`${e}`}indentText({text:e,spaces:n}){return e.replace(/^/gm," ".repeat(n))}heading(e){return pe?this.bold(e):e.toLocaleUpperCase()}section({title:e,body:n,indentBody:a=2}){return`${(e?`${this.heading(e)}
7
+ `:"")+(n?this.indentText({text:this.render(n),spaces:a}):"")}
8
+ `}table({tableData:e,tableOptions:n,tableBreakpoints:a}){return(0,C.default)(e.map(r=>r.map(s=>this.render(s))),a?(0,C.breakpoints)(a):n)}flagParameter(e){return e===Boolean?"":e===String?"<string>":e===Number?"<number>":Array.isArray(e)?this.flagParameter(e[0]):"<value>"}flagOperator(){return" "}flagName({flag:e,flagFormatted:n,aliasesEnabled:a,aliasFormatted:r}){let s="";if(r?s+=`${r}, `:a&&(s+=" "),s+=n,"placeholder"in e&&typeof e.placeholder=="string")s+=`${this.flagOperator()}${e.placeholder}`;else{let i=this.flagParameter("type"in e?e.type:e);i&&(s+=`${this.flagOperator()}${i}`)}return s}flagDefault(e){return JSON.stringify(e)}flagDescription({flag:e}){var a;let n="description"in e&&(a=e.description)!=null?a:"";if("default"in e){let{default:r}=e;typeof r=="function"&&(r=r()),r&&(n+=` (default: ${this.flagDefault(r)})`)}return n}render(e){if(typeof e=="string")return e;if(Array.isArray(e))return e.map(n=>this.render(n)).join(`
9
+ `);if("type"in e&&this[e.type]){let n=this[e.type];if(typeof n=="function")return n.call(this,e.data)}throw new Error(`Invalid node type: ${JSON.stringify(e)}`)}};var h=/^[\w-]+$/;var{stringify:m}=JSON,le=/[|\\{}()[\]^$+*?.]/;function de(t){let e=[],n,a;for(let r of t){if(a)throw new Error(`Invalid parameter: Spread parameter ${m(a)} must be last`);let s=r[0],i=r[r.length-1],p;if(s==="<"&&i===">"&&(p=!0,n))throw new Error(`Invalid parameter: Required parameter ${m(r)} cannot come after optional parameter ${m(n)}`);if(s==="["&&i==="]"&&(p=!1,n=r),p===void 0)throw new Error(`Invalid parameter: ${m(r)}. Must be wrapped in <> (required parameter) or [] (optional parameter)`);let o=r.slice(1,-1),u=o.slice(-3)==="...";u&&(a=r,o=o.slice(0,-3));let g=o.match(le);if(g)throw new Error(`Invalid parameter: ${m(r)}. Invalid character found ${m(g[0])}`);e.push({name:o,required:p,spread:u})}return e}function ce(t){return t===void 0||t!==!1}function L(t,e,n,a){let r=c({},e.flags),s=e.version;s&&(r.version={type:Boolean,description:"Show version"});let{help:i}=e,p=ce(i);p&&!("help"in r)&&(r.help={type:Boolean,alias:"h",description:"Show help"});let o=(0,q.default)(r,a),u=()=>{console.log(e.version)};if(s&&o.flags.version===!0)return u(),process.exit(0);let g=new b,U=p&&(i==null?void 0:i.render)?i.render:l=>g.render(l),P=l=>{let x=$(O(c(c({},e),l?{help:l}:{}),{flags:r}));console.log(U(x,g))};if(p&&o.flags.help===!0)return P(),process.exit(0);if(e.parameters){let l=de(e.parameters),x=o._;for(let f=0;f<l.length;f+=1){let{name:N,required:J,spread:w}=l[f],v=w?o._.slice(f):o._[f];if(w&&(f=l.length),J&&(!v||w&&v.length===0))return console.error(`Error: Missing required parameter ${m(N)}
10
+ `),P(),process.exit(1);x[S(N)]=v}}let H=O(c({},o),{showVersion:u,showHelp:P});return typeof n=="function"&&n(H),c({command:t},H)}function fe(t,e){let n=new Map;for(let a of e){let r=[a.options.name],{alias:s}=a.options;s&&(Array.isArray(s)?r.push(...s):r.push(s));for(let i of r){if(n.has(i))throw new Error(`Duplicate command name found: ${m(i)}`);n.set(i,a)}}return n.get(t)}function V(t,e,n=process.argv.slice(2)){if(!t)throw new Error("Options is required");if("name"in t&&(!t.name||!h.test(t.name)))throw new Error(`Invalid script name: ${m(t.name)}`);let a=n[0];if(t.commands&&h.test(a)){let r=fe(a,t.commands);if(r)return L(r.options.name,O(c({},r.options),{parent:t}),r.callback,n.slice(1))}return L(void 0,t,e,n)}function _(t,e){if(!t)throw new Error("Command options are required");let{name:n}=t;if(t.name===void 0)throw new Error("Command name is required");if(!h.test(n))throw new Error(`Invalid command name ${JSON.stringify(n)}. Command names must be one word.`);return{options:t,callback:e}}module.exports=ee(ue);0&&(module.exports={cli,command});
package/dist/index.mjs ADDED
@@ -0,0 +1,10 @@
1
+ var R=Object.defineProperty,j=Object.defineProperties;var K=Object.getOwnPropertyDescriptors;var H=Object.getOwnPropertySymbols;var q=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable;var N=(t,e,n)=>e in t?R(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,f=(t,e)=>{for(var n in e||(e={}))q.call(e,n)&&N(t,n,e[n]);if(H)for(var n of H(e))L.call(e,n)&&N(t,n,e[n]);return t},O=(t,e)=>j(t,K(e));import ee from"type-flag";var F=t=>t.replace(/[-_ ](\w)/g,(e,n)=>n.toUpperCase()),k=t=>t.replace(/\B([A-Z])/g,"-$1").toLowerCase();var V={"> 80":[{width:"content-width",paddingLeft:2,paddingRight:8},{width:"auto"}],"> 40":[{width:"auto",paddingLeft:2,paddingRight:8,preprocess:t=>t.trim()},{width:"100%",paddingLeft:2,paddingBottom:1}],"> 0":{stdoutColumns:1e3,columns:[{width:"content-width",paddingLeft:2,paddingRight:8},{width:"content-width"}]}};function D(t){let e=!1,a=Object.keys(t).sort((r,s)=>r.localeCompare(s)).map(r=>{let s=t[r],i="alias"in s;return i&&(e=!0),{name:r,flag:s,flagFormatted:`--${k(r)}`,aliasesEnabled:e,aliasFormatted:i?`-${s.alias}`:void 0}}).map(r=>(r.aliasesEnabled=e,[{type:"flagName",data:r},{type:"flagDescription",data:r}]));return{type:"table",data:{tableData:a,tableBreakpoints:V}}}var T=t=>{var e;return!t||((e=t.version)!=null?e:t.help?t.help.version:void 0)},E=t=>{var n;let e="parent"in t&&((n=t.parent)==null?void 0:n.name);return(e?`${e} `:"")+t.name};function _(t){var a;let e=[];t.name&&e.push(E(t));let n=(a=T(t))!=null?a:"parent"in t&&T(t.parent);if(n&&e.push(`v${n}`),e.length!==0)return{id:"name",type:"text",data:`${e.join(" ")}
2
+ `}}function U(t){let{help:e}=t;if(!(!e||!e.description))return{id:"description",type:"text",data:`${e.description}
3
+ `}}function J(t){var n;let e=t.help||{};if("usage"in e)return e.usage?{id:"usage",type:"section",data:{title:"Usage:",body:Array.isArray(e.usage)?e.usage.join(`
4
+ `):e.usage}}:void 0;if(t.name){let a=[],r=[E(t)];if(t.flags&&Object.keys(t.flags).length>0&&r.push("[flags...]"),t.parameters&&t.parameters.length>0&&r.push(t.parameters.join(" ")),r.length>1&&a.push(r.join(" ")),"commands"in t&&((n=t.commands)==null?void 0:n.length)&&a.push(`${t.name} <command>`),a.length>0)return{id:"usage",type:"section",data:{title:"Usage:",body:a.join(`
5
+ `)}}}}function M(t){var a;if(!("commands"in t)||!((a=t.commands)==null?void 0:a.length))return;let e=t.commands.map(r=>[r.options.name,r.options.help?r.options.help.description:""]);return{id:"commands",type:"section",data:{title:"Commands:",body:{type:"table",data:{tableData:e,tableOptions:[{width:"content-width",paddingLeft:2,paddingRight:8}]}},indentBody:0}}}function W(t){if(!(!t.flags||Object.keys(t.flags).length===0))return{id:"flags",type:"section",data:{title:"Flags:",body:D(t.flags),indentBody:0}}}function Z(t){let{help:e}=t;if(!e||!e.examples||e.examples.length===0)return;let{examples:n}=e;if(Array.isArray(n)&&(n=n.join(`
6
+ `)),n)return{id:"examples",type:"section",data:{title:"Examples:",body:n}}}function z(t){if(!("alias"in t)||!t.alias)return;let{alias:e}=t,n=Array.isArray(e)?e.join(", "):e;return{id:"aliases",type:"section",data:{title:"Aliases:",body:n}}}var v=t=>[_,U,J,M,W,Z,z].map(e=>e(t)).filter(e=>Boolean(e));import G from"tty";import Q,{breakpoints as X}from"terminal-columns";var Y=G.WriteStream.prototype.hasColors(),C=class{text(e){return e}bold(e){return`${e}`}indentText({text:e,spaces:n}){return e.replace(/^/gm," ".repeat(n))}heading(e){return Y?this.bold(e):e.toLocaleUpperCase()}section({title:e,body:n,indentBody:a=2}){return`${(e?`${this.heading(e)}
7
+ `:"")+(n?this.indentText({text:this.render(n),spaces:a}):"")}
8
+ `}table({tableData:e,tableOptions:n,tableBreakpoints:a}){return Q(e.map(r=>r.map(s=>this.render(s))),a?X(a):n)}flagParameter(e){return e===Boolean?"":e===String?"<string>":e===Number?"<number>":Array.isArray(e)?this.flagParameter(e[0]):"<value>"}flagOperator(){return" "}flagName({flag:e,flagFormatted:n,aliasesEnabled:a,aliasFormatted:r}){let s="";if(r?s+=`${r}, `:a&&(s+=" "),s+=n,"placeholder"in e&&typeof e.placeholder=="string")s+=`${this.flagOperator()}${e.placeholder}`;else{let i=this.flagParameter("type"in e?e.type:e);i&&(s+=`${this.flagOperator()}${i}`)}return s}flagDefault(e){return JSON.stringify(e)}flagDescription({flag:e}){var a;let n="description"in e&&(a=e.description)!=null?a:"";if("default"in e){let{default:r}=e;typeof r=="function"&&(r=r()),r&&(n+=` (default: ${this.flagDefault(r)})`)}return n}render(e){if(typeof e=="string")return e;if(Array.isArray(e))return e.map(n=>this.render(n)).join(`
9
+ `);if("type"in e&&this[e.type]){let n=this[e.type];if(typeof n=="function")return n.call(this,e.data)}throw new Error(`Invalid node type: ${JSON.stringify(e)}`)}};var h=/^[\w-]+$/;var{stringify:m}=JSON,te=/[|\\{}()[\]^$+*?.]/;function ne(t){let e=[],n,a;for(let r of t){if(a)throw new Error(`Invalid parameter: Spread parameter ${m(a)} must be last`);let s=r[0],i=r[r.length-1],p;if(s==="<"&&i===">"&&(p=!0,n))throw new Error(`Invalid parameter: Required parameter ${m(r)} cannot come after optional parameter ${m(n)}`);if(s==="["&&i==="]"&&(p=!1,n=r),p===void 0)throw new Error(`Invalid parameter: ${m(r)}. Must be wrapped in <> (required parameter) or [] (optional parameter)`);let o=r.slice(1,-1),g=o.slice(-3)==="...";g&&(a=r,o=o.slice(0,-3));let y=o.match(te);if(y)throw new Error(`Invalid parameter: ${m(r)}. Invalid character found ${m(y[0])}`);e.push({name:o,required:p,spread:g})}return e}function re(t){return t===void 0||t!==!1}function S(t,e,n,a){let r=f({},e.flags),s=e.version;s&&(r.version={type:Boolean,description:"Show version"});let{help:i}=e,p=re(i);p&&!("help"in r)&&(r.help={type:Boolean,alias:"h",description:"Show help"});let o=ee(r,a),g=()=>{console.log(e.version)};if(s&&o.flags.version===!0)return g(),process.exit(0);let y=new C,B=p&&(i==null?void 0:i.render)?i.render:l=>y.render(l),b=l=>{let P=v(O(f(f({},e),l?{help:l}:{}),{flags:r}));console.log(B(P,y))};if(p&&o.flags.help===!0)return b(),process.exit(0);if(e.parameters){let l=ne(e.parameters),P=o._;for(let u=0;u<l.length;u+=1){let{name:$,required:I,spread:x}=l[u],w=x?o._.slice(u):o._[u];if(x&&(u=l.length),I&&(!w||x&&w.length===0))return console.error(`Error: Missing required parameter ${m($)}
10
+ `),b(),process.exit(1);P[F($)]=w}}let A=O(f({},o),{showVersion:g,showHelp:b});return typeof n=="function"&&n(A),f({command:t},A)}function ae(t,e){let n=new Map;for(let a of e){let r=[a.options.name],{alias:s}=a.options;s&&(Array.isArray(s)?r.push(...s):r.push(s));for(let i of r){if(n.has(i))throw new Error(`Duplicate command name found: ${m(i)}`);n.set(i,a)}}return n.get(t)}function se(t,e,n=process.argv.slice(2)){if(!t)throw new Error("Options is required");if("name"in t&&(!t.name||!h.test(t.name)))throw new Error(`Invalid script name: ${m(t.name)}`);let a=n[0];if(t.commands&&h.test(a)){let r=ae(a,t.commands);if(r)return S(r.options.name,O(f({},r.options),{parent:t}),r.callback,n.slice(1))}return S(void 0,t,e,n)}function ie(t,e){if(!t)throw new Error("Command options are required");let{name:n}=t;if(t.name===void 0)throw new Error("Command name is required");if(!h.test(n))throw new Error(`Invalid command name ${JSON.stringify(n)}. Command names must be one word.`);return{options:t,callback:e}}export{se as cli,ie as command};
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "cleye",
3
+ "version": "1.0.0",
4
+ "description": "The intuitive CLI development tool",
5
+ "keywords": [
6
+ "cli",
7
+ "command line",
8
+ "argv",
9
+ "parameters",
10
+ "flags",
11
+ "node",
12
+ "typescript"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": "privatenumber/cleye",
16
+ "funding": "https://github.com/privatenumber/cleye?sponsor=1",
17
+ "author": {
18
+ "name": "Hiroki Osame",
19
+ "email": "hiroki.osame@gmail.com"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "main": "./dist/index.js",
25
+ "module": "./dist/index.mjs",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "require": "./dist/index.js",
30
+ "import": "./dist/index.mjs",
31
+ "types": "./dist/index.d.ts"
32
+ }
33
+ },
34
+ "scripts": {
35
+ "build": "tsup src/index.ts --dts --format esm,cjs --minify",
36
+ "lint": "eslint .",
37
+ "test": "CI=true jest"
38
+ },
39
+ "husky": {
40
+ "hooks": {
41
+ "pre-commit": "lint-staged && npm test"
42
+ }
43
+ },
44
+ "lint-staged": {
45
+ "*.ts": [
46
+ "eslint"
47
+ ]
48
+ },
49
+ "dependencies": {
50
+ "terminal-columns": "^1.4.0",
51
+ "type-flag": "^2.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "@pvtnbr/eslint-config": "^0.11.0",
55
+ "@types/jest": "^27.4.0",
56
+ "@types/node": "^17.0.8",
57
+ "colorette": "^2.0.16",
58
+ "es-jest": "^1.5.0",
59
+ "eslint": "^8.6.0",
60
+ "esno": "^0.13.0",
61
+ "husky": "^4.3.8",
62
+ "jest": "^27.4.7",
63
+ "lint-staged": "^12.1.2",
64
+ "tsup": "^5.11.10",
65
+ "typescript": "^4.5.4"
66
+ },
67
+ "eslintConfig": {
68
+ "extends": "@pvtnbr",
69
+ "rules": {
70
+ "unicorn/no-process-exit": "off"
71
+ }
72
+ }
73
+ }