@guanghechen/commander 4.7.2 → 4.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/lib/cjs/browser.cjs +125 -17
- package/lib/cjs/index.cjs +176 -1028
- package/lib/cjs/node.cjs +177 -39
- package/lib/esm/browser.mjs +125 -17
- package/lib/esm/index.mjs +175 -1021
- package/lib/esm/node.mjs +177 -39
- package/lib/types/browser.d.ts +2 -1
- package/lib/types/index.d.ts +15 -191
- package/lib/types/node.d.ts +2 -1
- package/package.json +1 -1
package/lib/esm/node.mjs
CHANGED
|
@@ -305,6 +305,30 @@ function parsePrimitiveNumber(rawValue) {
|
|
|
305
305
|
}
|
|
306
306
|
return value;
|
|
307
307
|
}
|
|
308
|
+
function normalizeSubcommandNameForDistance(name) {
|
|
309
|
+
return camelToKebabCase$1(name).toLowerCase();
|
|
310
|
+
}
|
|
311
|
+
function levenshteinDistance(left, right) {
|
|
312
|
+
if (left === right) {
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
if (left.length === 0) {
|
|
316
|
+
return right.length;
|
|
317
|
+
}
|
|
318
|
+
if (right.length === 0) {
|
|
319
|
+
return left.length;
|
|
320
|
+
}
|
|
321
|
+
let prev = Array.from({ length: right.length + 1 }, (_, i) => i);
|
|
322
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
323
|
+
const current = [i + 1];
|
|
324
|
+
for (let j = 0; j < right.length; j += 1) {
|
|
325
|
+
const substitutionCost = left[i] === right[j] ? 0 : 1;
|
|
326
|
+
current[j + 1] = Math.min(current[j] + 1, prev[j + 1] + 1, prev[j] + substitutionCost);
|
|
327
|
+
}
|
|
328
|
+
prev = current;
|
|
329
|
+
}
|
|
330
|
+
return prev[right.length];
|
|
331
|
+
}
|
|
308
332
|
function tokenizeLongOption(arg, commandPath) {
|
|
309
333
|
const eqIdx = arg.indexOf('=');
|
|
310
334
|
const namePart = eqIdx !== -1 ? arg.slice(0, eqIdx) : arg;
|
|
@@ -707,7 +731,10 @@ class Command {
|
|
|
707
731
|
const kebabLong = camelToKebabCase$1(opt.long);
|
|
708
732
|
let sig = opt.short ? `-${opt.short}, ` : ' ';
|
|
709
733
|
sig += `--${kebabLong}`;
|
|
710
|
-
if (opt.args
|
|
734
|
+
if (opt.args === 'optional') {
|
|
735
|
+
sig += ' [value]';
|
|
736
|
+
}
|
|
737
|
+
else if (opt.args !== 'none') {
|
|
711
738
|
sig += ' <value>';
|
|
712
739
|
}
|
|
713
740
|
let desc = opt.desc;
|
|
@@ -1326,6 +1353,14 @@ class Command {
|
|
|
1326
1353
|
consumed.push(tokens[i]);
|
|
1327
1354
|
}
|
|
1328
1355
|
}
|
|
1356
|
+
else if (opt.args === 'optional') {
|
|
1357
|
+
if (!token.resolved.includes('=') &&
|
|
1358
|
+
i + 1 < tokens.length &&
|
|
1359
|
+
tokens[i + 1].type === 'none') {
|
|
1360
|
+
i += 1;
|
|
1361
|
+
consumed.push(tokens[i]);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1329
1364
|
else if (opt.args === 'variadic') {
|
|
1330
1365
|
if (!token.resolved.includes('=')) {
|
|
1331
1366
|
while (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
|
|
@@ -1351,6 +1386,12 @@ class Command {
|
|
|
1351
1386
|
consumed.push(tokens[i]);
|
|
1352
1387
|
}
|
|
1353
1388
|
}
|
|
1389
|
+
else if (opt.args === 'optional') {
|
|
1390
|
+
if (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
|
|
1391
|
+
i += 1;
|
|
1392
|
+
consumed.push(tokens[i]);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1354
1395
|
else if (opt.args === 'variadic') {
|
|
1355
1396
|
while (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
|
|
1356
1397
|
i += 1;
|
|
@@ -1392,6 +1433,7 @@ class Command {
|
|
|
1392
1433
|
leafLocalOpts[opt.long] = leafParsedOpts[opt.long];
|
|
1393
1434
|
}
|
|
1394
1435
|
}
|
|
1436
|
+
leafCommand.#assertUnknownSubcommand(ctx.sources.user.argv);
|
|
1395
1437
|
const rawArgStrings = [...argTokens.map(t => t.original), ...restArgs];
|
|
1396
1438
|
const { args, rawArgs } = leafCommand.#parseArguments(rawArgStrings);
|
|
1397
1439
|
const parseCtx = {
|
|
@@ -1474,6 +1516,23 @@ class Command {
|
|
|
1474
1516
|
i += 1;
|
|
1475
1517
|
continue;
|
|
1476
1518
|
}
|
|
1519
|
+
if (opt.args === 'optional') {
|
|
1520
|
+
const eqIdx = token.resolved.indexOf('=');
|
|
1521
|
+
if (eqIdx !== -1) {
|
|
1522
|
+
opts[opt.long] = this.#convertValue(opt, token.resolved.slice(eqIdx + 1));
|
|
1523
|
+
i += 1;
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
if (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
|
|
1527
|
+
opts[opt.long] = this.#convertValue(opt, tokens[i + 1].original);
|
|
1528
|
+
i += 1;
|
|
1529
|
+
}
|
|
1530
|
+
else {
|
|
1531
|
+
opts[opt.long] = undefined;
|
|
1532
|
+
}
|
|
1533
|
+
i += 1;
|
|
1534
|
+
continue;
|
|
1535
|
+
}
|
|
1477
1536
|
if (opt.args === 'variadic') {
|
|
1478
1537
|
const values = Array.isArray(opts[opt.long]) ? opts[opt.long] : [];
|
|
1479
1538
|
const eqIdx = token.resolved.indexOf('=');
|
|
@@ -1493,7 +1552,7 @@ class Command {
|
|
|
1493
1552
|
i += 1;
|
|
1494
1553
|
}
|
|
1495
1554
|
for (const opt of allOptions) {
|
|
1496
|
-
if (opt.required && opts
|
|
1555
|
+
if (opt.required && !Object.prototype.hasOwnProperty.call(opts, opt.long)) {
|
|
1497
1556
|
throw new CommanderError('MissingRequired', `missing required option "--${camelToKebabCase$1(opt.long)}"`, this.#getCommandPath());
|
|
1498
1557
|
}
|
|
1499
1558
|
}
|
|
@@ -1530,6 +1589,9 @@ class Command {
|
|
|
1530
1589
|
#parseArguments(rawArgs) {
|
|
1531
1590
|
const argumentDefs = this.#arguments;
|
|
1532
1591
|
const args = {};
|
|
1592
|
+
if (argumentDefs.length === 0 && rawArgs.length > 0) {
|
|
1593
|
+
throw new CommanderError('UnexpectedArgument', `unexpected argument "${rawArgs[0]}"`, this.#getCommandPath());
|
|
1594
|
+
}
|
|
1533
1595
|
const missing = [];
|
|
1534
1596
|
let remaining = rawArgs.length;
|
|
1535
1597
|
for (const def of argumentDefs) {
|
|
@@ -1570,25 +1632,23 @@ class Command {
|
|
|
1570
1632
|
}
|
|
1571
1633
|
if (def.kind === 'some') {
|
|
1572
1634
|
const rest = rawArgs.slice(index);
|
|
1573
|
-
if (rest.length === 0) {
|
|
1574
|
-
throw new CommanderError('MissingRequiredArgument', `missing required argument(s): ${def.name}`, this.#getCommandPath());
|
|
1575
|
-
}
|
|
1576
1635
|
args[def.name] = rest.map(raw => this.#convertArgument(def, raw));
|
|
1577
1636
|
index = rawArgs.length;
|
|
1578
1637
|
break;
|
|
1579
1638
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
if (
|
|
1639
|
+
if (def.kind === 'optional') {
|
|
1640
|
+
const raw = rawArgs[index];
|
|
1641
|
+
if (raw === undefined) {
|
|
1583
1642
|
args[def.name] = def.default ?? undefined;
|
|
1584
1643
|
continue;
|
|
1585
1644
|
}
|
|
1586
|
-
throw new CommanderError('MissingRequiredArgument', `missing required argument(s): ${def.name}`, this.#getCommandPath());
|
|
1587
|
-
}
|
|
1588
|
-
else {
|
|
1589
1645
|
args[def.name] = this.#convertArgument(def, raw);
|
|
1590
1646
|
index += 1;
|
|
1647
|
+
continue;
|
|
1591
1648
|
}
|
|
1649
|
+
const raw = rawArgs[index];
|
|
1650
|
+
args[def.name] = this.#convertArgument(def, raw);
|
|
1651
|
+
index += 1;
|
|
1592
1652
|
}
|
|
1593
1653
|
const hasRestArgument = argumentDefs.some(a => a.kind === 'variadic' || a.kind === 'some');
|
|
1594
1654
|
if (!hasRestArgument && index < rawArgs.length) {
|
|
@@ -1622,6 +1682,50 @@ class Command {
|
|
|
1622
1682
|
}
|
|
1623
1683
|
return value;
|
|
1624
1684
|
}
|
|
1685
|
+
#assertUnknownSubcommand(userTailArgv) {
|
|
1686
|
+
if (this.#subcommandsList.length === 0) {
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
const token = userTailArgv[0];
|
|
1690
|
+
if (token === undefined || token.startsWith('-') || token === 'help') {
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
if (this.#findSubcommandEntry(token) !== undefined) {
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
const hints = [];
|
|
1697
|
+
if (this.#arguments.length === 0) {
|
|
1698
|
+
hints.push(`Hint: command "${this.#getCommandPath()}" does not accept positional arguments.`);
|
|
1699
|
+
}
|
|
1700
|
+
const candidate = this.#resolveDidYouMeanSubcommandName(token);
|
|
1701
|
+
if (candidate !== undefined) {
|
|
1702
|
+
hints.push(`Hint: did you mean "${candidate}"?`);
|
|
1703
|
+
}
|
|
1704
|
+
const details = hints.length > 0 ? `\n${hints.join('\n')}` : '';
|
|
1705
|
+
throw new CommanderError('UnknownSubcommand', `unknown subcommand "${token}" for command "${this.#getCommandPath()}"${details}`, this.#getCommandPath());
|
|
1706
|
+
}
|
|
1707
|
+
#resolveDidYouMeanSubcommandName(token) {
|
|
1708
|
+
const source = normalizeSubcommandNameForDistance(token);
|
|
1709
|
+
let minDistance = Number.POSITIVE_INFINITY;
|
|
1710
|
+
let bestName;
|
|
1711
|
+
let isUniqueBest = false;
|
|
1712
|
+
for (const entry of this.#subcommandsList) {
|
|
1713
|
+
const target = normalizeSubcommandNameForDistance(entry.name);
|
|
1714
|
+
const distance = levenshteinDistance(source, target);
|
|
1715
|
+
if (distance < minDistance) {
|
|
1716
|
+
minDistance = distance;
|
|
1717
|
+
bestName = entry.name;
|
|
1718
|
+
isUniqueBest = true;
|
|
1719
|
+
}
|
|
1720
|
+
else if (distance === minDistance) {
|
|
1721
|
+
isUniqueBest = false;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
if (minDistance <= 2 && isUniqueBest) {
|
|
1725
|
+
return bestName;
|
|
1726
|
+
}
|
|
1727
|
+
return undefined;
|
|
1728
|
+
}
|
|
1625
1729
|
#hasUserOption(long) {
|
|
1626
1730
|
return this.#options.some(option => option.long === long);
|
|
1627
1731
|
}
|
|
@@ -1665,11 +1769,9 @@ class Command {
|
|
|
1665
1769
|
return optionPolicyMap;
|
|
1666
1770
|
}
|
|
1667
1771
|
#mustGetOptionPolicy(optionPolicyMap, cmd) {
|
|
1668
|
-
const policy = optionPolicyMap.get(cmd);
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
}
|
|
1672
|
-
throw new CommanderError('ConfigurationError', `missing option policy for command "${cmd.#getCommandPath()}"`, this.#getCommandPath());
|
|
1772
|
+
const policy = optionPolicyMap.get(cmd) ?? cmd.#resolveOptionPolicy();
|
|
1773
|
+
optionPolicyMap.set(cmd, policy);
|
|
1774
|
+
return policy;
|
|
1673
1775
|
}
|
|
1674
1776
|
#validateMergedShortOptions(chain, optionPolicyMap) {
|
|
1675
1777
|
const mergedByLong = new Map();
|
|
@@ -1698,7 +1800,10 @@ class Command {
|
|
|
1698
1800
|
throw new CommanderError('ConfigurationError', `boolean option "--${opt.long}" must have args: 'none'`, this.#getCommandPath());
|
|
1699
1801
|
}
|
|
1700
1802
|
if ((opt.type === 'string' || opt.type === 'number') && opt.args === 'none') {
|
|
1701
|
-
throw new CommanderError('ConfigurationError', `${opt.type} option "--${opt.long}" must have args: 'required' or 'variadic'`, this.#getCommandPath());
|
|
1803
|
+
throw new CommanderError('ConfigurationError', `${opt.type} option "--${opt.long}" must have args: 'required', 'optional', or 'variadic'`, this.#getCommandPath());
|
|
1804
|
+
}
|
|
1805
|
+
if (opt.type === 'number' && opt.args === 'optional') {
|
|
1806
|
+
throw new CommanderError('ConfigurationError', `number option "--${opt.long}" does not support args: 'optional'`, this.#getCommandPath());
|
|
1702
1807
|
}
|
|
1703
1808
|
if (opt.long.startsWith('no')) {
|
|
1704
1809
|
throw new CommanderError('ConfigurationError', `option long name cannot start with "no": "${opt.long}"`, this.#getCommandPath());
|
|
@@ -1715,6 +1820,9 @@ class Command {
|
|
|
1715
1820
|
if (opt.type === 'boolean' && opt.required) {
|
|
1716
1821
|
throw new CommanderError('ConfigurationError', `boolean option "--${opt.long}" cannot be required`, this.#getCommandPath());
|
|
1717
1822
|
}
|
|
1823
|
+
if (opt.required && opt.args !== 'required') {
|
|
1824
|
+
throw new CommanderError('ConfigurationError', `required option "--${opt.long}" must use args: 'required'`, this.#getCommandPath());
|
|
1825
|
+
}
|
|
1718
1826
|
}
|
|
1719
1827
|
#checkOptionUniqueness(opt) {
|
|
1720
1828
|
if (this.#options.some(o => o.long === opt.long)) {
|
|
@@ -2023,6 +2131,35 @@ class Coerce {
|
|
|
2023
2131
|
function camelToKebabCase(str) {
|
|
2024
2132
|
return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
2025
2133
|
}
|
|
2134
|
+
const COMPLETION_SHELL_STATE = Symbol('completion-shell-state');
|
|
2135
|
+
function getCommandPath(ctx) {
|
|
2136
|
+
const names = ctx.chain
|
|
2137
|
+
.map(command => command.name)
|
|
2138
|
+
.filter((name) => Boolean(name));
|
|
2139
|
+
if (names.length > 0) {
|
|
2140
|
+
return names.join(' ');
|
|
2141
|
+
}
|
|
2142
|
+
return ctx.cmd.name ?? 'command';
|
|
2143
|
+
}
|
|
2144
|
+
function getCompletionShellState(ctx) {
|
|
2145
|
+
const host = ctx;
|
|
2146
|
+
host[COMPLETION_SHELL_STATE] ??= {};
|
|
2147
|
+
return host[COMPLETION_SHELL_STATE];
|
|
2148
|
+
}
|
|
2149
|
+
function registerCompletionShell(ctx, shell) {
|
|
2150
|
+
const state = getCompletionShellState(ctx);
|
|
2151
|
+
if (state.shell !== undefined && state.shell !== shell) {
|
|
2152
|
+
throw new CommanderError('OptionConflict', 'options "--bash", "--fish", and "--pwsh" are mutually exclusive', getCommandPath(ctx));
|
|
2153
|
+
}
|
|
2154
|
+
state.shell = shell;
|
|
2155
|
+
}
|
|
2156
|
+
function mustGetCompletionShell(ctx) {
|
|
2157
|
+
const state = getCompletionShellState(ctx);
|
|
2158
|
+
if (state.shell === undefined) {
|
|
2159
|
+
throw new CommanderError('MissingRequired', 'missing required option: one of "--bash", "--fish", or "--pwsh"', getCommandPath(ctx));
|
|
2160
|
+
}
|
|
2161
|
+
return state.shell;
|
|
2162
|
+
}
|
|
2026
2163
|
class CompletionCommand extends Command {
|
|
2027
2164
|
constructor(root, config = {}) {
|
|
2028
2165
|
const programName = config.programName ?? root.name ?? 'program';
|
|
@@ -2036,45 +2173,45 @@ class CompletionCommand extends Command {
|
|
|
2036
2173
|
type: 'boolean',
|
|
2037
2174
|
args: 'none',
|
|
2038
2175
|
desc: 'Generate Bash completion script',
|
|
2176
|
+
apply: (value, ctx) => {
|
|
2177
|
+
if (value === true) {
|
|
2178
|
+
registerCompletionShell(ctx, 'bash');
|
|
2179
|
+
}
|
|
2180
|
+
},
|
|
2039
2181
|
})
|
|
2040
2182
|
.option({
|
|
2041
2183
|
long: 'fish',
|
|
2042
2184
|
type: 'boolean',
|
|
2043
2185
|
args: 'none',
|
|
2044
2186
|
desc: 'Generate Fish completion script',
|
|
2187
|
+
apply: (value, ctx) => {
|
|
2188
|
+
if (value === true) {
|
|
2189
|
+
registerCompletionShell(ctx, 'fish');
|
|
2190
|
+
}
|
|
2191
|
+
},
|
|
2045
2192
|
})
|
|
2046
2193
|
.option({
|
|
2047
2194
|
long: 'pwsh',
|
|
2048
2195
|
type: 'boolean',
|
|
2049
2196
|
args: 'none',
|
|
2050
2197
|
desc: 'Generate PowerShell completion script',
|
|
2198
|
+
apply: (value, ctx) => {
|
|
2199
|
+
if (value === true) {
|
|
2200
|
+
registerCompletionShell(ctx, 'pwsh');
|
|
2201
|
+
}
|
|
2202
|
+
mustGetCompletionShell(ctx);
|
|
2203
|
+
},
|
|
2051
2204
|
})
|
|
2052
2205
|
.option({
|
|
2053
2206
|
long: 'write',
|
|
2054
2207
|
short: 'w',
|
|
2055
2208
|
type: 'string',
|
|
2056
|
-
args: '
|
|
2057
|
-
desc: 'Write to file (use shell default path
|
|
2058
|
-
default: undefined,
|
|
2209
|
+
args: 'optional',
|
|
2210
|
+
desc: 'Write to file (use shell default path when value is omitted or empty)',
|
|
2059
2211
|
})
|
|
2060
|
-
.action(({ opts }) => {
|
|
2212
|
+
.action(({ opts, ctx }) => {
|
|
2061
2213
|
const meta = root.getCompletionMeta();
|
|
2062
|
-
const
|
|
2063
|
-
opts['bash'] && 'bash',
|
|
2064
|
-
opts['fish'] && 'fish',
|
|
2065
|
-
opts['pwsh'] && 'pwsh',
|
|
2066
|
-
].filter(Boolean);
|
|
2067
|
-
if (selectedShells.length === 0) {
|
|
2068
|
-
console.error('Please specify a shell: --bash, --fish, or --pwsh');
|
|
2069
|
-
process.exit(1);
|
|
2070
|
-
return;
|
|
2071
|
-
}
|
|
2072
|
-
if (selectedShells.length > 1) {
|
|
2073
|
-
console.error('Please specify only one shell option');
|
|
2074
|
-
process.exit(1);
|
|
2075
|
-
return;
|
|
2076
|
-
}
|
|
2077
|
-
const shell = selectedShells[0];
|
|
2214
|
+
const shell = mustGetCompletionShell(ctx);
|
|
2078
2215
|
let script;
|
|
2079
2216
|
switch (shell) {
|
|
2080
2217
|
case 'bash':
|
|
@@ -2087,8 +2224,9 @@ class CompletionCommand extends Command {
|
|
|
2087
2224
|
script = new PwshCompletion(meta, programName).generate();
|
|
2088
2225
|
break;
|
|
2089
2226
|
}
|
|
2090
|
-
const
|
|
2091
|
-
if (
|
|
2227
|
+
const hasWrite = Object.prototype.hasOwnProperty.call(opts, 'write');
|
|
2228
|
+
if (hasWrite) {
|
|
2229
|
+
const writeOpt = opts['write'];
|
|
2092
2230
|
const filePath = typeof writeOpt === 'string' && writeOpt !== '' ? writeOpt : paths[shell];
|
|
2093
2231
|
const expandedPath = expandHome(filePath);
|
|
2094
2232
|
const dir = path.dirname(expandedPath);
|
package/lib/types/browser.d.ts
CHANGED
|
@@ -29,13 +29,14 @@ interface ICommandToken {
|
|
|
29
29
|
/** Option value type */
|
|
30
30
|
type ICommandOptionType = 'boolean' | 'number' | 'string';
|
|
31
31
|
/** Option argument mode */
|
|
32
|
-
type ICommandOptionArgs = 'none' | 'required' | 'variadic';
|
|
32
|
+
type ICommandOptionArgs = 'none' | 'required' | 'optional' | 'variadic';
|
|
33
33
|
/**
|
|
34
34
|
* Option configuration.
|
|
35
35
|
*
|
|
36
36
|
* `type` and `args` must be specified together. Valid combinations:
|
|
37
37
|
* - boolean + none → boolean
|
|
38
38
|
* - string + required → string
|
|
39
|
+
* - string + optional → string | undefined
|
|
39
40
|
* - number + required → number
|
|
40
41
|
* - string + variadic → string[]
|
|
41
42
|
* - number + variadic → number[]
|
package/lib/types/index.d.ts
CHANGED
|
@@ -92,54 +92,16 @@ interface ICommandArgumentConfig<T = unknown> {
|
|
|
92
92
|
/** Custom value transformation (takes precedence over type conversion) */
|
|
93
93
|
coerce?: (rawValue: string) => T;
|
|
94
94
|
}
|
|
95
|
-
interface ICommandBuiltinOptionConfig {
|
|
96
|
-
/** Enable built-in --version option (root only, requires configured version) */
|
|
97
|
-
version?: boolean;
|
|
98
|
-
/** Enable built-in --color/--no-color option for help rendering (defaults respect NO_COLOR) */
|
|
99
|
-
color?: boolean;
|
|
100
|
-
/** Enable built-in --log-level option */
|
|
101
|
-
logLevel?: boolean;
|
|
102
|
-
/** Enable built-in --silent option */
|
|
103
|
-
silent?: boolean;
|
|
104
|
-
/** Enable built-in --log-date/--no-log-date option */
|
|
105
|
-
logDate?: boolean;
|
|
106
|
-
/** Enable built-in --log-colorful/--no-log-colorful option */
|
|
107
|
-
logColorful?: boolean;
|
|
108
|
-
}
|
|
109
|
-
interface ICommandBuiltinConfig {
|
|
110
|
-
/** Built-in options configuration */
|
|
111
|
-
option?: boolean | ICommandBuiltinOptionConfig;
|
|
112
|
-
}
|
|
113
|
-
/** Command example configuration */
|
|
114
|
-
interface ICommandExample {
|
|
115
|
-
/** Example title */
|
|
116
|
-
title: string;
|
|
117
|
-
/** Usage fragment relative to command path */
|
|
118
|
-
usage: string;
|
|
119
|
-
/** Example description */
|
|
120
|
-
desc: string;
|
|
121
|
-
}
|
|
122
|
-
/** Command preset defaults */
|
|
123
|
-
interface ICommandPresetConfig {
|
|
124
|
-
/** Preset root directory (absolute path) */
|
|
125
|
-
root?: string;
|
|
126
|
-
/** Default preset options file */
|
|
127
|
-
opt?: string;
|
|
128
|
-
/** Default preset envs file */
|
|
129
|
-
env?: string;
|
|
130
|
-
}
|
|
131
95
|
/** Command configuration */
|
|
132
96
|
interface ICommandConfig {
|
|
133
97
|
/** Command name (only for root command) */
|
|
134
98
|
name?: string;
|
|
135
99
|
/** Command description */
|
|
136
100
|
desc: string;
|
|
137
|
-
/** Version (for
|
|
101
|
+
/** Version (for root --version) */
|
|
138
102
|
version?: string;
|
|
139
|
-
/**
|
|
140
|
-
|
|
141
|
-
/** Command-level preset defaults */
|
|
142
|
-
preset?: ICommandPresetConfig;
|
|
103
|
+
/** Enable built-in "help" subcommand */
|
|
104
|
+
help?: boolean;
|
|
143
105
|
/** Default reporter for this command */
|
|
144
106
|
reporter?: IReporter;
|
|
145
107
|
}
|
|
@@ -148,28 +110,21 @@ interface ICommand {
|
|
|
148
110
|
readonly name: string | undefined;
|
|
149
111
|
readonly description: string;
|
|
150
112
|
readonly version: string | undefined;
|
|
151
|
-
readonly builtin: ICommandConfig['builtin'] | undefined;
|
|
152
|
-
readonly preset: ICommandPresetConfig | undefined;
|
|
153
113
|
readonly parent: ICommand | undefined;
|
|
154
114
|
readonly options: ICommandOptionConfig[];
|
|
155
115
|
readonly arguments: ICommandArgumentConfig[];
|
|
156
|
-
readonly examples: ICommandExample[];
|
|
157
116
|
readonly subcommands: Map<string, ICommand>;
|
|
158
117
|
}
|
|
159
118
|
/** Execution context */
|
|
160
119
|
interface ICommandContext {
|
|
161
120
|
/** Current command node */
|
|
162
121
|
cmd: ICommand;
|
|
163
|
-
/**
|
|
164
|
-
chain: ICommand[];
|
|
165
|
-
/** Effective environment variables */
|
|
122
|
+
/** Environment variables */
|
|
166
123
|
envs: Record<string, string | undefined>;
|
|
167
|
-
/** Built-in control hit status */
|
|
168
|
-
controls: ICommandControls;
|
|
169
|
-
/** Input source snapshots */
|
|
170
|
-
sources: ICommandInputSources;
|
|
171
124
|
/** Reporter instance */
|
|
172
125
|
reporter: IReporter;
|
|
126
|
+
/** Original argv */
|
|
127
|
+
argv: string[];
|
|
173
128
|
}
|
|
174
129
|
/** Action callback parameters */
|
|
175
130
|
interface ICommandActionParams {
|
|
@@ -198,29 +153,11 @@ type ICommandParsedOpts = Record<string, unknown>;
|
|
|
198
153
|
/** Parsed arguments record */
|
|
199
154
|
type ICommandParsedArgs = Record<string, unknown>;
|
|
200
155
|
/** Route stage result */
|
|
201
|
-
interface ICommandRouteResult
|
|
156
|
+
interface ICommandRouteResult {
|
|
202
157
|
/** Command chain from root to leaf */
|
|
203
|
-
chain:
|
|
158
|
+
chain: ICommand[];
|
|
204
159
|
/** Remaining argv after routing */
|
|
205
160
|
remaining: string[];
|
|
206
|
-
/** Routed command tokens from user argv (name/alias) */
|
|
207
|
-
cmds: string[];
|
|
208
|
-
}
|
|
209
|
-
/** Control-scan stage result */
|
|
210
|
-
interface ICommandControlScanResult {
|
|
211
|
-
/** Built-in control hit status */
|
|
212
|
-
controls: ICommandControls;
|
|
213
|
-
/** Remaining argv after stripping control tokens */
|
|
214
|
-
remaining: string[];
|
|
215
|
-
/** Optional target token from `help <child>` syntax */
|
|
216
|
-
helpTarget?: string;
|
|
217
|
-
}
|
|
218
|
-
/** Preset stage result */
|
|
219
|
-
interface ICommandPresetResult {
|
|
220
|
-
/** Effective tail argv after preset merge */
|
|
221
|
-
tailArgv: string[];
|
|
222
|
-
/** Effective envs after preset merge */
|
|
223
|
-
envs: Record<string, string | undefined>;
|
|
224
161
|
}
|
|
225
162
|
/** Tokenize stage result */
|
|
226
163
|
interface ICommandTokenizeResult {
|
|
@@ -254,71 +191,8 @@ interface ICommandParseResult {
|
|
|
254
191
|
/** Raw argument strings */
|
|
255
192
|
rawArgs: string[];
|
|
256
193
|
}
|
|
257
|
-
/** Input source snapshots for debugging/tracing */
|
|
258
|
-
interface ICommandInputSources {
|
|
259
|
-
preset: {
|
|
260
|
-
argv: string[];
|
|
261
|
-
envs: Record<string, string>;
|
|
262
|
-
};
|
|
263
|
-
user: {
|
|
264
|
-
/** Routed command tokens (name/alias as entered by user) */
|
|
265
|
-
cmds: string[];
|
|
266
|
-
/** Clean user tail argv after removing command chain/control/preset directives */
|
|
267
|
-
argv: string[];
|
|
268
|
-
/** Raw env snapshot from run/parse params */
|
|
269
|
-
envs: Record<string, string | undefined>;
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
/** Built-in run controls */
|
|
273
|
-
interface ICommandControls {
|
|
274
|
-
help: boolean;
|
|
275
|
-
version: boolean;
|
|
276
|
-
}
|
|
277
|
-
/** Built-in option resolution result (internal) */
|
|
278
|
-
interface ICommandBuiltinOptionResolved {
|
|
279
|
-
version: boolean;
|
|
280
|
-
color: boolean;
|
|
281
|
-
logLevel: boolean;
|
|
282
|
-
silent: boolean;
|
|
283
|
-
logDate: boolean;
|
|
284
|
-
logColorful: boolean;
|
|
285
|
-
}
|
|
286
|
-
/** Built-in config resolution result (internal) */
|
|
287
|
-
interface ICommandBuiltinResolved {
|
|
288
|
-
option: ICommandBuiltinOptionResolved;
|
|
289
|
-
}
|
|
290
|
-
/** Subcommand registry entry (internal) */
|
|
291
|
-
interface ISubcommandEntry<TCommand = ICommand> {
|
|
292
|
-
name: string;
|
|
293
|
-
aliases: string[];
|
|
294
|
-
command: TCommand;
|
|
295
|
-
}
|
|
296
|
-
/** Help option line (internal) */
|
|
297
|
-
interface IHelpOptionLine {
|
|
298
|
-
sig: string;
|
|
299
|
-
desc: string;
|
|
300
|
-
}
|
|
301
|
-
/** Help command line (internal) */
|
|
302
|
-
interface IHelpCommandLine {
|
|
303
|
-
name: string;
|
|
304
|
-
desc: string;
|
|
305
|
-
}
|
|
306
|
-
/** Help example line (internal) */
|
|
307
|
-
interface IHelpExampleLine {
|
|
308
|
-
title: string;
|
|
309
|
-
usage: string;
|
|
310
|
-
desc: string;
|
|
311
|
-
}
|
|
312
|
-
/** Structured help data for rendering (internal) */
|
|
313
|
-
interface IHelpData {
|
|
314
|
-
desc: string;
|
|
315
|
-
usage: string;
|
|
316
|
-
options: IHelpOptionLine[];
|
|
317
|
-
commands: IHelpCommandLine[];
|
|
318
|
-
examples: IHelpExampleLine[];
|
|
319
|
-
}
|
|
320
194
|
/** Error kinds for command parsing */
|
|
321
|
-
type ICommanderErrorKind = 'InvalidOptionFormat' | 'InvalidNegativeOption' | 'NegativeOptionWithValue' | 'NegativeOptionType' | 'UnknownOption' | '
|
|
195
|
+
type ICommanderErrorKind = 'InvalidOptionFormat' | 'InvalidNegativeOption' | 'NegativeOptionWithValue' | 'NegativeOptionType' | 'UnknownOption' | 'UnexpectedArgument' | 'MissingValue' | 'InvalidType' | 'UnsupportedShortSyntax' | 'OptionConflict' | 'MissingRequired' | 'InvalidChoice' | 'InvalidBooleanValue' | 'MissingRequiredArgument' | 'TooManyArguments' | 'ConfigurationError';
|
|
322
196
|
/** Commander error with structured information */
|
|
323
197
|
declare class CommanderError extends Error {
|
|
324
198
|
readonly kind: ICommanderErrorKind;
|
|
@@ -369,13 +243,13 @@ interface ICompletionCommandConfig {
|
|
|
369
243
|
/** Program name for completion scripts (defaults to root.name) */
|
|
370
244
|
programName?: string;
|
|
371
245
|
/** Default completion file paths for each shell */
|
|
372
|
-
paths
|
|
246
|
+
paths: ICompletionPaths;
|
|
373
247
|
}
|
|
374
248
|
|
|
375
249
|
/**
|
|
376
250
|
* Command class - CLI command builder with fluent API
|
|
377
251
|
*
|
|
378
|
-
* Execution flow: route →
|
|
252
|
+
* Execution flow: route → tokenize → resolve → parse → run
|
|
379
253
|
*
|
|
380
254
|
* @module @guanghechen/commander
|
|
381
255
|
*/
|
|
@@ -386,48 +260,20 @@ declare class Command implements ICommand {
|
|
|
386
260
|
get name(): string | undefined;
|
|
387
261
|
get description(): string;
|
|
388
262
|
get version(): string | undefined;
|
|
389
|
-
get builtin(): ICommandConfig['builtin'] | undefined;
|
|
390
|
-
get preset(): ICommandPresetConfig | undefined;
|
|
391
263
|
get parent(): Command | undefined;
|
|
392
264
|
get options(): ICommandOptionConfig[];
|
|
393
265
|
get arguments(): ICommandArgumentConfig[];
|
|
394
|
-
get examples(): ICommandExample[];
|
|
395
266
|
get subcommands(): Map<string, ICommand>;
|
|
396
267
|
option<T>(opt: ICommandOptionConfig<T>): this;
|
|
397
268
|
argument<T>(arg: ICommandArgumentConfig<T>): this;
|
|
398
269
|
action(fn: ICommandAction): this;
|
|
399
|
-
example(title: string, usage: string, desc: string): this;
|
|
400
270
|
subcommand(name: string, cmd: Command): this;
|
|
401
271
|
run(params: ICommandRunParams): Promise<void>;
|
|
402
|
-
parse(params: ICommandRunParams):
|
|
272
|
+
parse(params: ICommandRunParams): ICommandParseResult;
|
|
403
273
|
formatHelp(): string;
|
|
404
274
|
getCompletionMeta(): ICompletionMeta;
|
|
405
275
|
}
|
|
406
276
|
|
|
407
|
-
/**
|
|
408
|
-
* Pre-defined coerce factory methods for @guanghechen/commander.
|
|
409
|
-
*
|
|
410
|
-
* @module @guanghechen/commander/coerce
|
|
411
|
-
*/
|
|
412
|
-
declare class Coerce {
|
|
413
|
-
private constructor();
|
|
414
|
-
private static create;
|
|
415
|
-
static choice<TValue extends string>(name: string, values: ReadonlyArray<TValue>, errorMessage?: string): (rawValue: string) => TValue;
|
|
416
|
-
static domain(name: string, errorMessage?: string): (rawValue: string) => string;
|
|
417
|
-
static host(name: string, errorMessage?: string): (rawValue: string) => string;
|
|
418
|
-
static integer(name: string, errorMessage?: string): (rawValue: string) => number;
|
|
419
|
-
static ip(name: string, errorMessage?: string): (rawValue: string) => string;
|
|
420
|
-
static number(name: string, errorMessage?: string): (rawValue: string) => number;
|
|
421
|
-
static port(name: string, errorMessage?: string): (rawValue: string) => number;
|
|
422
|
-
static positiveInteger(name: string, errorMessage?: string): (rawValue: string) => number;
|
|
423
|
-
static positiveNumber(name: string, errorMessage?: string): (rawValue: string) => number;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
declare function isIpv4(rawValue: string): boolean;
|
|
427
|
-
declare function isIpv6(rawValue: string): boolean;
|
|
428
|
-
declare function isIp(rawValue: string): boolean;
|
|
429
|
-
declare function isDomain(rawValue: string): boolean;
|
|
430
|
-
|
|
431
277
|
/**
|
|
432
278
|
* Shell completion generators
|
|
433
279
|
*
|
|
@@ -455,7 +301,7 @@ declare function isDomain(rawValue: string): boolean;
|
|
|
455
301
|
* ```
|
|
456
302
|
*/
|
|
457
303
|
declare class CompletionCommand extends Command {
|
|
458
|
-
constructor(root: Command, config
|
|
304
|
+
constructor(root: Command, config: ICompletionCommandConfig);
|
|
459
305
|
}
|
|
460
306
|
declare class BashCompletion {
|
|
461
307
|
#private;
|
|
@@ -509,28 +355,6 @@ declare class PwshCompletion {
|
|
|
509
355
|
* ```
|
|
510
356
|
*/
|
|
511
357
|
declare const logLevelOption: ICommandOptionConfig<string>;
|
|
512
|
-
/**
|
|
513
|
-
* Pre-defined --log-date option for controlling timestamp output.
|
|
514
|
-
*
|
|
515
|
-
* | Property | Value |
|
|
516
|
-
* | --------- | --------- |
|
|
517
|
-
* | long | 'logDate' |
|
|
518
|
-
* | type | 'boolean' |
|
|
519
|
-
* | args | 'none' |
|
|
520
|
-
* | default | true |
|
|
521
|
-
*/
|
|
522
|
-
declare const logDateOption: ICommandOptionConfig<boolean>;
|
|
523
|
-
/**
|
|
524
|
-
* Pre-defined --log-colorful option for controlling colorful output.
|
|
525
|
-
*
|
|
526
|
-
* | Property | Value |
|
|
527
|
-
* | --------- | ------------- |
|
|
528
|
-
* | long | 'logColorful' |
|
|
529
|
-
* | type | 'boolean' |
|
|
530
|
-
* | args | 'none' |
|
|
531
|
-
* | default | true |
|
|
532
|
-
*/
|
|
533
|
-
declare const logColorfulOption: ICommandOptionConfig<boolean>;
|
|
534
358
|
/**
|
|
535
359
|
* Pre-defined --silent option for suppressing non-error output.
|
|
536
360
|
*
|
|
@@ -556,5 +380,5 @@ declare const logColorfulOption: ICommandOptionConfig<boolean>;
|
|
|
556
380
|
*/
|
|
557
381
|
declare const silentOption: ICommandOptionConfig<boolean>;
|
|
558
382
|
|
|
559
|
-
export { BashCompletion,
|
|
560
|
-
export type { ICommand, ICommandAction, ICommandActionParams, ICommandArgumentConfig, ICommandArgumentKind, ICommandArgumentType,
|
|
383
|
+
export { BashCompletion, Command, CommanderError, CompletionCommand, FishCompletion, PwshCompletion, logLevelOption, silentOption };
|
|
384
|
+
export type { ICommand, ICommandAction, ICommandActionParams, ICommandArgumentConfig, ICommandArgumentKind, ICommandArgumentType, ICommandConfig, ICommandContext, ICommandOptionArgs, ICommandOptionConfig, ICommandOptionType, ICommandParseResult, ICommandParsedArgs, ICommandParsedOpts, ICommandResolveResult, ICommandRouteResult, ICommandRunParams, ICommandShiftResult, ICommandToken, ICommandTokenType, ICommandTokenizeResult, ICommanderErrorKind, ICompletionCommandConfig, ICompletionMeta, ICompletionOptionMeta, ICompletionPaths, ICompletionShellType };
|