citty 0.0.1 → 0.1.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/dist/index.cjs CHANGED
@@ -1,19 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const tty = require('tty');
4
-
5
- function _interopNamespaceDefault(e) {
6
- const n = Object.create(null);
7
- if (e) {
8
- for (const k in e) {
9
- n[k] = e[k];
10
- }
11
- }
12
- n.default = e;
13
- return n;
14
- }
15
-
16
- const tty__namespace = /*#__PURE__*/_interopNamespaceDefault(tty);
3
+ const scule = require('scule');
4
+ const colorette = require('colorette');
17
5
 
18
6
  function toArray(val) {
19
7
  if (Array.isArray(val)) {
@@ -180,23 +168,30 @@ function parseArgs(rawArgs, argsDef) {
180
168
  }
181
169
  }
182
170
  const parsed = parseRawArgs(rawArgs, parseOptions);
183
- for (const [i, arg] of args.entries()) {
171
+ const [, ...positionalArguments] = parsed._;
172
+ const parsedArgsProxy = new Proxy(parsed, {
173
+ get(target, prop) {
174
+ return target[prop] ?? target[scule.camelCase(prop)] ?? target[scule.kebabCase(prop)];
175
+ }
176
+ });
177
+ for (const [, arg] of args.entries()) {
184
178
  if (arg.type === "positional") {
185
- if (parsed._[i] !== void 0) {
186
- parsed[arg.name] = parsed._[i];
179
+ const nextPositionalArgument = positionalArguments.shift();
180
+ if (nextPositionalArgument !== void 0) {
181
+ parsedArgsProxy[arg.name] = nextPositionalArgument;
187
182
  } else if (arg.default !== void 0) {
188
- parsed[arg.name] = arg.default;
183
+ parsedArgsProxy[arg.name] = arg.default;
189
184
  } else {
190
185
  throw new CLIError(
191
186
  `Missing required positional argument: ${arg.name.toUpperCase()}`,
192
187
  "EARG"
193
188
  );
194
189
  }
195
- } else if (arg.required && parsed[arg.name] === void 0) {
190
+ } else if (arg.required && parsedArgsProxy[arg.name] === void 0) {
196
191
  throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
197
192
  }
198
193
  }
199
- return parsed;
194
+ return parsedArgsProxy;
200
195
  }
201
196
  function resolveArgs(argsDef) {
202
197
  const args = [];
@@ -214,17 +209,18 @@ function defineCommand(def) {
214
209
  return def;
215
210
  }
216
211
  async function runCommand(cmd, opts) {
217
- if (typeof cmd.run === "function") {
218
- const cmdArgs = await resolveValue(cmd.args || {});
219
- const parsedArgs = parseArgs(opts.rawArgs, cmdArgs);
220
- await cmd.run({
221
- rawArgs: opts.rawArgs,
222
- args: parsedArgs,
223
- cmd
224
- });
212
+ const cmdArgs = await resolveValue(cmd.args || {});
213
+ const parsedArgs = parseArgs(opts.rawArgs, cmdArgs);
214
+ const context = {
215
+ rawArgs: opts.rawArgs,
216
+ args: parsedArgs,
217
+ cmd
218
+ };
219
+ if (typeof cmd.setup === "function") {
220
+ await cmd.setup(context);
225
221
  }
226
222
  const subCommands = await resolveValue(cmd.subCommands);
227
- if (subCommands && Object.keys(subCommands.length > 0)) {
223
+ if (subCommands && Object.keys(subCommands).length > 0) {
228
224
  const subCommandArgIndex = opts.rawArgs.findIndex(
229
225
  (arg) => !arg.startsWith("-")
230
226
  );
@@ -248,10 +244,13 @@ async function runCommand(cmd, opts) {
248
244
  });
249
245
  }
250
246
  }
247
+ if (typeof cmd.run === "function") {
248
+ await cmd.run(context);
249
+ }
251
250
  }
252
251
  async function resolveSubCommand(cmd, rawArgs, parent) {
253
252
  const subCommands = await resolveValue(cmd.subCommands);
254
- if (subCommands && Object.keys(subCommands.length > 0)) {
253
+ if (subCommands && Object.keys(subCommands).length > 0) {
255
254
  const subCommandArgIndex = rawArgs.findIndex((arg) => !arg.startsWith("-"));
256
255
  const subCommandName = rawArgs[subCommandArgIndex];
257
256
  const subCommand = await resolveValue(subCommands[subCommandName]);
@@ -349,155 +348,6 @@ async function renderUsage(cmd, parent) {
349
348
  return usageLines.filter((l) => typeof l === "string").join("\n");
350
349
  }
351
350
 
352
- const {
353
- env = {},
354
- argv = [],
355
- platform = "",
356
- } = typeof process === "undefined" ? {} : process;
357
-
358
- const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
359
- const isForced = "FORCE_COLOR" in env || argv.includes("--color");
360
- const isWindows = platform === "win32";
361
- const isDumbTerminal = env.TERM === "dumb";
362
-
363
- const isCompatibleTerminal =
364
- tty__namespace && tty__namespace.isatty && tty__namespace.isatty(1) && env.TERM && !isDumbTerminal;
365
-
366
- const isCI =
367
- "CI" in env &&
368
- ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
369
-
370
- const isColorSupported =
371
- !isDisabled &&
372
- (isForced || (isWindows && !isDumbTerminal) || isCompatibleTerminal || isCI);
373
-
374
- const replaceClose = (
375
- index,
376
- string,
377
- close,
378
- replace,
379
- head = string.substring(0, index) + replace,
380
- tail = string.substring(index + close.length),
381
- next = tail.indexOf(close)
382
- ) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
383
-
384
- const clearBleed = (index, string, open, close, replace) =>
385
- index < 0
386
- ? open + string + close
387
- : open + replaceClose(index, string, close, replace) + close;
388
-
389
- const filterEmpty =
390
- (open, close, replace = open, at = open.length + 1) =>
391
- (string) =>
392
- string || !(string === "" || string === undefined)
393
- ? clearBleed(
394
- ("" + string).indexOf(close, at),
395
- string,
396
- open,
397
- close,
398
- replace
399
- )
400
- : "";
401
-
402
- const init = (open, close, replace) =>
403
- filterEmpty(`\x1b[${open}m`, `\x1b[${close}m`, replace);
404
-
405
- const colors = {
406
- reset: init(0, 0),
407
- bold: init(1, 22, "\x1b[22m\x1b[1m"),
408
- dim: init(2, 22, "\x1b[22m\x1b[2m"),
409
- italic: init(3, 23),
410
- underline: init(4, 24),
411
- inverse: init(7, 27),
412
- hidden: init(8, 28),
413
- strikethrough: init(9, 29),
414
- black: init(30, 39),
415
- red: init(31, 39),
416
- green: init(32, 39),
417
- yellow: init(33, 39),
418
- blue: init(34, 39),
419
- magenta: init(35, 39),
420
- cyan: init(36, 39),
421
- white: init(37, 39),
422
- gray: init(90, 39),
423
- bgBlack: init(40, 49),
424
- bgRed: init(41, 49),
425
- bgGreen: init(42, 49),
426
- bgYellow: init(43, 49),
427
- bgBlue: init(44, 49),
428
- bgMagenta: init(45, 49),
429
- bgCyan: init(46, 49),
430
- bgWhite: init(47, 49),
431
- blackBright: init(90, 39),
432
- redBright: init(91, 39),
433
- greenBright: init(92, 39),
434
- yellowBright: init(93, 39),
435
- blueBright: init(94, 39),
436
- magentaBright: init(95, 39),
437
- cyanBright: init(96, 39),
438
- whiteBright: init(97, 39),
439
- bgBlackBright: init(100, 49),
440
- bgRedBright: init(101, 49),
441
- bgGreenBright: init(102, 49),
442
- bgYellowBright: init(103, 49),
443
- bgBlueBright: init(104, 49),
444
- bgMagentaBright: init(105, 49),
445
- bgCyanBright: init(106, 49),
446
- bgWhiteBright: init(107, 49),
447
- };
448
-
449
- const createColors = ({ useColor = isColorSupported } = {}) =>
450
- useColor
451
- ? colors
452
- : Object.keys(colors).reduce(
453
- (colors, key) => ({ ...colors, [key]: String }),
454
- {}
455
- );
456
-
457
- const {
458
- reset,
459
- bold,
460
- dim,
461
- italic,
462
- underline,
463
- inverse,
464
- hidden,
465
- strikethrough,
466
- black,
467
- red,
468
- green,
469
- yellow,
470
- blue,
471
- magenta,
472
- cyan,
473
- white,
474
- gray,
475
- bgBlack,
476
- bgRed,
477
- bgGreen,
478
- bgYellow,
479
- bgBlue,
480
- bgMagenta,
481
- bgCyan,
482
- bgWhite,
483
- blackBright,
484
- redBright,
485
- greenBright,
486
- yellowBright,
487
- blueBright,
488
- magentaBright,
489
- cyanBright,
490
- whiteBright,
491
- bgBlackBright,
492
- bgRedBright,
493
- bgGreenBright,
494
- bgYellowBright,
495
- bgBlueBright,
496
- bgMagentaBright,
497
- bgCyanBright,
498
- bgWhiteBright,
499
- } = createColors();
500
-
501
351
  async function runMain(cmd, opts = {}) {
502
352
  const rawArgs = opts.rawArgs || process.argv.slice(2);
503
353
  try {
@@ -514,7 +364,7 @@ async function runMain(cmd, opts = {}) {
514
364
  }
515
365
  console.error(
516
366
  `
517
- ${bgRed(` ${error.code || error.name} `)} ${error.message}
367
+ ${colorette.bgRed(` ${error.code || error.name} `)} ${error.message}
518
368
  `
519
369
  );
520
370
  if (isCLIError) {
package/dist/index.d.ts CHANGED
@@ -16,31 +16,45 @@ type Arg = ArgDef & {
16
16
  name: string;
17
17
  alias: string[];
18
18
  };
19
- type ParsedArgs = Record<string, string | boolean> & {
19
+ type ParsedArgs<T extends ArgsDef = ArgsDef> = {
20
20
  _: string[];
21
- };
21
+ } & Record<{
22
+ [K in keyof T]: T[K] extends {
23
+ type: "positional";
24
+ } ? K : never;
25
+ }[keyof T], string | boolean> & 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>;
22
34
  interface CommandMeta {
23
35
  name?: string;
24
36
  version?: string;
25
37
  description?: string;
26
38
  }
27
- type SubCommandsDef = Record<string, Resolvable<CommandDef>>;
28
- type CommandDef = {
39
+ type SubCommandsDef = Record<string, Resolvable<CommandDef<any>>>;
40
+ type CommandDef<T extends ArgsDef = ArgsDef> = {
29
41
  meta?: Resolvable<CommandMeta>;
30
- args?: Resolvable<ArgsDef>;
42
+ args?: Resolvable<T>;
31
43
  subCommands?: Resolvable<SubCommandsDef>;
32
- run?: CommandRun;
44
+ setup?: (context: CommandContext<T>) => any | Promise<any>;
45
+ cleanup?: (context: CommandContext<T>) => any | Promise<any>;
46
+ run?: (context: CommandContext<T>) => any | Promise<any>;
33
47
  };
34
- type CommandRun = (context: CommandContext) => any | Promise<any>;
35
- interface CommandContext {
48
+ type CommandContext<T extends ArgsDef = ArgsDef> = {
36
49
  rawArgs: string[];
37
- args: ParsedArgs;
50
+ args: ParsedArgs<T>;
38
51
  cmd: CommandDef;
39
- }
52
+ subCommand?: CommandDef<T>;
53
+ };
40
54
  type Awaitable<T> = () => T | Promise<T>;
41
55
  type Resolvable<T> = T | Promise<T> | (() => T) | (() => Promise<T>);
42
56
 
43
- declare function defineCommand(def: CommandDef): CommandDef;
57
+ declare function defineCommand<T extends ArgsDef = ArgsDef>(def: CommandDef<T>): CommandDef<T>;
44
58
  interface RunCommandOptions {
45
59
  rawArgs: string[];
46
60
  showUsage?: boolean;
@@ -57,4 +71,4 @@ declare function parseArgs(rawArgs: string[], argsDef: ArgsDef): ParsedArgs;
57
71
  declare function showUsage(cmd: CommandDef, parent?: CommandDef): Promise<void>;
58
72
  declare function renderUsage(cmd: CommandDef, parent?: CommandDef): Promise<string>;
59
73
 
60
- export { Arg, ArgDef, ArgType, ArgsDef, Awaitable, BooleanArgDef, CommandContext, CommandDef, CommandMeta, CommandRun, ParsedArgs, PositionalArgDef, Resolvable, RunCommandOptions, RunMainOptions, StringArgDef, SubCommandsDef, _ArgDef, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage };
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 };
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import * as tty from 'tty';
1
+ import { camelCase, kebabCase } from 'scule';
2
+ import { bgRed } from 'colorette';
2
3
 
3
4
  function toArray(val) {
4
5
  if (Array.isArray(val)) {
@@ -165,23 +166,30 @@ function parseArgs(rawArgs, argsDef) {
165
166
  }
166
167
  }
167
168
  const parsed = parseRawArgs(rawArgs, parseOptions);
168
- for (const [i, arg] of args.entries()) {
169
+ const [, ...positionalArguments] = parsed._;
170
+ const parsedArgsProxy = new Proxy(parsed, {
171
+ get(target, prop) {
172
+ return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];
173
+ }
174
+ });
175
+ for (const [, arg] of args.entries()) {
169
176
  if (arg.type === "positional") {
170
- if (parsed._[i] !== void 0) {
171
- parsed[arg.name] = parsed._[i];
177
+ const nextPositionalArgument = positionalArguments.shift();
178
+ if (nextPositionalArgument !== void 0) {
179
+ parsedArgsProxy[arg.name] = nextPositionalArgument;
172
180
  } else if (arg.default !== void 0) {
173
- parsed[arg.name] = arg.default;
181
+ parsedArgsProxy[arg.name] = arg.default;
174
182
  } else {
175
183
  throw new CLIError(
176
184
  `Missing required positional argument: ${arg.name.toUpperCase()}`,
177
185
  "EARG"
178
186
  );
179
187
  }
180
- } else if (arg.required && parsed[arg.name] === void 0) {
188
+ } else if (arg.required && parsedArgsProxy[arg.name] === void 0) {
181
189
  throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
182
190
  }
183
191
  }
184
- return parsed;
192
+ return parsedArgsProxy;
185
193
  }
186
194
  function resolveArgs(argsDef) {
187
195
  const args = [];
@@ -199,17 +207,18 @@ function defineCommand(def) {
199
207
  return def;
200
208
  }
201
209
  async function runCommand(cmd, opts) {
202
- if (typeof cmd.run === "function") {
203
- const cmdArgs = await resolveValue(cmd.args || {});
204
- const parsedArgs = parseArgs(opts.rawArgs, cmdArgs);
205
- await cmd.run({
206
- rawArgs: opts.rawArgs,
207
- args: parsedArgs,
208
- cmd
209
- });
210
+ const cmdArgs = await resolveValue(cmd.args || {});
211
+ const parsedArgs = parseArgs(opts.rawArgs, cmdArgs);
212
+ const context = {
213
+ rawArgs: opts.rawArgs,
214
+ args: parsedArgs,
215
+ cmd
216
+ };
217
+ if (typeof cmd.setup === "function") {
218
+ await cmd.setup(context);
210
219
  }
211
220
  const subCommands = await resolveValue(cmd.subCommands);
212
- if (subCommands && Object.keys(subCommands.length > 0)) {
221
+ if (subCommands && Object.keys(subCommands).length > 0) {
213
222
  const subCommandArgIndex = opts.rawArgs.findIndex(
214
223
  (arg) => !arg.startsWith("-")
215
224
  );
@@ -233,10 +242,13 @@ async function runCommand(cmd, opts) {
233
242
  });
234
243
  }
235
244
  }
245
+ if (typeof cmd.run === "function") {
246
+ await cmd.run(context);
247
+ }
236
248
  }
237
249
  async function resolveSubCommand(cmd, rawArgs, parent) {
238
250
  const subCommands = await resolveValue(cmd.subCommands);
239
- if (subCommands && Object.keys(subCommands.length > 0)) {
251
+ if (subCommands && Object.keys(subCommands).length > 0) {
240
252
  const subCommandArgIndex = rawArgs.findIndex((arg) => !arg.startsWith("-"));
241
253
  const subCommandName = rawArgs[subCommandArgIndex];
242
254
  const subCommand = await resolveValue(subCommands[subCommandName]);
@@ -334,155 +346,6 @@ async function renderUsage(cmd, parent) {
334
346
  return usageLines.filter((l) => typeof l === "string").join("\n");
335
347
  }
336
348
 
337
- const {
338
- env = {},
339
- argv = [],
340
- platform = "",
341
- } = typeof process === "undefined" ? {} : process;
342
-
343
- const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
344
- const isForced = "FORCE_COLOR" in env || argv.includes("--color");
345
- const isWindows = platform === "win32";
346
- const isDumbTerminal = env.TERM === "dumb";
347
-
348
- const isCompatibleTerminal =
349
- tty && tty.isatty && tty.isatty(1) && env.TERM && !isDumbTerminal;
350
-
351
- const isCI =
352
- "CI" in env &&
353
- ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
354
-
355
- const isColorSupported =
356
- !isDisabled &&
357
- (isForced || (isWindows && !isDumbTerminal) || isCompatibleTerminal || isCI);
358
-
359
- const replaceClose = (
360
- index,
361
- string,
362
- close,
363
- replace,
364
- head = string.substring(0, index) + replace,
365
- tail = string.substring(index + close.length),
366
- next = tail.indexOf(close)
367
- ) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
368
-
369
- const clearBleed = (index, string, open, close, replace) =>
370
- index < 0
371
- ? open + string + close
372
- : open + replaceClose(index, string, close, replace) + close;
373
-
374
- const filterEmpty =
375
- (open, close, replace = open, at = open.length + 1) =>
376
- (string) =>
377
- string || !(string === "" || string === undefined)
378
- ? clearBleed(
379
- ("" + string).indexOf(close, at),
380
- string,
381
- open,
382
- close,
383
- replace
384
- )
385
- : "";
386
-
387
- const init = (open, close, replace) =>
388
- filterEmpty(`\x1b[${open}m`, `\x1b[${close}m`, replace);
389
-
390
- const colors = {
391
- reset: init(0, 0),
392
- bold: init(1, 22, "\x1b[22m\x1b[1m"),
393
- dim: init(2, 22, "\x1b[22m\x1b[2m"),
394
- italic: init(3, 23),
395
- underline: init(4, 24),
396
- inverse: init(7, 27),
397
- hidden: init(8, 28),
398
- strikethrough: init(9, 29),
399
- black: init(30, 39),
400
- red: init(31, 39),
401
- green: init(32, 39),
402
- yellow: init(33, 39),
403
- blue: init(34, 39),
404
- magenta: init(35, 39),
405
- cyan: init(36, 39),
406
- white: init(37, 39),
407
- gray: init(90, 39),
408
- bgBlack: init(40, 49),
409
- bgRed: init(41, 49),
410
- bgGreen: init(42, 49),
411
- bgYellow: init(43, 49),
412
- bgBlue: init(44, 49),
413
- bgMagenta: init(45, 49),
414
- bgCyan: init(46, 49),
415
- bgWhite: init(47, 49),
416
- blackBright: init(90, 39),
417
- redBright: init(91, 39),
418
- greenBright: init(92, 39),
419
- yellowBright: init(93, 39),
420
- blueBright: init(94, 39),
421
- magentaBright: init(95, 39),
422
- cyanBright: init(96, 39),
423
- whiteBright: init(97, 39),
424
- bgBlackBright: init(100, 49),
425
- bgRedBright: init(101, 49),
426
- bgGreenBright: init(102, 49),
427
- bgYellowBright: init(103, 49),
428
- bgBlueBright: init(104, 49),
429
- bgMagentaBright: init(105, 49),
430
- bgCyanBright: init(106, 49),
431
- bgWhiteBright: init(107, 49),
432
- };
433
-
434
- const createColors = ({ useColor = isColorSupported } = {}) =>
435
- useColor
436
- ? colors
437
- : Object.keys(colors).reduce(
438
- (colors, key) => ({ ...colors, [key]: String }),
439
- {}
440
- );
441
-
442
- const {
443
- reset,
444
- bold,
445
- dim,
446
- italic,
447
- underline,
448
- inverse,
449
- hidden,
450
- strikethrough,
451
- black,
452
- red,
453
- green,
454
- yellow,
455
- blue,
456
- magenta,
457
- cyan,
458
- white,
459
- gray,
460
- bgBlack,
461
- bgRed,
462
- bgGreen,
463
- bgYellow,
464
- bgBlue,
465
- bgMagenta,
466
- bgCyan,
467
- bgWhite,
468
- blackBright,
469
- redBright,
470
- greenBright,
471
- yellowBright,
472
- blueBright,
473
- magentaBright,
474
- cyanBright,
475
- whiteBright,
476
- bgBlackBright,
477
- bgRedBright,
478
- bgGreenBright,
479
- bgYellowBright,
480
- bgBlueBright,
481
- bgMagentaBright,
482
- bgCyanBright,
483
- bgWhiteBright,
484
- } = createColors();
485
-
486
349
  async function runMain(cmd, opts = {}) {
487
350
  const rawArgs = opts.rawArgs || process.argv.slice(2);
488
351
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "citty",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Elegant CLI Builder",
5
5
  "repository": "unjs/citty",
6
6
  "license": "MIT",
@@ -29,18 +29,19 @@
29
29
  "release": "pnpm test && changelogen --release --push && npm publish",
30
30
  "test": "pnpm lint && vitest run --coverage"
31
31
  },
32
- "devDependencies": {
33
- "@types/node": "^18.15.3",
34
- "@vitest/coverage-c8": "^0.29.2",
35
- "changelogen": "^0.5.1",
32
+ "dependencies": {
33
+ "@types/node": "^18.15.11",
34
+ "@vitest/coverage-c8": "^0.29.8",
35
+ "changelogen": "^0.5.2",
36
36
  "colorette": "^2.0.19",
37
- "eslint": "^8.36.0",
37
+ "eslint": "^8.37.0",
38
38
  "eslint-config-unjs": "^0.1.0",
39
- "jiti": "^1.17.2",
40
- "prettier": "^2.8.4",
41
- "typescript": "^4.9.5",
39
+ "jiti": "^1.18.2",
40
+ "prettier": "^2.8.7",
41
+ "scule": "^1.0.0",
42
+ "typescript": "^5.0.3",
42
43
  "unbuild": "^1.1.2",
43
- "vitest": "^0.29.2"
44
+ "vitest": "^0.29.8"
44
45
  },
45
- "packageManager": "pnpm@7.25.1"
46
- }
46
+ "packageManager": "pnpm@8.1.0"
47
+ }