citty 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,7 +1,20 @@
1
1
  'use strict';
2
2
 
3
- const scule = require('scule');
4
- const colorette = require('colorette');
3
+ const tty = require('tty');
4
+
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
+ }
16
+
17
+ const tty__namespace = /*#__PURE__*/_interopNamespaceCompat(tty);
5
18
 
6
19
  function toArray(val) {
7
20
  if (Array.isArray(val)) {
@@ -33,6 +46,70 @@ class CLIError extends Error {
33
46
  }
34
47
  }
35
48
 
49
+ const NUMBER_CHAR_RE = /\d/;
50
+ const STR_SPLITTERS = ["-", "_", "/", "."];
51
+ function isUppercase(char = "") {
52
+ if (NUMBER_CHAR_RE.test(char)) {
53
+ return void 0;
54
+ }
55
+ return char.toUpperCase() === char;
56
+ }
57
+ function splitByCase(string_, separators) {
58
+ const splitters = separators ?? STR_SPLITTERS;
59
+ const parts = [];
60
+ if (!string_ || typeof string_ !== "string") {
61
+ return parts;
62
+ }
63
+ let buff = "";
64
+ let previousUpper;
65
+ let previousSplitter;
66
+ for (const char of string_) {
67
+ const isSplitter = splitters.includes(char);
68
+ if (isSplitter === true) {
69
+ parts.push(buff);
70
+ buff = "";
71
+ previousUpper = void 0;
72
+ continue;
73
+ }
74
+ const isUpper = isUppercase(char);
75
+ if (previousSplitter === false) {
76
+ if (previousUpper === false && isUpper === true) {
77
+ parts.push(buff);
78
+ buff = char;
79
+ previousUpper = isUpper;
80
+ continue;
81
+ }
82
+ if (previousUpper === true && isUpper === false && buff.length > 1) {
83
+ const lastChar = buff[buff.length - 1];
84
+ parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
85
+ buff = lastChar + char;
86
+ previousUpper = isUpper;
87
+ continue;
88
+ }
89
+ }
90
+ buff += char;
91
+ previousUpper = isUpper;
92
+ previousSplitter = isSplitter;
93
+ }
94
+ parts.push(buff);
95
+ return parts;
96
+ }
97
+ function upperFirst(string_) {
98
+ return !string_ ? "" : string_[0].toUpperCase() + string_.slice(1);
99
+ }
100
+ function lowerFirst(string_) {
101
+ return !string_ ? "" : string_[0].toLowerCase() + string_.slice(1);
102
+ }
103
+ function pascalCase(string_) {
104
+ return !string_ ? "" : (Array.isArray(string_) ? string_ : splitByCase(string_)).map((p) => upperFirst(p)).join("");
105
+ }
106
+ function camelCase(string_) {
107
+ return lowerFirst(pascalCase(string_));
108
+ }
109
+ function kebabCase(string_, joiner) {
110
+ return !string_ ? "" : (Array.isArray(string_) ? string_ : splitByCase(string_)).map((p) => p.toLowerCase()).join(joiner ?? "-");
111
+ }
112
+
36
113
  function toArr(any) {
37
114
  return any == void 0 ? [] : Array.isArray(any) ? any : [any];
38
115
  }
@@ -151,6 +228,7 @@ function parseArgs(rawArgs, argsDef) {
151
228
  const parseOptions = {
152
229
  boolean: [],
153
230
  string: [],
231
+ mixed: [],
154
232
  alias: {},
155
233
  default: {}
156
234
  };
@@ -159,7 +237,11 @@ function parseArgs(rawArgs, argsDef) {
159
237
  if (arg.type === "positional") {
160
238
  continue;
161
239
  }
162
- parseOptions[arg.type || "boolean"].push(arg.name);
240
+ if (arg.type === "string") {
241
+ parseOptions.string.push(arg.name);
242
+ } else if (arg.type === "boolean") {
243
+ parseOptions.boolean.push(arg.name);
244
+ }
163
245
  if (arg.default !== void 0) {
164
246
  parseOptions.default[arg.name] = arg.default;
165
247
  }
@@ -168,10 +250,10 @@ function parseArgs(rawArgs, argsDef) {
168
250
  }
169
251
  }
170
252
  const parsed = parseRawArgs(rawArgs, parseOptions);
171
- const [, ...positionalArguments] = parsed._;
253
+ const [...positionalArguments] = parsed._;
172
254
  const parsedArgsProxy = new Proxy(parsed, {
173
255
  get(target, prop) {
174
- return target[prop] ?? target[scule.camelCase(prop)] ?? target[scule.kebabCase(prop)];
256
+ return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];
175
257
  }
176
258
  });
177
259
  for (const [, arg] of args.entries()) {
@@ -240,7 +322,7 @@ async function runCommand(cmd, opts) {
240
322
  const subCommand = await resolveValue(subCommands[subCommandName]);
241
323
  if (subCommand) {
242
324
  await runCommand(subCommand, {
243
- rawArgs: opts.rawArgs.slice(subCommandArgIndex)
325
+ rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1)
244
326
  });
245
327
  }
246
328
  }
@@ -257,7 +339,7 @@ async function resolveSubCommand(cmd, rawArgs, parent) {
257
339
  if (subCommand) {
258
340
  return resolveSubCommand(
259
341
  subCommand,
260
- rawArgs.slice(subCommandArgIndex),
342
+ rawArgs.slice(subCommandArgIndex + 1),
261
343
  cmd
262
344
  );
263
345
  }
@@ -348,6 +430,155 @@ async function renderUsage(cmd, parent) {
348
430
  return usageLines.filter((l) => typeof l === "string").join("\n");
349
431
  }
350
432
 
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
+
351
582
  async function runMain(cmd, opts = {}) {
352
583
  const rawArgs = opts.rawArgs || process.argv.slice(2);
353
584
  try {
@@ -364,7 +595,7 @@ async function runMain(cmd, opts = {}) {
364
595
  }
365
596
  console.error(
366
597
  `
367
- ${colorette.bgRed(` ${error.code || error.name} `)} ${error.message}
598
+ ${bgRed(` ${error.code || error.name} `)} ${error.message}
368
599
  `
369
600
  );
370
601
  if (isCLIError) {
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ type ParsedArgs<T extends ArgsDef = ArgsDef> = {
22
22
  [K in keyof T]: T[K] extends {
23
23
  type: "positional";
24
24
  } ? K : never;
25
- }[keyof T], string | boolean> & Record<{
25
+ }[keyof T], string> & Record<{
26
26
  [K in keyof T]: T[K] extends {
27
27
  type: "string";
28
28
  } ? K : never;
@@ -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>;
33
+ }[keyof T], boolean> & Record<string, string | boolean>;
34
34
  interface CommandMeta {
35
35
  name?: string;
36
36
  version?: string;
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
1
- import { camelCase, kebabCase } from 'scule';
2
- import { bgRed } from 'colorette';
1
+ import * as tty from 'tty';
3
2
 
4
3
  function toArray(val) {
5
4
  if (Array.isArray(val)) {
@@ -31,6 +30,70 @@ class CLIError extends Error {
31
30
  }
32
31
  }
33
32
 
33
+ const NUMBER_CHAR_RE = /\d/;
34
+ const STR_SPLITTERS = ["-", "_", "/", "."];
35
+ function isUppercase(char = "") {
36
+ if (NUMBER_CHAR_RE.test(char)) {
37
+ return void 0;
38
+ }
39
+ return char.toUpperCase() === char;
40
+ }
41
+ function splitByCase(string_, separators) {
42
+ const splitters = separators ?? STR_SPLITTERS;
43
+ const parts = [];
44
+ if (!string_ || typeof string_ !== "string") {
45
+ return parts;
46
+ }
47
+ let buff = "";
48
+ let previousUpper;
49
+ let previousSplitter;
50
+ for (const char of string_) {
51
+ const isSplitter = splitters.includes(char);
52
+ if (isSplitter === true) {
53
+ parts.push(buff);
54
+ buff = "";
55
+ previousUpper = void 0;
56
+ continue;
57
+ }
58
+ const isUpper = isUppercase(char);
59
+ if (previousSplitter === false) {
60
+ if (previousUpper === false && isUpper === true) {
61
+ parts.push(buff);
62
+ buff = char;
63
+ previousUpper = isUpper;
64
+ continue;
65
+ }
66
+ if (previousUpper === true && isUpper === false && buff.length > 1) {
67
+ const lastChar = buff[buff.length - 1];
68
+ parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
69
+ buff = lastChar + char;
70
+ previousUpper = isUpper;
71
+ continue;
72
+ }
73
+ }
74
+ buff += char;
75
+ previousUpper = isUpper;
76
+ previousSplitter = isSplitter;
77
+ }
78
+ parts.push(buff);
79
+ return parts;
80
+ }
81
+ function upperFirst(string_) {
82
+ return !string_ ? "" : string_[0].toUpperCase() + string_.slice(1);
83
+ }
84
+ function lowerFirst(string_) {
85
+ return !string_ ? "" : string_[0].toLowerCase() + string_.slice(1);
86
+ }
87
+ function pascalCase(string_) {
88
+ return !string_ ? "" : (Array.isArray(string_) ? string_ : splitByCase(string_)).map((p) => upperFirst(p)).join("");
89
+ }
90
+ function camelCase(string_) {
91
+ return lowerFirst(pascalCase(string_));
92
+ }
93
+ function kebabCase(string_, joiner) {
94
+ return !string_ ? "" : (Array.isArray(string_) ? string_ : splitByCase(string_)).map((p) => p.toLowerCase()).join(joiner ?? "-");
95
+ }
96
+
34
97
  function toArr(any) {
35
98
  return any == void 0 ? [] : Array.isArray(any) ? any : [any];
36
99
  }
@@ -149,6 +212,7 @@ function parseArgs(rawArgs, argsDef) {
149
212
  const parseOptions = {
150
213
  boolean: [],
151
214
  string: [],
215
+ mixed: [],
152
216
  alias: {},
153
217
  default: {}
154
218
  };
@@ -157,7 +221,11 @@ function parseArgs(rawArgs, argsDef) {
157
221
  if (arg.type === "positional") {
158
222
  continue;
159
223
  }
160
- parseOptions[arg.type || "boolean"].push(arg.name);
224
+ if (arg.type === "string") {
225
+ parseOptions.string.push(arg.name);
226
+ } else if (arg.type === "boolean") {
227
+ parseOptions.boolean.push(arg.name);
228
+ }
161
229
  if (arg.default !== void 0) {
162
230
  parseOptions.default[arg.name] = arg.default;
163
231
  }
@@ -166,7 +234,7 @@ function parseArgs(rawArgs, argsDef) {
166
234
  }
167
235
  }
168
236
  const parsed = parseRawArgs(rawArgs, parseOptions);
169
- const [, ...positionalArguments] = parsed._;
237
+ const [...positionalArguments] = parsed._;
170
238
  const parsedArgsProxy = new Proxy(parsed, {
171
239
  get(target, prop) {
172
240
  return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];
@@ -238,7 +306,7 @@ async function runCommand(cmd, opts) {
238
306
  const subCommand = await resolveValue(subCommands[subCommandName]);
239
307
  if (subCommand) {
240
308
  await runCommand(subCommand, {
241
- rawArgs: opts.rawArgs.slice(subCommandArgIndex)
309
+ rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1)
242
310
  });
243
311
  }
244
312
  }
@@ -255,7 +323,7 @@ async function resolveSubCommand(cmd, rawArgs, parent) {
255
323
  if (subCommand) {
256
324
  return resolveSubCommand(
257
325
  subCommand,
258
- rawArgs.slice(subCommandArgIndex),
326
+ rawArgs.slice(subCommandArgIndex + 1),
259
327
  cmd
260
328
  );
261
329
  }
@@ -346,6 +414,155 @@ async function renderUsage(cmd, parent) {
346
414
  return usageLines.filter((l) => typeof l === "string").join("\n");
347
415
  }
348
416
 
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
+
349
566
  async function runMain(cmd, opts = {}) {
350
567
  const rawArgs = opts.rawArgs || process.argv.slice(2);
351
568
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "citty",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Elegant CLI Builder",
5
5
  "repository": "unjs/citty",
6
6
  "license": "MIT",
@@ -29,7 +29,7 @@
29
29
  "release": "pnpm test && changelogen --release --push && npm publish",
30
30
  "test": "pnpm lint && vitest run --coverage"
31
31
  },
32
- "dependencies": {
32
+ "devDependencies": {
33
33
  "@types/node": "^18.15.11",
34
34
  "@vitest/coverage-c8": "^0.29.8",
35
35
  "changelogen": "^0.5.2",
@@ -40,7 +40,7 @@
40
40
  "prettier": "^2.8.7",
41
41
  "scule": "^1.0.0",
42
42
  "typescript": "^5.0.3",
43
- "unbuild": "^1.1.2",
43
+ "unbuild": "^1.2.0",
44
44
  "vitest": "^0.29.8"
45
45
  },
46
46
  "packageManager": "pnpm@8.1.0"