citty 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -65,7 +65,7 @@ const main = defineCommand({
65
65
  },
66
66
  },
67
67
  run({ args }) {
68
- console.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`);
68
+ consol.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`);
69
69
  },
70
70
  });
71
71
 
package/dist/index.cjs CHANGED
@@ -1,13 +1,17 @@
1
1
  'use strict';
2
2
 
3
- const scule = require('scule');
4
- const colorette = require('colorette');
3
+ const consola = require('consola');
4
+ const utils = require('consola/utils');
5
+
6
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
7
+
8
+ const consola__default = /*#__PURE__*/_interopDefaultCompat(consola);
5
9
 
6
10
  function toArray(val) {
7
11
  if (Array.isArray(val)) {
8
12
  return val;
9
13
  }
10
- return val !== void 0 ? [val] : [];
14
+ return val === void 0 ? [] : [val];
11
15
  }
12
16
  function formatLineColumns(lines, linePrefix = "") {
13
17
  const maxLengh = [];
@@ -33,6 +37,70 @@ class CLIError extends Error {
33
37
  }
34
38
  }
35
39
 
40
+ const NUMBER_CHAR_RE = /\d/;
41
+ const STR_SPLITTERS = ["-", "_", "/", "."];
42
+ function isUppercase(char = "") {
43
+ if (NUMBER_CHAR_RE.test(char)) {
44
+ return void 0;
45
+ }
46
+ return char.toUpperCase() === char;
47
+ }
48
+ function splitByCase(string_, separators) {
49
+ const splitters = separators ?? STR_SPLITTERS;
50
+ const parts = [];
51
+ if (!string_ || typeof string_ !== "string") {
52
+ return parts;
53
+ }
54
+ let buff = "";
55
+ let previousUpper;
56
+ let previousSplitter;
57
+ for (const char of string_) {
58
+ const isSplitter = splitters.includes(char);
59
+ if (isSplitter === true) {
60
+ parts.push(buff);
61
+ buff = "";
62
+ previousUpper = void 0;
63
+ continue;
64
+ }
65
+ const isUpper = isUppercase(char);
66
+ if (previousSplitter === false) {
67
+ if (previousUpper === false && isUpper === true) {
68
+ parts.push(buff);
69
+ buff = char;
70
+ previousUpper = isUpper;
71
+ continue;
72
+ }
73
+ if (previousUpper === true && isUpper === false && buff.length > 1) {
74
+ const lastChar = buff[buff.length - 1];
75
+ parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
76
+ buff = lastChar + char;
77
+ previousUpper = isUpper;
78
+ continue;
79
+ }
80
+ }
81
+ buff += char;
82
+ previousUpper = isUpper;
83
+ previousSplitter = isSplitter;
84
+ }
85
+ parts.push(buff);
86
+ return parts;
87
+ }
88
+ function upperFirst(string_) {
89
+ return !string_ ? "" : string_[0].toUpperCase() + string_.slice(1);
90
+ }
91
+ function lowerFirst(string_) {
92
+ return !string_ ? "" : string_[0].toLowerCase() + string_.slice(1);
93
+ }
94
+ function pascalCase(string_) {
95
+ return !string_ ? "" : (Array.isArray(string_) ? string_ : splitByCase(string_)).map((p) => upperFirst(p)).join("");
96
+ }
97
+ function camelCase(string_) {
98
+ return lowerFirst(pascalCase(string_));
99
+ }
100
+ function kebabCase(string_, joiner) {
101
+ return !string_ ? "" : (Array.isArray(string_) ? string_ : splitByCase(string_)).map((p) => p.toLowerCase()).join(joiner ?? "-");
102
+ }
103
+
36
104
  function toArr(any) {
37
105
  return any == void 0 ? [] : Array.isArray(any) ? any : [any];
38
106
  }
@@ -151,6 +219,7 @@ function parseArgs(rawArgs, argsDef) {
151
219
  const parseOptions = {
152
220
  boolean: [],
153
221
  string: [],
222
+ mixed: [],
154
223
  alias: {},
155
224
  default: {}
156
225
  };
@@ -159,7 +228,11 @@ function parseArgs(rawArgs, argsDef) {
159
228
  if (arg.type === "positional") {
160
229
  continue;
161
230
  }
162
- parseOptions[arg.type || "boolean"].push(arg.name);
231
+ if (arg.type === "string") {
232
+ parseOptions.string.push(arg.name);
233
+ } else if (arg.type === "boolean") {
234
+ parseOptions.boolean.push(arg.name);
235
+ }
163
236
  if (arg.default !== void 0) {
164
237
  parseOptions.default[arg.name] = arg.default;
165
238
  }
@@ -168,10 +241,10 @@ function parseArgs(rawArgs, argsDef) {
168
241
  }
169
242
  }
170
243
  const parsed = parseRawArgs(rawArgs, parseOptions);
171
- const [, ...positionalArguments] = parsed._;
244
+ const [...positionalArguments] = parsed._;
172
245
  const parsedArgsProxy = new Proxy(parsed, {
173
246
  get(target, prop) {
174
- return target[prop] ?? target[scule.camelCase(prop)] ?? target[scule.kebabCase(prop)];
247
+ return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];
175
248
  }
176
249
  });
177
250
  for (const [, arg] of args.entries()) {
@@ -179,13 +252,13 @@ function parseArgs(rawArgs, argsDef) {
179
252
  const nextPositionalArgument = positionalArguments.shift();
180
253
  if (nextPositionalArgument !== void 0) {
181
254
  parsedArgsProxy[arg.name] = nextPositionalArgument;
182
- } else if (arg.default !== void 0) {
183
- parsedArgsProxy[arg.name] = arg.default;
184
- } else {
255
+ } else if (arg.default === void 0 && arg.required !== false) {
185
256
  throw new CLIError(
186
257
  `Missing required positional argument: ${arg.name.toUpperCase()}`,
187
258
  "EARG"
188
259
  );
260
+ } else {
261
+ parsedArgsProxy[arg.name] = arg.default;
189
262
  }
190
263
  } else if (arg.required && parsedArgsProxy[arg.name] === void 0) {
191
264
  throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
@@ -214,6 +287,7 @@ async function runCommand(cmd, opts) {
214
287
  const context = {
215
288
  rawArgs: opts.rawArgs,
216
289
  args: parsedArgs,
290
+ data: opts.data,
217
291
  cmd
218
292
  };
219
293
  if (typeof cmd.setup === "function") {
@@ -226,21 +300,18 @@ async function runCommand(cmd, opts) {
226
300
  );
227
301
  const subCommandName = opts.rawArgs[subCommandArgIndex];
228
302
  if (!subCommandName && !cmd.run) {
229
- throw new CLIError(
230
- `Missing sub command. Use --help to see available sub commands.`,
231
- "ESUBCOMMAND"
232
- );
303
+ throw new CLIError(`No command specified.`, "E_NO_COMMAND");
233
304
  }
234
305
  if (!subCommands[subCommandName]) {
235
306
  throw new CLIError(
236
- `Unknown sub command: ${subCommandName}`,
237
- "ESUBCOMMAND"
307
+ `Unknown command \`${subCommandName}\``,
308
+ "E_UNKNOWN_COMMAND"
238
309
  );
239
310
  }
240
311
  const subCommand = await resolveValue(subCommands[subCommandName]);
241
312
  if (subCommand) {
242
313
  await runCommand(subCommand, {
243
- rawArgs: opts.rawArgs.slice(subCommandArgIndex)
314
+ rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1)
244
315
  });
245
316
  }
246
317
  }
@@ -257,7 +328,7 @@ async function resolveSubCommand(cmd, rawArgs, parent) {
257
328
  if (subCommand) {
258
329
  return resolveSubCommand(
259
330
  subCommand,
260
- rawArgs.slice(subCommandArgIndex),
331
+ rawArgs.slice(subCommandArgIndex + 1),
261
332
  cmd
262
333
  );
263
334
  }
@@ -267,9 +338,9 @@ async function resolveSubCommand(cmd, rawArgs, parent) {
267
338
 
268
339
  async function showUsage(cmd, parent) {
269
340
  try {
270
- console.log(await renderUsage(cmd, parent) + "\n");
341
+ consola__default.log(await renderUsage(cmd, parent) + "\n");
271
342
  } catch (error) {
272
- console.error(error);
343
+ consola__default.error(error);
273
344
  }
274
345
  }
275
346
  async function renderUsage(cmd, parent) {
@@ -286,7 +357,7 @@ async function renderUsage(cmd, parent) {
286
357
  const name = arg.name.toUpperCase();
287
358
  const isRequired = arg.required !== false && arg.default === void 0;
288
359
  const usageHint = arg.default ? `="${arg.default}"` : "";
289
- posLines.push([name + usageHint, arg.description || ""]);
360
+ posLines.push(["`" + name + usageHint + "`", arg.description || ""]);
290
361
  usageLine.push(isRequired ? `<${name}>` : `[${name}]`);
291
362
  } else {
292
363
  const isRequired = arg.required === true && arg.default === void 0;
@@ -297,7 +368,7 @@ async function renderUsage(cmd, parent) {
297
368
  ", "
298
369
  )) + (arg.type === "string" && (arg.valueHint || arg.default) ? `=${arg.valueHint ? `<${arg.valueHint}>` : `"${arg.default || ""}"`}` : "");
299
370
  argLines.push([
300
- argStr + (isRequired ? " (required)" : ""),
371
+ "`" + argStr + (isRequired ? " (required)" : "") + "`",
301
372
  arg.description || ""
302
373
  ]);
303
374
  if (isRequired) {
@@ -307,8 +378,11 @@ async function renderUsage(cmd, parent) {
307
378
  }
308
379
  if (cmd.subCommands) {
309
380
  const commandNames = [];
310
- for (const [name, sub] of Object.entries(cmd.subCommands)) {
311
- 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 || ""]);
312
386
  commandNames.push(name);
313
387
  }
314
388
  usageLine.push(commandNames.join("|"));
@@ -316,29 +390,28 @@ async function renderUsage(cmd, parent) {
316
390
  const usageLines = [];
317
391
  const version = cmdMeta.version || parentMeta.version;
318
392
  usageLines.push(
319
- commandName + (version ? ` v${version}` : ""),
320
- cmdMeta.description,
393
+ utils.colors.gray(
394
+ `${cmdMeta.description} (${commandName + (version ? ` v${version}` : "")})`
395
+ ),
321
396
  ""
322
397
  );
323
398
  const hasOptions = argLines.length > 0 || posLines.length > 0;
324
399
  usageLines.push(
325
- `USAGE: ${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(
326
- " "
327
- )}`,
400
+ `${utils.colors.underline(utils.colors.bold("USAGE"))} \`${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(" ")}\``,
328
401
  ""
329
402
  );
330
403
  if (posLines.length > 0) {
331
- usageLines.push("ARGUMENTS:", "");
404
+ usageLines.push(utils.colors.underline(utils.colors.bold("ARGUMENTS")), "");
332
405
  usageLines.push(formatLineColumns(posLines, " "));
333
406
  usageLines.push("");
334
407
  }
335
408
  if (argLines.length > 0) {
336
- usageLines.push("OPTIONS:", "");
409
+ usageLines.push(utils.colors.underline(utils.colors.bold("OPTIONS")), "");
337
410
  usageLines.push(formatLineColumns(argLines, " "));
338
411
  usageLines.push("");
339
412
  }
340
413
  if (commandsLines.length > 0) {
341
- usageLines.push("COMMANDS:", "");
414
+ usageLines.push(utils.colors.underline(utils.colors.bold("COMMANDS")), "");
342
415
  usageLines.push(formatLineColumns(commandsLines, " "));
343
416
  usageLines.push(
344
417
  "",
@@ -360,16 +433,12 @@ async function runMain(cmd, opts = {}) {
360
433
  } catch (error) {
361
434
  const isCLIError = error instanceof CLIError;
362
435
  if (!isCLIError) {
363
- console.error(error, "\n");
436
+ consola__default.error(error, "\n");
364
437
  }
365
- console.error(
366
- `
367
- ${colorette.bgRed(` ${error.code || error.name} `)} ${error.message}
368
- `
369
- );
370
438
  if (isCLIError) {
371
439
  await showUsage(...await resolveSubCommand(cmd, rawArgs));
372
440
  }
441
+ consola__default.error(error.message);
373
442
  process.exit(1);
374
443
  }
375
444
  }
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;
@@ -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
76
  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,11 +1,11 @@
1
- import { camelCase, kebabCase } from 'scule';
2
- import { bgRed } from 'colorette';
1
+ import consola from 'consola';
2
+ import { colors } from 'consola/utils';
3
3
 
4
4
  function toArray(val) {
5
5
  if (Array.isArray(val)) {
6
6
  return val;
7
7
  }
8
- return val !== void 0 ? [val] : [];
8
+ return val === void 0 ? [] : [val];
9
9
  }
10
10
  function formatLineColumns(lines, linePrefix = "") {
11
11
  const maxLengh = [];
@@ -31,6 +31,70 @@ class CLIError extends Error {
31
31
  }
32
32
  }
33
33
 
34
+ const NUMBER_CHAR_RE = /\d/;
35
+ const STR_SPLITTERS = ["-", "_", "/", "."];
36
+ function isUppercase(char = "") {
37
+ if (NUMBER_CHAR_RE.test(char)) {
38
+ return void 0;
39
+ }
40
+ return char.toUpperCase() === char;
41
+ }
42
+ function splitByCase(string_, separators) {
43
+ const splitters = separators ?? STR_SPLITTERS;
44
+ const parts = [];
45
+ if (!string_ || typeof string_ !== "string") {
46
+ return parts;
47
+ }
48
+ let buff = "";
49
+ let previousUpper;
50
+ let previousSplitter;
51
+ for (const char of string_) {
52
+ const isSplitter = splitters.includes(char);
53
+ if (isSplitter === true) {
54
+ parts.push(buff);
55
+ buff = "";
56
+ previousUpper = void 0;
57
+ continue;
58
+ }
59
+ const isUpper = isUppercase(char);
60
+ if (previousSplitter === false) {
61
+ if (previousUpper === false && isUpper === true) {
62
+ parts.push(buff);
63
+ buff = char;
64
+ previousUpper = isUpper;
65
+ continue;
66
+ }
67
+ if (previousUpper === true && isUpper === false && buff.length > 1) {
68
+ const lastChar = buff[buff.length - 1];
69
+ parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
70
+ buff = lastChar + char;
71
+ previousUpper = isUpper;
72
+ continue;
73
+ }
74
+ }
75
+ buff += char;
76
+ previousUpper = isUpper;
77
+ previousSplitter = isSplitter;
78
+ }
79
+ parts.push(buff);
80
+ return parts;
81
+ }
82
+ function upperFirst(string_) {
83
+ return !string_ ? "" : string_[0].toUpperCase() + string_.slice(1);
84
+ }
85
+ function lowerFirst(string_) {
86
+ return !string_ ? "" : string_[0].toLowerCase() + string_.slice(1);
87
+ }
88
+ function pascalCase(string_) {
89
+ return !string_ ? "" : (Array.isArray(string_) ? string_ : splitByCase(string_)).map((p) => upperFirst(p)).join("");
90
+ }
91
+ function camelCase(string_) {
92
+ return lowerFirst(pascalCase(string_));
93
+ }
94
+ function kebabCase(string_, joiner) {
95
+ return !string_ ? "" : (Array.isArray(string_) ? string_ : splitByCase(string_)).map((p) => p.toLowerCase()).join(joiner ?? "-");
96
+ }
97
+
34
98
  function toArr(any) {
35
99
  return any == void 0 ? [] : Array.isArray(any) ? any : [any];
36
100
  }
@@ -149,6 +213,7 @@ function parseArgs(rawArgs, argsDef) {
149
213
  const parseOptions = {
150
214
  boolean: [],
151
215
  string: [],
216
+ mixed: [],
152
217
  alias: {},
153
218
  default: {}
154
219
  };
@@ -157,7 +222,11 @@ function parseArgs(rawArgs, argsDef) {
157
222
  if (arg.type === "positional") {
158
223
  continue;
159
224
  }
160
- parseOptions[arg.type || "boolean"].push(arg.name);
225
+ if (arg.type === "string") {
226
+ parseOptions.string.push(arg.name);
227
+ } else if (arg.type === "boolean") {
228
+ parseOptions.boolean.push(arg.name);
229
+ }
161
230
  if (arg.default !== void 0) {
162
231
  parseOptions.default[arg.name] = arg.default;
163
232
  }
@@ -166,7 +235,7 @@ function parseArgs(rawArgs, argsDef) {
166
235
  }
167
236
  }
168
237
  const parsed = parseRawArgs(rawArgs, parseOptions);
169
- const [, ...positionalArguments] = parsed._;
238
+ const [...positionalArguments] = parsed._;
170
239
  const parsedArgsProxy = new Proxy(parsed, {
171
240
  get(target, prop) {
172
241
  return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];
@@ -177,13 +246,13 @@ function parseArgs(rawArgs, argsDef) {
177
246
  const nextPositionalArgument = positionalArguments.shift();
178
247
  if (nextPositionalArgument !== void 0) {
179
248
  parsedArgsProxy[arg.name] = nextPositionalArgument;
180
- } else if (arg.default !== void 0) {
181
- parsedArgsProxy[arg.name] = arg.default;
182
- } else {
249
+ } else if (arg.default === void 0 && arg.required !== false) {
183
250
  throw new CLIError(
184
251
  `Missing required positional argument: ${arg.name.toUpperCase()}`,
185
252
  "EARG"
186
253
  );
254
+ } else {
255
+ parsedArgsProxy[arg.name] = arg.default;
187
256
  }
188
257
  } else if (arg.required && parsedArgsProxy[arg.name] === void 0) {
189
258
  throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
@@ -212,6 +281,7 @@ async function runCommand(cmd, opts) {
212
281
  const context = {
213
282
  rawArgs: opts.rawArgs,
214
283
  args: parsedArgs,
284
+ data: opts.data,
215
285
  cmd
216
286
  };
217
287
  if (typeof cmd.setup === "function") {
@@ -224,21 +294,18 @@ async function runCommand(cmd, opts) {
224
294
  );
225
295
  const subCommandName = opts.rawArgs[subCommandArgIndex];
226
296
  if (!subCommandName && !cmd.run) {
227
- throw new CLIError(
228
- `Missing sub command. Use --help to see available sub commands.`,
229
- "ESUBCOMMAND"
230
- );
297
+ throw new CLIError(`No command specified.`, "E_NO_COMMAND");
231
298
  }
232
299
  if (!subCommands[subCommandName]) {
233
300
  throw new CLIError(
234
- `Unknown sub command: ${subCommandName}`,
235
- "ESUBCOMMAND"
301
+ `Unknown command \`${subCommandName}\``,
302
+ "E_UNKNOWN_COMMAND"
236
303
  );
237
304
  }
238
305
  const subCommand = await resolveValue(subCommands[subCommandName]);
239
306
  if (subCommand) {
240
307
  await runCommand(subCommand, {
241
- rawArgs: opts.rawArgs.slice(subCommandArgIndex)
308
+ rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1)
242
309
  });
243
310
  }
244
311
  }
@@ -255,7 +322,7 @@ async function resolveSubCommand(cmd, rawArgs, parent) {
255
322
  if (subCommand) {
256
323
  return resolveSubCommand(
257
324
  subCommand,
258
- rawArgs.slice(subCommandArgIndex),
325
+ rawArgs.slice(subCommandArgIndex + 1),
259
326
  cmd
260
327
  );
261
328
  }
@@ -265,9 +332,9 @@ async function resolveSubCommand(cmd, rawArgs, parent) {
265
332
 
266
333
  async function showUsage(cmd, parent) {
267
334
  try {
268
- console.log(await renderUsage(cmd, parent) + "\n");
335
+ consola.log(await renderUsage(cmd, parent) + "\n");
269
336
  } catch (error) {
270
- console.error(error);
337
+ consola.error(error);
271
338
  }
272
339
  }
273
340
  async function renderUsage(cmd, parent) {
@@ -284,7 +351,7 @@ async function renderUsage(cmd, parent) {
284
351
  const name = arg.name.toUpperCase();
285
352
  const isRequired = arg.required !== false && arg.default === void 0;
286
353
  const usageHint = arg.default ? `="${arg.default}"` : "";
287
- posLines.push([name + usageHint, arg.description || ""]);
354
+ posLines.push(["`" + name + usageHint + "`", arg.description || ""]);
288
355
  usageLine.push(isRequired ? `<${name}>` : `[${name}]`);
289
356
  } else {
290
357
  const isRequired = arg.required === true && arg.default === void 0;
@@ -295,7 +362,7 @@ async function renderUsage(cmd, parent) {
295
362
  ", "
296
363
  )) + (arg.type === "string" && (arg.valueHint || arg.default) ? `=${arg.valueHint ? `<${arg.valueHint}>` : `"${arg.default || ""}"`}` : "");
297
364
  argLines.push([
298
- argStr + (isRequired ? " (required)" : ""),
365
+ "`" + argStr + (isRequired ? " (required)" : "") + "`",
299
366
  arg.description || ""
300
367
  ]);
301
368
  if (isRequired) {
@@ -305,8 +372,11 @@ async function renderUsage(cmd, parent) {
305
372
  }
306
373
  if (cmd.subCommands) {
307
374
  const commandNames = [];
308
- for (const [name, sub] of Object.entries(cmd.subCommands)) {
309
- 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 || ""]);
310
380
  commandNames.push(name);
311
381
  }
312
382
  usageLine.push(commandNames.join("|"));
@@ -314,29 +384,28 @@ async function renderUsage(cmd, parent) {
314
384
  const usageLines = [];
315
385
  const version = cmdMeta.version || parentMeta.version;
316
386
  usageLines.push(
317
- commandName + (version ? ` v${version}` : ""),
318
- cmdMeta.description,
387
+ colors.gray(
388
+ `${cmdMeta.description} (${commandName + (version ? ` v${version}` : "")})`
389
+ ),
319
390
  ""
320
391
  );
321
392
  const hasOptions = argLines.length > 0 || posLines.length > 0;
322
393
  usageLines.push(
323
- `USAGE: ${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(
324
- " "
325
- )}`,
394
+ `${colors.underline(colors.bold("USAGE"))} \`${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(" ")}\``,
326
395
  ""
327
396
  );
328
397
  if (posLines.length > 0) {
329
- usageLines.push("ARGUMENTS:", "");
398
+ usageLines.push(colors.underline(colors.bold("ARGUMENTS")), "");
330
399
  usageLines.push(formatLineColumns(posLines, " "));
331
400
  usageLines.push("");
332
401
  }
333
402
  if (argLines.length > 0) {
334
- usageLines.push("OPTIONS:", "");
403
+ usageLines.push(colors.underline(colors.bold("OPTIONS")), "");
335
404
  usageLines.push(formatLineColumns(argLines, " "));
336
405
  usageLines.push("");
337
406
  }
338
407
  if (commandsLines.length > 0) {
339
- usageLines.push("COMMANDS:", "");
408
+ usageLines.push(colors.underline(colors.bold("COMMANDS")), "");
340
409
  usageLines.push(formatLineColumns(commandsLines, " "));
341
410
  usageLines.push(
342
411
  "",
@@ -358,16 +427,12 @@ async function runMain(cmd, opts = {}) {
358
427
  } catch (error) {
359
428
  const isCLIError = error instanceof CLIError;
360
429
  if (!isCLIError) {
361
- console.error(error, "\n");
430
+ consola.error(error, "\n");
362
431
  }
363
- console.error(
364
- `
365
- ${bgRed(` ${error.code || error.name} `)} ${error.message}
366
- `
367
- );
368
432
  if (isCLIError) {
369
433
  await showUsage(...await resolveSubCommand(cmd, rawArgs));
370
434
  }
435
+ consola.error(error.message);
371
436
  process.exit(1);
372
437
  }
373
438
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "citty",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Elegant CLI Builder",
5
5
  "repository": "unjs/citty",
6
6
  "license": "MIT",
@@ -30,18 +30,20 @@
30
30
  "test": "pnpm lint && vitest run --coverage"
31
31
  },
32
32
  "dependencies": {
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",
33
+ "consola": "^3.2.3"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.4.0",
37
+ "@vitest/coverage-v8": "^0.32.4",
38
+ "changelogen": "^0.5.4",
39
+ "eslint": "^8.44.0",
40
+ "eslint-config-unjs": "^0.2.1",
41
+ "jiti": "^1.19.1",
42
+ "prettier": "^3.0.0",
41
43
  "scule": "^1.0.0",
42
- "typescript": "^5.0.3",
43
- "unbuild": "^1.1.2",
44
- "vitest": "^0.29.8"
44
+ "typescript": "^5.1.6",
45
+ "unbuild": "^1.2.1",
46
+ "vitest": "^0.32.4"
45
47
  },
46
- "packageManager": "pnpm@8.1.0"
48
+ "packageManager": "pnpm@8.6.6"
47
49
  }