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 +0 -11
- package/README.md +1 -1
- package/dist/index.cjs +106 -37
- package/dist/index.d.ts +10 -8
- package/dist/index.mjs +101 -36
- package/package.json +15 -13
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
package/dist/index.cjs
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
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
|
|
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
|
-
|
|
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 [
|
|
244
|
+
const [...positionalArguments] = parsed._;
|
|
172
245
|
const parsedArgsProxy = new Proxy(parsed, {
|
|
173
246
|
get(target, prop) {
|
|
174
|
-
return target[prop] ?? target[
|
|
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
|
|
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
|
|
237
|
-
"
|
|
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
|
-
|
|
341
|
+
consola__default.log(await renderUsage(cmd, parent) + "\n");
|
|
271
342
|
} catch (error) {
|
|
272
|
-
|
|
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
|
-
|
|
311
|
-
|
|
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
|
-
|
|
320
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
72
|
-
declare function renderUsage(cmd: CommandDef
|
|
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
|
|
2
|
-
import {
|
|
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
|
|
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
|
-
|
|
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 [
|
|
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
|
|
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
|
|
235
|
-
"
|
|
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
|
-
|
|
335
|
+
consola.log(await renderUsage(cmd, parent) + "\n");
|
|
269
336
|
} catch (error) {
|
|
270
|
-
|
|
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
|
-
|
|
309
|
-
|
|
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
|
-
|
|
318
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
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.
|
|
43
|
-
"unbuild": "^1.1
|
|
44
|
-
"vitest": "^0.
|
|
44
|
+
"typescript": "^5.1.6",
|
|
45
|
+
"unbuild": "^1.2.1",
|
|
46
|
+
"vitest": "^0.32.4"
|
|
45
47
|
},
|
|
46
|
-
"packageManager": "pnpm@8.
|
|
48
|
+
"packageManager": "pnpm@8.6.6"
|
|
47
49
|
}
|