citty 0.1.1 → 0.1.3

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/LICENSE CHANGED
@@ -34,14 +34,3 @@ The above copyright notice and this permission notice shall be included in all c
34
34
 
35
35
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36
36
 
37
- ---
38
-
39
- Colorette dependency is bundled (https://github.com/jorgebucaran/colorette)
40
-
41
- Copyright © Jorge Bucaran <https://jorgebucaran.com>
42
-
43
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
44
-
45
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
46
-
47
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/dist/index.cjs CHANGED
@@ -1,26 +1,17 @@
1
1
  'use strict';
2
2
 
3
- const tty = require('tty');
3
+ const consola = require('consola');
4
+ const utils = require('consola/utils');
4
5
 
5
- function _interopNamespaceCompat(e) {
6
- if (e && typeof e === 'object' && 'default' in e) return e;
7
- const n = Object.create(null);
8
- if (e) {
9
- for (const k in e) {
10
- n[k] = e[k];
11
- }
12
- }
13
- n.default = e;
14
- return n;
15
- }
6
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
16
7
 
17
- const tty__namespace = /*#__PURE__*/_interopNamespaceCompat(tty);
8
+ const consola__default = /*#__PURE__*/_interopDefaultCompat(consola);
18
9
 
19
10
  function toArray(val) {
20
11
  if (Array.isArray(val)) {
21
12
  return val;
22
13
  }
23
- return val !== void 0 ? [val] : [];
14
+ return val === void 0 ? [] : [val];
24
15
  }
25
16
  function formatLineColumns(lines, linePrefix = "") {
26
17
  const maxLengh = [];
@@ -261,13 +252,13 @@ function parseArgs(rawArgs, argsDef) {
261
252
  const nextPositionalArgument = positionalArguments.shift();
262
253
  if (nextPositionalArgument !== void 0) {
263
254
  parsedArgsProxy[arg.name] = nextPositionalArgument;
264
- } else if (arg.default !== void 0) {
265
- parsedArgsProxy[arg.name] = arg.default;
266
- } else {
255
+ } else if (arg.default === void 0 && arg.required !== false) {
267
256
  throw new CLIError(
268
257
  `Missing required positional argument: ${arg.name.toUpperCase()}`,
269
258
  "EARG"
270
259
  );
260
+ } else {
261
+ parsedArgsProxy[arg.name] = arg.default;
271
262
  }
272
263
  } else if (arg.required && parsedArgsProxy[arg.name] === void 0) {
273
264
  throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
@@ -296,6 +287,7 @@ async function runCommand(cmd, opts) {
296
287
  const context = {
297
288
  rawArgs: opts.rawArgs,
298
289
  args: parsedArgs,
290
+ data: opts.data,
299
291
  cmd
300
292
  };
301
293
  if (typeof cmd.setup === "function") {
@@ -308,15 +300,12 @@ async function runCommand(cmd, opts) {
308
300
  );
309
301
  const subCommandName = opts.rawArgs[subCommandArgIndex];
310
302
  if (!subCommandName && !cmd.run) {
311
- throw new CLIError(
312
- `Missing sub command. Use --help to see available sub commands.`,
313
- "ESUBCOMMAND"
314
- );
303
+ throw new CLIError(`No command specified.`, "E_NO_COMMAND");
315
304
  }
316
305
  if (!subCommands[subCommandName]) {
317
306
  throw new CLIError(
318
- `Unknown sub command: ${subCommandName}`,
319
- "ESUBCOMMAND"
307
+ `Unknown command \`${subCommandName}\``,
308
+ "E_UNKNOWN_COMMAND"
320
309
  );
321
310
  }
322
311
  const subCommand = await resolveValue(subCommands[subCommandName]);
@@ -349,9 +338,9 @@ async function resolveSubCommand(cmd, rawArgs, parent) {
349
338
 
350
339
  async function showUsage(cmd, parent) {
351
340
  try {
352
- console.log(await renderUsage(cmd, parent) + "\n");
341
+ consola__default.log(await renderUsage(cmd, parent) + "\n");
353
342
  } catch (error) {
354
- console.error(error);
343
+ consola__default.error(error);
355
344
  }
356
345
  }
357
346
  async function renderUsage(cmd, parent) {
@@ -368,7 +357,7 @@ async function renderUsage(cmd, parent) {
368
357
  const name = arg.name.toUpperCase();
369
358
  const isRequired = arg.required !== false && arg.default === void 0;
370
359
  const usageHint = arg.default ? `="${arg.default}"` : "";
371
- posLines.push([name + usageHint, arg.description || ""]);
360
+ posLines.push(["`" + name + usageHint + "`", arg.description || ""]);
372
361
  usageLine.push(isRequired ? `<${name}>` : `[${name}]`);
373
362
  } else {
374
363
  const isRequired = arg.required === true && arg.default === void 0;
@@ -379,7 +368,7 @@ async function renderUsage(cmd, parent) {
379
368
  ", "
380
369
  )) + (arg.type === "string" && (arg.valueHint || arg.default) ? `=${arg.valueHint ? `<${arg.valueHint}>` : `"${arg.default || ""}"`}` : "");
381
370
  argLines.push([
382
- argStr + (isRequired ? " (required)" : ""),
371
+ "`" + argStr + (isRequired ? " (required)" : "") + "`",
383
372
  arg.description || ""
384
373
  ]);
385
374
  if (isRequired) {
@@ -389,8 +378,11 @@ async function renderUsage(cmd, parent) {
389
378
  }
390
379
  if (cmd.subCommands) {
391
380
  const commandNames = [];
392
- for (const [name, sub] of Object.entries(cmd.subCommands)) {
393
- commandsLines.push([name, sub.meta?.description || ""]);
381
+ const subCommands = await resolveValue(cmd.subCommands);
382
+ for (const [name, sub] of Object.entries(subCommands)) {
383
+ const subCmd = await resolveValue(sub);
384
+ const meta = await resolveValue(subCmd?.meta);
385
+ commandsLines.push([`\`${name}\``, meta?.description || ""]);
394
386
  commandNames.push(name);
395
387
  }
396
388
  usageLine.push(commandNames.join("|"));
@@ -398,29 +390,28 @@ async function renderUsage(cmd, parent) {
398
390
  const usageLines = [];
399
391
  const version = cmdMeta.version || parentMeta.version;
400
392
  usageLines.push(
401
- commandName + (version ? ` v${version}` : ""),
402
- cmdMeta.description,
393
+ utils.colors.gray(
394
+ `${cmdMeta.description} (${commandName + (version ? ` v${version}` : "")})`
395
+ ),
403
396
  ""
404
397
  );
405
398
  const hasOptions = argLines.length > 0 || posLines.length > 0;
406
399
  usageLines.push(
407
- `USAGE: ${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(
408
- " "
409
- )}`,
400
+ `${utils.colors.underline(utils.colors.bold("USAGE"))} \`${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(" ")}\``,
410
401
  ""
411
402
  );
412
403
  if (posLines.length > 0) {
413
- usageLines.push("ARGUMENTS:", "");
404
+ usageLines.push(utils.colors.underline(utils.colors.bold("ARGUMENTS")), "");
414
405
  usageLines.push(formatLineColumns(posLines, " "));
415
406
  usageLines.push("");
416
407
  }
417
408
  if (argLines.length > 0) {
418
- usageLines.push("OPTIONS:", "");
409
+ usageLines.push(utils.colors.underline(utils.colors.bold("OPTIONS")), "");
419
410
  usageLines.push(formatLineColumns(argLines, " "));
420
411
  usageLines.push("");
421
412
  }
422
413
  if (commandsLines.length > 0) {
423
- usageLines.push("COMMANDS:", "");
414
+ usageLines.push(utils.colors.underline(utils.colors.bold("COMMANDS")), "");
424
415
  usageLines.push(formatLineColumns(commandsLines, " "));
425
416
  usageLines.push(
426
417
  "",
@@ -430,155 +421,6 @@ async function renderUsage(cmd, parent) {
430
421
  return usageLines.filter((l) => typeof l === "string").join("\n");
431
422
  }
432
423
 
433
- const {
434
- env = {},
435
- argv = [],
436
- platform = "",
437
- } = typeof process === "undefined" ? {} : process;
438
-
439
- const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
440
- const isForced = "FORCE_COLOR" in env || argv.includes("--color");
441
- const isWindows = platform === "win32";
442
- const isDumbTerminal = env.TERM === "dumb";
443
-
444
- const isCompatibleTerminal =
445
- tty__namespace && tty__namespace.isatty && tty__namespace.isatty(1) && env.TERM && !isDumbTerminal;
446
-
447
- const isCI =
448
- "CI" in env &&
449
- ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
450
-
451
- const isColorSupported =
452
- !isDisabled &&
453
- (isForced || (isWindows && !isDumbTerminal) || isCompatibleTerminal || isCI);
454
-
455
- const replaceClose = (
456
- index,
457
- string,
458
- close,
459
- replace,
460
- head = string.substring(0, index) + replace,
461
- tail = string.substring(index + close.length),
462
- next = tail.indexOf(close)
463
- ) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
464
-
465
- const clearBleed = (index, string, open, close, replace) =>
466
- index < 0
467
- ? open + string + close
468
- : open + replaceClose(index, string, close, replace) + close;
469
-
470
- const filterEmpty =
471
- (open, close, replace = open, at = open.length + 1) =>
472
- (string) =>
473
- string || !(string === "" || string === undefined)
474
- ? clearBleed(
475
- ("" + string).indexOf(close, at),
476
- string,
477
- open,
478
- close,
479
- replace
480
- )
481
- : "";
482
-
483
- const init = (open, close, replace) =>
484
- filterEmpty(`\x1b[${open}m`, `\x1b[${close}m`, replace);
485
-
486
- const colors = {
487
- reset: init(0, 0),
488
- bold: init(1, 22, "\x1b[22m\x1b[1m"),
489
- dim: init(2, 22, "\x1b[22m\x1b[2m"),
490
- italic: init(3, 23),
491
- underline: init(4, 24),
492
- inverse: init(7, 27),
493
- hidden: init(8, 28),
494
- strikethrough: init(9, 29),
495
- black: init(30, 39),
496
- red: init(31, 39),
497
- green: init(32, 39),
498
- yellow: init(33, 39),
499
- blue: init(34, 39),
500
- magenta: init(35, 39),
501
- cyan: init(36, 39),
502
- white: init(37, 39),
503
- gray: init(90, 39),
504
- bgBlack: init(40, 49),
505
- bgRed: init(41, 49),
506
- bgGreen: init(42, 49),
507
- bgYellow: init(43, 49),
508
- bgBlue: init(44, 49),
509
- bgMagenta: init(45, 49),
510
- bgCyan: init(46, 49),
511
- bgWhite: init(47, 49),
512
- blackBright: init(90, 39),
513
- redBright: init(91, 39),
514
- greenBright: init(92, 39),
515
- yellowBright: init(93, 39),
516
- blueBright: init(94, 39),
517
- magentaBright: init(95, 39),
518
- cyanBright: init(96, 39),
519
- whiteBright: init(97, 39),
520
- bgBlackBright: init(100, 49),
521
- bgRedBright: init(101, 49),
522
- bgGreenBright: init(102, 49),
523
- bgYellowBright: init(103, 49),
524
- bgBlueBright: init(104, 49),
525
- bgMagentaBright: init(105, 49),
526
- bgCyanBright: init(106, 49),
527
- bgWhiteBright: init(107, 49),
528
- };
529
-
530
- const createColors = ({ useColor = isColorSupported } = {}) =>
531
- useColor
532
- ? colors
533
- : Object.keys(colors).reduce(
534
- (colors, key) => ({ ...colors, [key]: String }),
535
- {}
536
- );
537
-
538
- const {
539
- reset,
540
- bold,
541
- dim,
542
- italic,
543
- underline,
544
- inverse,
545
- hidden,
546
- strikethrough,
547
- black,
548
- red,
549
- green,
550
- yellow,
551
- blue,
552
- magenta,
553
- cyan,
554
- white,
555
- gray,
556
- bgBlack,
557
- bgRed,
558
- bgGreen,
559
- bgYellow,
560
- bgBlue,
561
- bgMagenta,
562
- bgCyan,
563
- bgWhite,
564
- blackBright,
565
- redBright,
566
- greenBright,
567
- yellowBright,
568
- blueBright,
569
- magentaBright,
570
- cyanBright,
571
- whiteBright,
572
- bgBlackBright,
573
- bgRedBright,
574
- bgGreenBright,
575
- bgYellowBright,
576
- bgBlueBright,
577
- bgMagentaBright,
578
- bgCyanBright,
579
- bgWhiteBright,
580
- } = createColors();
581
-
582
424
  async function runMain(cmd, opts = {}) {
583
425
  const rawArgs = opts.rawArgs || process.argv.slice(2);
584
426
  try {
@@ -591,16 +433,12 @@ async function runMain(cmd, opts = {}) {
591
433
  } catch (error) {
592
434
  const isCLIError = error instanceof CLIError;
593
435
  if (!isCLIError) {
594
- console.error(error, "\n");
436
+ consola__default.error(error, "\n");
595
437
  }
596
- console.error(
597
- `
598
- ${bgRed(` ${error.code || error.name} `)} ${error.message}
599
- `
600
- );
601
438
  if (isCLIError) {
602
439
  await showUsage(...await resolveSubCommand(cmd, rawArgs));
603
440
  }
441
+ consola__default.error(error.message);
604
442
  process.exit(1);
605
443
  }
606
444
  }
@@ -0,0 +1,76 @@
1
+ type ArgType = "boolean" | "string" | "positional" | undefined;
2
+ type _ArgDef<T extends ArgType, VT extends boolean | string> = {
3
+ type?: T;
4
+ description?: string;
5
+ valueHint?: string;
6
+ alias?: string | string[];
7
+ default?: VT;
8
+ required?: boolean;
9
+ };
10
+ type BooleanArgDef = _ArgDef<"boolean", boolean>;
11
+ type StringArgDef = _ArgDef<"string", string>;
12
+ type PositionalArgDef = Omit<_ArgDef<"positional", string>, "alias">;
13
+ type ArgDef = BooleanArgDef | StringArgDef | PositionalArgDef;
14
+ type ArgsDef = Record<string, ArgDef>;
15
+ type Arg = ArgDef & {
16
+ name: string;
17
+ alias: string[];
18
+ };
19
+ type ParsedArgs<T extends ArgsDef = ArgsDef> = {
20
+ _: string[];
21
+ } & Record<{
22
+ [K in keyof T]: T[K] extends {
23
+ type: "positional";
24
+ } ? K : never;
25
+ }[keyof T], string> & Record<{
26
+ [K in keyof T]: T[K] extends {
27
+ type: "string";
28
+ } ? K : never;
29
+ }[keyof T], string> & Record<{
30
+ [K in keyof T]: T[K] extends {
31
+ type: "boolean";
32
+ } ? K : never;
33
+ }[keyof T], boolean> & Record<string, string | boolean | string[]>;
34
+ interface CommandMeta {
35
+ name?: string;
36
+ version?: string;
37
+ description?: string;
38
+ }
39
+ type SubCommandsDef = Record<string, Resolvable<CommandDef<any>>>;
40
+ type CommandDef<T extends ArgsDef = ArgsDef> = {
41
+ meta?: Resolvable<CommandMeta>;
42
+ args?: Resolvable<T>;
43
+ subCommands?: Resolvable<SubCommandsDef>;
44
+ setup?: (context: CommandContext<T>) => any | Promise<any>;
45
+ cleanup?: (context: CommandContext<T>) => any | Promise<any>;
46
+ run?: (context: CommandContext<T>) => any | Promise<any>;
47
+ };
48
+ type CommandContext<T extends ArgsDef = ArgsDef> = {
49
+ rawArgs: string[];
50
+ args: ParsedArgs<T>;
51
+ cmd: CommandDef<T>;
52
+ subCommand?: CommandDef<T>;
53
+ data?: any;
54
+ };
55
+ type Awaitable<T> = () => T | Promise<T>;
56
+ type Resolvable<T> = T | Promise<T> | (() => T) | (() => Promise<T>);
57
+
58
+ declare function defineCommand<T extends ArgsDef = ArgsDef>(def: CommandDef<T>): CommandDef<T>;
59
+ interface RunCommandOptions {
60
+ rawArgs: string[];
61
+ data?: any;
62
+ showUsage?: boolean;
63
+ }
64
+ declare function runCommand<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, opts: RunCommandOptions): Promise<void>;
65
+
66
+ interface RunMainOptions {
67
+ rawArgs?: string[];
68
+ }
69
+ declare function runMain<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, opts?: RunMainOptions): Promise<void>;
70
+
71
+ declare function parseArgs<T extends ArgsDef = ArgsDef>(rawArgs: string[], argsDef: ArgsDef): ParsedArgs<T>;
72
+
73
+ declare function showUsage<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, parent?: CommandDef<T>): Promise<void>;
74
+ declare function renderUsage<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, parent?: CommandDef<T>): Promise<string>;
75
+
76
+ export { type Arg, type ArgDef, type ArgType, type ArgsDef, type Awaitable, type BooleanArgDef, type CommandContext, type CommandDef, type CommandMeta, type ParsedArgs, type PositionalArgDef, type Resolvable, type RunCommandOptions, type RunMainOptions, type StringArgDef, type SubCommandsDef, type _ArgDef, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage };
@@ -0,0 +1,76 @@
1
+ type ArgType = "boolean" | "string" | "positional" | undefined;
2
+ type _ArgDef<T extends ArgType, VT extends boolean | string> = {
3
+ type?: T;
4
+ description?: string;
5
+ valueHint?: string;
6
+ alias?: string | string[];
7
+ default?: VT;
8
+ required?: boolean;
9
+ };
10
+ type BooleanArgDef = _ArgDef<"boolean", boolean>;
11
+ type StringArgDef = _ArgDef<"string", string>;
12
+ type PositionalArgDef = Omit<_ArgDef<"positional", string>, "alias">;
13
+ type ArgDef = BooleanArgDef | StringArgDef | PositionalArgDef;
14
+ type ArgsDef = Record<string, ArgDef>;
15
+ type Arg = ArgDef & {
16
+ name: string;
17
+ alias: string[];
18
+ };
19
+ type ParsedArgs<T extends ArgsDef = ArgsDef> = {
20
+ _: string[];
21
+ } & Record<{
22
+ [K in keyof T]: T[K] extends {
23
+ type: "positional";
24
+ } ? K : never;
25
+ }[keyof T], string> & Record<{
26
+ [K in keyof T]: T[K] extends {
27
+ type: "string";
28
+ } ? K : never;
29
+ }[keyof T], string> & Record<{
30
+ [K in keyof T]: T[K] extends {
31
+ type: "boolean";
32
+ } ? K : never;
33
+ }[keyof T], boolean> & Record<string, string | boolean | string[]>;
34
+ interface CommandMeta {
35
+ name?: string;
36
+ version?: string;
37
+ description?: string;
38
+ }
39
+ type SubCommandsDef = Record<string, Resolvable<CommandDef<any>>>;
40
+ type CommandDef<T extends ArgsDef = ArgsDef> = {
41
+ meta?: Resolvable<CommandMeta>;
42
+ args?: Resolvable<T>;
43
+ subCommands?: Resolvable<SubCommandsDef>;
44
+ setup?: (context: CommandContext<T>) => any | Promise<any>;
45
+ cleanup?: (context: CommandContext<T>) => any | Promise<any>;
46
+ run?: (context: CommandContext<T>) => any | Promise<any>;
47
+ };
48
+ type CommandContext<T extends ArgsDef = ArgsDef> = {
49
+ rawArgs: string[];
50
+ args: ParsedArgs<T>;
51
+ cmd: CommandDef<T>;
52
+ subCommand?: CommandDef<T>;
53
+ data?: any;
54
+ };
55
+ type Awaitable<T> = () => T | Promise<T>;
56
+ type Resolvable<T> = T | Promise<T> | (() => T) | (() => Promise<T>);
57
+
58
+ declare function defineCommand<T extends ArgsDef = ArgsDef>(def: CommandDef<T>): CommandDef<T>;
59
+ interface RunCommandOptions {
60
+ rawArgs: string[];
61
+ data?: any;
62
+ showUsage?: boolean;
63
+ }
64
+ declare function runCommand<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, opts: RunCommandOptions): Promise<void>;
65
+
66
+ interface RunMainOptions {
67
+ rawArgs?: string[];
68
+ }
69
+ declare function runMain<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, opts?: RunMainOptions): Promise<void>;
70
+
71
+ declare function parseArgs<T extends ArgsDef = ArgsDef>(rawArgs: string[], argsDef: ArgsDef): ParsedArgs<T>;
72
+
73
+ declare function showUsage<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, parent?: CommandDef<T>): Promise<void>;
74
+ declare function renderUsage<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, parent?: CommandDef<T>): Promise<string>;
75
+
76
+ export { type Arg, type ArgDef, type ArgType, type ArgsDef, type Awaitable, type BooleanArgDef, type CommandContext, type CommandDef, type CommandMeta, type ParsedArgs, type PositionalArgDef, type Resolvable, type RunCommandOptions, type RunMainOptions, type StringArgDef, type SubCommandsDef, type _ArgDef, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage };
package/dist/index.d.ts CHANGED
@@ -30,7 +30,7 @@ type ParsedArgs<T extends ArgsDef = ArgsDef> = {
30
30
  [K in keyof T]: T[K] extends {
31
31
  type: "boolean";
32
32
  } ? K : never;
33
- }[keyof T], boolean> & Record<string, string | boolean>;
33
+ }[keyof T], boolean> & Record<string, string | boolean | string[]>;
34
34
  interface CommandMeta {
35
35
  name?: string;
36
36
  version?: string;
@@ -48,8 +48,9 @@ type CommandDef<T extends ArgsDef = ArgsDef> = {
48
48
  type CommandContext<T extends ArgsDef = ArgsDef> = {
49
49
  rawArgs: string[];
50
50
  args: ParsedArgs<T>;
51
- cmd: CommandDef;
51
+ cmd: CommandDef<T>;
52
52
  subCommand?: CommandDef<T>;
53
+ data?: any;
53
54
  };
54
55
  type Awaitable<T> = () => T | Promise<T>;
55
56
  type Resolvable<T> = T | Promise<T> | (() => T) | (() => Promise<T>);
@@ -57,18 +58,19 @@ type Resolvable<T> = T | Promise<T> | (() => T) | (() => Promise<T>);
57
58
  declare function defineCommand<T extends ArgsDef = ArgsDef>(def: CommandDef<T>): CommandDef<T>;
58
59
  interface RunCommandOptions {
59
60
  rawArgs: string[];
61
+ data?: any;
60
62
  showUsage?: boolean;
61
63
  }
62
- declare function runCommand(cmd: CommandDef, opts: RunCommandOptions): Promise<void>;
64
+ declare function runCommand<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, opts: RunCommandOptions): Promise<void>;
63
65
 
64
66
  interface RunMainOptions {
65
67
  rawArgs?: string[];
66
68
  }
67
- declare function runMain(cmd: CommandDef, opts?: RunMainOptions): Promise<void>;
69
+ declare function runMain<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, opts?: RunMainOptions): Promise<void>;
68
70
 
69
- declare function parseArgs(rawArgs: string[], argsDef: ArgsDef): ParsedArgs;
71
+ declare function parseArgs<T extends ArgsDef = ArgsDef>(rawArgs: string[], argsDef: ArgsDef): ParsedArgs<T>;
70
72
 
71
- declare function showUsage(cmd: CommandDef, parent?: CommandDef): Promise<void>;
72
- declare function renderUsage(cmd: CommandDef, parent?: CommandDef): Promise<string>;
73
+ declare function showUsage<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, parent?: CommandDef<T>): Promise<void>;
74
+ declare function renderUsage<T extends ArgsDef = ArgsDef>(cmd: CommandDef<T>, parent?: CommandDef<T>): Promise<string>;
73
75
 
74
- export { Arg, ArgDef, ArgType, ArgsDef, Awaitable, BooleanArgDef, CommandContext, CommandDef, CommandMeta, ParsedArgs, PositionalArgDef, Resolvable, RunCommandOptions, RunMainOptions, StringArgDef, SubCommandsDef, _ArgDef, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage };
76
+ export { type Arg, type ArgDef, type ArgType, type ArgsDef, type Awaitable, type BooleanArgDef, type CommandContext, type CommandDef, type CommandMeta, type ParsedArgs, type PositionalArgDef, type Resolvable, type RunCommandOptions, type RunMainOptions, type StringArgDef, type SubCommandsDef, type _ArgDef, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage };
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
- import * as tty from 'tty';
1
+ import consola from 'consola';
2
+ import { colors } from 'consola/utils';
2
3
 
3
4
  function toArray(val) {
4
5
  if (Array.isArray(val)) {
5
6
  return val;
6
7
  }
7
- return val !== void 0 ? [val] : [];
8
+ return val === void 0 ? [] : [val];
8
9
  }
9
10
  function formatLineColumns(lines, linePrefix = "") {
10
11
  const maxLengh = [];
@@ -245,13 +246,13 @@ function parseArgs(rawArgs, argsDef) {
245
246
  const nextPositionalArgument = positionalArguments.shift();
246
247
  if (nextPositionalArgument !== void 0) {
247
248
  parsedArgsProxy[arg.name] = nextPositionalArgument;
248
- } else if (arg.default !== void 0) {
249
- parsedArgsProxy[arg.name] = arg.default;
250
- } else {
249
+ } else if (arg.default === void 0 && arg.required !== false) {
251
250
  throw new CLIError(
252
251
  `Missing required positional argument: ${arg.name.toUpperCase()}`,
253
252
  "EARG"
254
253
  );
254
+ } else {
255
+ parsedArgsProxy[arg.name] = arg.default;
255
256
  }
256
257
  } else if (arg.required && parsedArgsProxy[arg.name] === void 0) {
257
258
  throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
@@ -280,6 +281,7 @@ async function runCommand(cmd, opts) {
280
281
  const context = {
281
282
  rawArgs: opts.rawArgs,
282
283
  args: parsedArgs,
284
+ data: opts.data,
283
285
  cmd
284
286
  };
285
287
  if (typeof cmd.setup === "function") {
@@ -292,15 +294,12 @@ async function runCommand(cmd, opts) {
292
294
  );
293
295
  const subCommandName = opts.rawArgs[subCommandArgIndex];
294
296
  if (!subCommandName && !cmd.run) {
295
- throw new CLIError(
296
- `Missing sub command. Use --help to see available sub commands.`,
297
- "ESUBCOMMAND"
298
- );
297
+ throw new CLIError(`No command specified.`, "E_NO_COMMAND");
299
298
  }
300
299
  if (!subCommands[subCommandName]) {
301
300
  throw new CLIError(
302
- `Unknown sub command: ${subCommandName}`,
303
- "ESUBCOMMAND"
301
+ `Unknown command \`${subCommandName}\``,
302
+ "E_UNKNOWN_COMMAND"
304
303
  );
305
304
  }
306
305
  const subCommand = await resolveValue(subCommands[subCommandName]);
@@ -333,9 +332,9 @@ async function resolveSubCommand(cmd, rawArgs, parent) {
333
332
 
334
333
  async function showUsage(cmd, parent) {
335
334
  try {
336
- console.log(await renderUsage(cmd, parent) + "\n");
335
+ consola.log(await renderUsage(cmd, parent) + "\n");
337
336
  } catch (error) {
338
- console.error(error);
337
+ consola.error(error);
339
338
  }
340
339
  }
341
340
  async function renderUsage(cmd, parent) {
@@ -352,7 +351,7 @@ async function renderUsage(cmd, parent) {
352
351
  const name = arg.name.toUpperCase();
353
352
  const isRequired = arg.required !== false && arg.default === void 0;
354
353
  const usageHint = arg.default ? `="${arg.default}"` : "";
355
- posLines.push([name + usageHint, arg.description || ""]);
354
+ posLines.push(["`" + name + usageHint + "`", arg.description || ""]);
356
355
  usageLine.push(isRequired ? `<${name}>` : `[${name}]`);
357
356
  } else {
358
357
  const isRequired = arg.required === true && arg.default === void 0;
@@ -363,7 +362,7 @@ async function renderUsage(cmd, parent) {
363
362
  ", "
364
363
  )) + (arg.type === "string" && (arg.valueHint || arg.default) ? `=${arg.valueHint ? `<${arg.valueHint}>` : `"${arg.default || ""}"`}` : "");
365
364
  argLines.push([
366
- argStr + (isRequired ? " (required)" : ""),
365
+ "`" + argStr + (isRequired ? " (required)" : "") + "`",
367
366
  arg.description || ""
368
367
  ]);
369
368
  if (isRequired) {
@@ -373,8 +372,11 @@ async function renderUsage(cmd, parent) {
373
372
  }
374
373
  if (cmd.subCommands) {
375
374
  const commandNames = [];
376
- for (const [name, sub] of Object.entries(cmd.subCommands)) {
377
- commandsLines.push([name, sub.meta?.description || ""]);
375
+ const subCommands = await resolveValue(cmd.subCommands);
376
+ for (const [name, sub] of Object.entries(subCommands)) {
377
+ const subCmd = await resolveValue(sub);
378
+ const meta = await resolveValue(subCmd?.meta);
379
+ commandsLines.push([`\`${name}\``, meta?.description || ""]);
378
380
  commandNames.push(name);
379
381
  }
380
382
  usageLine.push(commandNames.join("|"));
@@ -382,29 +384,28 @@ async function renderUsage(cmd, parent) {
382
384
  const usageLines = [];
383
385
  const version = cmdMeta.version || parentMeta.version;
384
386
  usageLines.push(
385
- commandName + (version ? ` v${version}` : ""),
386
- cmdMeta.description,
387
+ colors.gray(
388
+ `${cmdMeta.description} (${commandName + (version ? ` v${version}` : "")})`
389
+ ),
387
390
  ""
388
391
  );
389
392
  const hasOptions = argLines.length > 0 || posLines.length > 0;
390
393
  usageLines.push(
391
- `USAGE: ${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(
392
- " "
393
- )}`,
394
+ `${colors.underline(colors.bold("USAGE"))} \`${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(" ")}\``,
394
395
  ""
395
396
  );
396
397
  if (posLines.length > 0) {
397
- usageLines.push("ARGUMENTS:", "");
398
+ usageLines.push(colors.underline(colors.bold("ARGUMENTS")), "");
398
399
  usageLines.push(formatLineColumns(posLines, " "));
399
400
  usageLines.push("");
400
401
  }
401
402
  if (argLines.length > 0) {
402
- usageLines.push("OPTIONS:", "");
403
+ usageLines.push(colors.underline(colors.bold("OPTIONS")), "");
403
404
  usageLines.push(formatLineColumns(argLines, " "));
404
405
  usageLines.push("");
405
406
  }
406
407
  if (commandsLines.length > 0) {
407
- usageLines.push("COMMANDS:", "");
408
+ usageLines.push(colors.underline(colors.bold("COMMANDS")), "");
408
409
  usageLines.push(formatLineColumns(commandsLines, " "));
409
410
  usageLines.push(
410
411
  "",
@@ -414,155 +415,6 @@ async function renderUsage(cmd, parent) {
414
415
  return usageLines.filter((l) => typeof l === "string").join("\n");
415
416
  }
416
417
 
417
- const {
418
- env = {},
419
- argv = [],
420
- platform = "",
421
- } = typeof process === "undefined" ? {} : process;
422
-
423
- const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
424
- const isForced = "FORCE_COLOR" in env || argv.includes("--color");
425
- const isWindows = platform === "win32";
426
- const isDumbTerminal = env.TERM === "dumb";
427
-
428
- const isCompatibleTerminal =
429
- tty && tty.isatty && tty.isatty(1) && env.TERM && !isDumbTerminal;
430
-
431
- const isCI =
432
- "CI" in env &&
433
- ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
434
-
435
- const isColorSupported =
436
- !isDisabled &&
437
- (isForced || (isWindows && !isDumbTerminal) || isCompatibleTerminal || isCI);
438
-
439
- const replaceClose = (
440
- index,
441
- string,
442
- close,
443
- replace,
444
- head = string.substring(0, index) + replace,
445
- tail = string.substring(index + close.length),
446
- next = tail.indexOf(close)
447
- ) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
448
-
449
- const clearBleed = (index, string, open, close, replace) =>
450
- index < 0
451
- ? open + string + close
452
- : open + replaceClose(index, string, close, replace) + close;
453
-
454
- const filterEmpty =
455
- (open, close, replace = open, at = open.length + 1) =>
456
- (string) =>
457
- string || !(string === "" || string === undefined)
458
- ? clearBleed(
459
- ("" + string).indexOf(close, at),
460
- string,
461
- open,
462
- close,
463
- replace
464
- )
465
- : "";
466
-
467
- const init = (open, close, replace) =>
468
- filterEmpty(`\x1b[${open}m`, `\x1b[${close}m`, replace);
469
-
470
- const colors = {
471
- reset: init(0, 0),
472
- bold: init(1, 22, "\x1b[22m\x1b[1m"),
473
- dim: init(2, 22, "\x1b[22m\x1b[2m"),
474
- italic: init(3, 23),
475
- underline: init(4, 24),
476
- inverse: init(7, 27),
477
- hidden: init(8, 28),
478
- strikethrough: init(9, 29),
479
- black: init(30, 39),
480
- red: init(31, 39),
481
- green: init(32, 39),
482
- yellow: init(33, 39),
483
- blue: init(34, 39),
484
- magenta: init(35, 39),
485
- cyan: init(36, 39),
486
- white: init(37, 39),
487
- gray: init(90, 39),
488
- bgBlack: init(40, 49),
489
- bgRed: init(41, 49),
490
- bgGreen: init(42, 49),
491
- bgYellow: init(43, 49),
492
- bgBlue: init(44, 49),
493
- bgMagenta: init(45, 49),
494
- bgCyan: init(46, 49),
495
- bgWhite: init(47, 49),
496
- blackBright: init(90, 39),
497
- redBright: init(91, 39),
498
- greenBright: init(92, 39),
499
- yellowBright: init(93, 39),
500
- blueBright: init(94, 39),
501
- magentaBright: init(95, 39),
502
- cyanBright: init(96, 39),
503
- whiteBright: init(97, 39),
504
- bgBlackBright: init(100, 49),
505
- bgRedBright: init(101, 49),
506
- bgGreenBright: init(102, 49),
507
- bgYellowBright: init(103, 49),
508
- bgBlueBright: init(104, 49),
509
- bgMagentaBright: init(105, 49),
510
- bgCyanBright: init(106, 49),
511
- bgWhiteBright: init(107, 49),
512
- };
513
-
514
- const createColors = ({ useColor = isColorSupported } = {}) =>
515
- useColor
516
- ? colors
517
- : Object.keys(colors).reduce(
518
- (colors, key) => ({ ...colors, [key]: String }),
519
- {}
520
- );
521
-
522
- const {
523
- reset,
524
- bold,
525
- dim,
526
- italic,
527
- underline,
528
- inverse,
529
- hidden,
530
- strikethrough,
531
- black,
532
- red,
533
- green,
534
- yellow,
535
- blue,
536
- magenta,
537
- cyan,
538
- white,
539
- gray,
540
- bgBlack,
541
- bgRed,
542
- bgGreen,
543
- bgYellow,
544
- bgBlue,
545
- bgMagenta,
546
- bgCyan,
547
- bgWhite,
548
- blackBright,
549
- redBright,
550
- greenBright,
551
- yellowBright,
552
- blueBright,
553
- magentaBright,
554
- cyanBright,
555
- whiteBright,
556
- bgBlackBright,
557
- bgRedBright,
558
- bgGreenBright,
559
- bgYellowBright,
560
- bgBlueBright,
561
- bgMagentaBright,
562
- bgCyanBright,
563
- bgWhiteBright,
564
- } = createColors();
565
-
566
418
  async function runMain(cmd, opts = {}) {
567
419
  const rawArgs = opts.rawArgs || process.argv.slice(2);
568
420
  try {
@@ -575,16 +427,12 @@ async function runMain(cmd, opts = {}) {
575
427
  } catch (error) {
576
428
  const isCLIError = error instanceof CLIError;
577
429
  if (!isCLIError) {
578
- console.error(error, "\n");
430
+ consola.error(error, "\n");
579
431
  }
580
- console.error(
581
- `
582
- ${bgRed(` ${error.code || error.name} `)} ${error.message}
583
- `
584
- );
585
432
  if (isCLIError) {
586
433
  await showUsage(...await resolveSubCommand(cmd, rawArgs));
587
434
  }
435
+ consola.error(error.message);
588
436
  process.exit(1);
589
437
  }
590
438
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "citty",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Elegant CLI Builder",
5
5
  "repository": "unjs/citty",
6
6
  "license": "MIT",
@@ -29,19 +29,21 @@
29
29
  "release": "pnpm test && changelogen --release --push && npm publish",
30
30
  "test": "pnpm lint && vitest run --coverage"
31
31
  },
32
+ "dependencies": {
33
+ "consola": "^3.2.3"
34
+ },
32
35
  "devDependencies": {
33
- "@types/node": "^18.15.11",
34
- "@vitest/coverage-c8": "^0.29.8",
35
- "changelogen": "^0.5.2",
36
- "colorette": "^2.0.19",
37
- "eslint": "^8.37.0",
38
- "eslint-config-unjs": "^0.1.0",
39
- "jiti": "^1.18.2",
40
- "prettier": "^2.8.7",
36
+ "@types/node": "^20.5.4",
37
+ "@vitest/coverage-v8": "^0.34.2",
38
+ "changelogen": "^0.5.5",
39
+ "eslint": "^8.47.0",
40
+ "eslint-config-unjs": "^0.2.1",
41
+ "jiti": "^1.19.3",
42
+ "prettier": "^3.0.2",
41
43
  "scule": "^1.0.0",
42
- "typescript": "^5.0.3",
43
- "unbuild": "^1.2.0",
44
- "vitest": "^0.29.8"
44
+ "typescript": "^5.2.2",
45
+ "unbuild": "^2.0.0",
46
+ "vitest": "^0.34.2"
45
47
  },
46
- "packageManager": "pnpm@8.1.0"
48
+ "packageManager": "pnpm@8.6.12"
47
49
  }