@optique/core 1.1.0-dev.2096 → 1.1.0-dev.2148
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/annotation-state.cjs +26 -26
- package/dist/annotation-state.d.cts +133 -1
- package/dist/annotation-state.d.ts +133 -1
- package/dist/annotations.cjs +2 -2
- package/dist/constructs.cjs +141 -73
- package/dist/constructs.js +70 -2
- package/dist/dependency-metadata.cjs +12 -12
- package/dist/dependency-metadata.d.cts +34 -3
- package/dist/dependency-metadata.d.ts +34 -3
- package/dist/dependency-runtime.cjs +37 -13
- package/dist/dependency-runtime.d.cts +197 -2
- package/dist/dependency-runtime.d.ts +197 -2
- package/dist/dependency-runtime.js +22 -1
- package/dist/dependency.cjs +7 -7
- package/dist/displaywidth.d.cts +12 -0
- package/dist/displaywidth.d.ts +12 -0
- package/dist/execution-context.d.cts +23 -0
- package/dist/execution-context.d.ts +23 -0
- package/dist/extension.cjs +14 -14
- package/dist/facade.cjs +46 -36
- package/dist/facade.js +31 -21
- package/dist/index.cjs +22 -21
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/input-trace.d.cts +2 -1
- package/dist/input-trace.d.ts +2 -1
- package/dist/internal/annotations.cjs +3 -0
- package/dist/internal/annotations.d.cts +47 -5
- package/dist/internal/annotations.d.ts +47 -5
- package/dist/internal/annotations.js +1 -1
- package/dist/internal/command-alias.cjs +16 -0
- package/dist/internal/command-alias.js +14 -0
- package/dist/internal/dependency.cjs +131 -0
- package/dist/internal/dependency.d.cts +311 -2
- package/dist/internal/dependency.d.ts +311 -2
- package/dist/internal/dependency.js +119 -1
- package/dist/internal/parser.cjs +35 -13
- package/dist/internal/parser.d.cts +44 -3
- package/dist/internal/parser.d.ts +44 -3
- package/dist/internal/parser.js +28 -6
- package/dist/modifiers.cjs +41 -41
- package/dist/parser.cjs +11 -11
- package/dist/phase2-seed.cjs +2 -2
- package/dist/phase2-seed.d.cts +50 -0
- package/dist/phase2-seed.d.ts +50 -0
- package/dist/primitives.cjs +74 -33
- package/dist/primitives.d.cts +10 -0
- package/dist/primitives.d.ts +10 -0
- package/dist/primitives.js +54 -13
- package/dist/suggestion.cjs +72 -2
- package/dist/suggestion.d.cts +188 -0
- package/dist/suggestion.d.ts +188 -0
- package/dist/suggestion.js +71 -3
- package/dist/usage-internals.cjs +5 -1
- package/dist/usage-internals.js +5 -1
- package/dist/usage.cjs +9 -1
- package/dist/usage.d.cts +14 -0
- package/dist/usage.d.ts +14 -0
- package/dist/usage.js +9 -1
- package/dist/validate.cjs +1 -0
- package/dist/validate.d.cts +99 -0
- package/dist/validate.d.ts +99 -0
- package/dist/validate.js +1 -1
- package/dist/valueparser.cjs +333 -79
- package/dist/valueparser.d.cts +197 -1
- package/dist/valueparser.d.ts +197 -1
- package/dist/valueparser.js +334 -81
- package/package.json +19 -4
package/dist/primitives.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_internal_annotations = require('./internal/annotations.cjs');
|
|
2
2
|
const require_message = require('./message.cjs');
|
|
3
3
|
const require_validate = require('./validate.cjs');
|
|
4
4
|
const require_usage = require('./usage.cjs');
|
|
5
5
|
const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
|
|
6
|
-
const
|
|
6
|
+
const require_internal_dependency = require('./internal/dependency.cjs');
|
|
7
7
|
const require_dependency_runtime = require('./dependency-runtime.cjs');
|
|
8
8
|
const require_annotation_state = require('./annotation-state.cjs');
|
|
9
|
+
const require_command_alias = require('./internal/command-alias.cjs');
|
|
9
10
|
const require_execution_context = require('./execution-context.cjs');
|
|
10
11
|
const require_phase2_seed = require('./phase2-seed.cjs');
|
|
11
12
|
const require_suggestion = require('./suggestion.cjs');
|
|
@@ -46,8 +47,8 @@ function buildTraceEntry(kind, rawInput, consumed, valueParser, parseResult, opt
|
|
|
46
47
|
...optionNames$1 != null ? { optionNames: optionNames$1 } : {},
|
|
47
48
|
metavar: valueParser.metavar
|
|
48
49
|
};
|
|
49
|
-
if (
|
|
50
|
-
const defaults =
|
|
50
|
+
if (require_internal_dependency.isDerivedValueParser(valueParser)) {
|
|
51
|
+
const defaults = require_internal_dependency.getSnapshottedDefaultDependencyValues(parseResult);
|
|
51
52
|
if (defaults != null) return {
|
|
52
53
|
...entry,
|
|
53
54
|
defaultDependencyValues: defaults
|
|
@@ -211,13 +212,13 @@ function fail() {
|
|
|
211
212
|
*/
|
|
212
213
|
function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry, exec) {
|
|
213
214
|
if (!valueParser.suggest) return;
|
|
214
|
-
if (
|
|
215
|
+
if (require_internal_dependency.isDerivedValueParser(valueParser) && require_internal_dependency.suggestWithDependency in valueParser) {
|
|
215
216
|
const derived = valueParser;
|
|
216
|
-
const suggestWithDep = derived[
|
|
217
|
+
const suggestWithDep = derived[require_internal_dependency.suggestWithDependency];
|
|
217
218
|
if (suggestWithDep && dependencyRegistry) {
|
|
218
219
|
const dependencyRuntime = exec?.dependencyRuntime;
|
|
219
|
-
const depIds =
|
|
220
|
-
const defaultsFn =
|
|
220
|
+
const depIds = require_internal_dependency.getDependencyIds(derived);
|
|
221
|
+
const defaultsFn = require_internal_dependency.getDefaultValuesFunction(derived);
|
|
221
222
|
const defaults = defaultsFn?.();
|
|
222
223
|
const dependencyValues = [];
|
|
223
224
|
let hasAnyValue = false;
|
|
@@ -295,13 +296,13 @@ function* suggestOptionSync(optionNames$1, valueParser, hidden, context, prefix)
|
|
|
295
296
|
*/
|
|
296
297
|
async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependencyRegistry, exec) {
|
|
297
298
|
if (!valueParser.suggest) return;
|
|
298
|
-
if (
|
|
299
|
+
if (require_internal_dependency.isDerivedValueParser(valueParser) && require_internal_dependency.suggestWithDependency in valueParser) {
|
|
299
300
|
const derived = valueParser;
|
|
300
|
-
const suggestWithDep = derived[
|
|
301
|
+
const suggestWithDep = derived[require_internal_dependency.suggestWithDependency];
|
|
301
302
|
if (suggestWithDep && dependencyRegistry) {
|
|
302
303
|
const dependencyRuntime = exec?.dependencyRuntime;
|
|
303
|
-
const depIds =
|
|
304
|
-
const defaultsFn =
|
|
304
|
+
const depIds = require_internal_dependency.getDependencyIds(derived);
|
|
305
|
+
const defaultsFn = require_internal_dependency.getDefaultValuesFunction(derived);
|
|
305
306
|
const defaults = defaultsFn?.();
|
|
306
307
|
const dependencyValues = [];
|
|
307
308
|
let hasAnyValue = false;
|
|
@@ -690,7 +691,7 @@ function option(...args) {
|
|
|
690
691
|
configurable: true,
|
|
691
692
|
enumerable: false
|
|
692
693
|
});
|
|
693
|
-
else if (!
|
|
694
|
+
else if (!require_internal_dependency.isDerivedValueParser(valueParser)) {
|
|
694
695
|
const vp = valueParser;
|
|
695
696
|
const wrapParseResult = (parsed) => parsed.success ? parsed : {
|
|
696
697
|
success: false,
|
|
@@ -698,6 +699,7 @@ function option(...args) {
|
|
|
698
699
|
};
|
|
699
700
|
Object.defineProperty(result, "validateValue", {
|
|
700
701
|
value(v) {
|
|
702
|
+
if (typeof vp.validate === "function") return require_mode_dispatch.wrapForMode(mode, wrapParseResult(vp.validate(v)));
|
|
701
703
|
let stringified;
|
|
702
704
|
try {
|
|
703
705
|
stringified = vp.format(v);
|
|
@@ -1324,7 +1326,7 @@ function argument(valueParser, options = {}) {
|
|
|
1324
1326
|
enumerable: false
|
|
1325
1327
|
});
|
|
1326
1328
|
}
|
|
1327
|
-
if (!
|
|
1329
|
+
if (!require_internal_dependency.isDerivedValueParser(valueParser)) {
|
|
1328
1330
|
const vp = valueParser;
|
|
1329
1331
|
const vpMode = valueParser.mode;
|
|
1330
1332
|
const wrapParseResult = (parsed) => parsed.success ? parsed : {
|
|
@@ -1333,6 +1335,7 @@ function argument(valueParser, options = {}) {
|
|
|
1333
1335
|
};
|
|
1334
1336
|
Object.defineProperty(result, "validateValue", {
|
|
1335
1337
|
value(v) {
|
|
1338
|
+
if (typeof vp.validate === "function") return require_mode_dispatch.wrapForMode(vpMode, wrapParseResult(vp.validate(v)));
|
|
1336
1339
|
let stringified;
|
|
1337
1340
|
try {
|
|
1338
1341
|
stringified = vp.format(v);
|
|
@@ -1380,7 +1383,7 @@ function getCommandChildState(commandState, childState, parser) {
|
|
|
1380
1383
|
return require_annotation_state.getWrappedChildState(commandState, childState, parser);
|
|
1381
1384
|
}
|
|
1382
1385
|
function createCommandState(sourceState, state) {
|
|
1383
|
-
return
|
|
1386
|
+
return require_internal_annotations.annotateFreshArray(sourceState, state);
|
|
1384
1387
|
}
|
|
1385
1388
|
function appendCommandPath(exec, name) {
|
|
1386
1389
|
if (exec == null) return void 0;
|
|
@@ -1389,25 +1392,56 @@ function appendCommandPath(exec, name) {
|
|
|
1389
1392
|
commandPath: [...exec.commandPath ?? [], name]
|
|
1390
1393
|
};
|
|
1391
1394
|
}
|
|
1392
|
-
function
|
|
1395
|
+
function getCommandNames(name, options) {
|
|
1396
|
+
return [
|
|
1397
|
+
name,
|
|
1398
|
+
...getVisibleCommandAliases(options),
|
|
1399
|
+
...getHiddenCommandAliases(options)
|
|
1400
|
+
];
|
|
1401
|
+
}
|
|
1402
|
+
function getVisibleCommandAliases(options) {
|
|
1403
|
+
const aliases = options.aliases;
|
|
1404
|
+
if (aliases == null) return [];
|
|
1405
|
+
if (!isNonEmptyStringArray(aliases)) throw new TypeError("Command aliases must be a non-empty array of strings.");
|
|
1406
|
+
return aliases;
|
|
1407
|
+
}
|
|
1408
|
+
function getHiddenCommandAliases(options) {
|
|
1409
|
+
const hiddenAliases = options[require_command_alias.hiddenCommandAliasesKey];
|
|
1410
|
+
if (hiddenAliases == null) return [];
|
|
1411
|
+
if (!isNonEmptyStringArray(hiddenAliases)) throw new TypeError("Hidden command aliases must be a non-empty array of strings.");
|
|
1412
|
+
return hiddenAliases;
|
|
1413
|
+
}
|
|
1414
|
+
function isNonEmptyStringArray(value) {
|
|
1415
|
+
if (!Array.isArray(value) || value.length === 0) return false;
|
|
1416
|
+
for (let i = 0; i < value.length; i++) if (typeof value[i] !== "string") return false;
|
|
1417
|
+
return true;
|
|
1418
|
+
}
|
|
1419
|
+
function validateUniqueCommandNames(names) {
|
|
1420
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1421
|
+
for (const name of names) {
|
|
1422
|
+
if (seen.has(name)) throw new TypeError(`Command has a duplicate name: "${name}".`);
|
|
1423
|
+
seen.add(name);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
|
|
1393
1427
|
if (require_usage.isSuggestionHidden(options.hidden)) return;
|
|
1394
1428
|
const state = normalizeCommandState(context.state);
|
|
1395
1429
|
if (state === void 0) {
|
|
1396
|
-
if (
|
|
1430
|
+
for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
|
|
1397
1431
|
kind: "literal",
|
|
1398
|
-
text:
|
|
1432
|
+
text: commandName,
|
|
1399
1433
|
...options.description && { description: options.description }
|
|
1400
1434
|
};
|
|
1401
1435
|
} else if (state[0] === "matched") yield* parser.suggest(require_execution_context.withChildContext(context, name, getCommandChildState(context.state, parser.initialState, parser), parser.usage), prefix);
|
|
1402
1436
|
else if (state[0] === "parsing") yield* parser.suggest(require_execution_context.withChildContext(context, name, getCommandChildState(context.state, state[1], parser), parser.usage), prefix);
|
|
1403
1437
|
}
|
|
1404
|
-
async function* suggestCommandAsync(context, prefix, name, parser, options) {
|
|
1438
|
+
async function* suggestCommandAsync(context, prefix, name, aliases, parser, options) {
|
|
1405
1439
|
if (require_usage.isSuggestionHidden(options.hidden)) return;
|
|
1406
1440
|
const state = normalizeCommandState(context.state);
|
|
1407
1441
|
if (state === void 0) {
|
|
1408
|
-
if (
|
|
1442
|
+
for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
|
|
1409
1443
|
kind: "literal",
|
|
1410
|
-
text:
|
|
1444
|
+
text: commandName,
|
|
1411
1445
|
...options.description && { description: options.description }
|
|
1412
1446
|
};
|
|
1413
1447
|
} else if (state[0] === "matched") {
|
|
@@ -1435,7 +1469,11 @@ async function* suggestCommandAsync(context, prefix, name, parser, options) {
|
|
|
1435
1469
|
* embedded whitespace, or contains control characters.
|
|
1436
1470
|
*/
|
|
1437
1471
|
function command(name, parser, options = {}) {
|
|
1438
|
-
|
|
1472
|
+
const commandNames = getCommandNames(name, options);
|
|
1473
|
+
const aliases = getVisibleCommandAliases(options);
|
|
1474
|
+
const hiddenAliases = getHiddenCommandAliases(options);
|
|
1475
|
+
require_validate.validateCommandNames(commandNames, "Command");
|
|
1476
|
+
validateUniqueCommandNames(commandNames);
|
|
1439
1477
|
const isAsync = parser.mode === "async";
|
|
1440
1478
|
const syncInnerParser = parser;
|
|
1441
1479
|
const asyncInnerParser = parser;
|
|
@@ -1448,10 +1486,12 @@ function command(name, parser, options = {}) {
|
|
|
1448
1486
|
usage: [{
|
|
1449
1487
|
type: "command",
|
|
1450
1488
|
name,
|
|
1489
|
+
...aliases.length > 0 && { aliases },
|
|
1490
|
+
...hiddenAliases.length > 0 && { hiddenAliases },
|
|
1451
1491
|
...options.usageLine != null && { usageLine: options.usageLine },
|
|
1452
1492
|
...options.hidden != null && { hidden: options.hidden }
|
|
1453
1493
|
}, ...parser.usage],
|
|
1454
|
-
leadingNames: new Set(
|
|
1494
|
+
leadingNames: new Set(commandNames),
|
|
1455
1495
|
acceptingAnyToken: false,
|
|
1456
1496
|
initialState: void 0,
|
|
1457
1497
|
canSkip(state, exec) {
|
|
@@ -1474,10 +1514,11 @@ function command(name, parser, options = {}) {
|
|
|
1474
1514
|
parse(context) {
|
|
1475
1515
|
const state = normalizeCommandState(context.state);
|
|
1476
1516
|
if (state === void 0) {
|
|
1477
|
-
if (context.buffer.length < 1 || context.buffer[0]
|
|
1517
|
+
if (context.buffer.length < 1 || !commandNames.includes(context.buffer[0])) {
|
|
1478
1518
|
const actual = context.buffer.length > 0 ? context.buffer[0] : null;
|
|
1479
1519
|
const leadingCmds = require_usage_internals.extractLeadingCommandNames(context.usage);
|
|
1480
|
-
const
|
|
1520
|
+
const rawSuggestions = actual ? require_suggestion.findSimilar(actual, leadingCmds, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS) : [];
|
|
1521
|
+
const suggestions = require_suggestion.expandCommandAliasSuggestions(context.usage, rawSuggestions);
|
|
1481
1522
|
if (options.errors?.notMatched) {
|
|
1482
1523
|
const errorMessage = options.errors.notMatched;
|
|
1483
1524
|
return {
|
|
@@ -1608,8 +1649,8 @@ function command(name, parser, options = {}) {
|
|
|
1608
1649
|
return require_mode_dispatch.wrapForMode(parser.mode, null);
|
|
1609
1650
|
},
|
|
1610
1651
|
suggest(context, prefix) {
|
|
1611
|
-
if (isAsync) return suggestCommandAsync(context, prefix, name, parser, options);
|
|
1612
|
-
return suggestCommandSync(context, prefix, name, parser, options);
|
|
1652
|
+
if (isAsync) return suggestCommandAsync(context, prefix, name, aliases, parser, options);
|
|
1653
|
+
return suggestCommandSync(context, prefix, name, aliases, parser, options);
|
|
1613
1654
|
},
|
|
1614
1655
|
getDocFragments(state, defaultValue) {
|
|
1615
1656
|
const commandState = state.kind === "available" ? normalizeCommandState(state.state) : void 0;
|
|
@@ -1740,7 +1781,7 @@ function passThrough(options = {}) {
|
|
|
1740
1781
|
next: {
|
|
1741
1782
|
...context,
|
|
1742
1783
|
buffer: [],
|
|
1743
|
-
state:
|
|
1784
|
+
state: require_internal_annotations.annotateFreshArray(context.state, [...context.state, ...captured])
|
|
1744
1785
|
},
|
|
1745
1786
|
consumed: captured
|
|
1746
1787
|
};
|
|
@@ -1761,7 +1802,7 @@ function passThrough(options = {}) {
|
|
|
1761
1802
|
next: {
|
|
1762
1803
|
...context,
|
|
1763
1804
|
buffer: context.buffer.slice(1),
|
|
1764
|
-
state:
|
|
1805
|
+
state: require_internal_annotations.annotateFreshArray(context.state, [...context.state, token])
|
|
1765
1806
|
},
|
|
1766
1807
|
consumed: [token]
|
|
1767
1808
|
};
|
|
@@ -1777,7 +1818,7 @@ function passThrough(options = {}) {
|
|
|
1777
1818
|
next: {
|
|
1778
1819
|
...context,
|
|
1779
1820
|
buffer: context.buffer.slice(1),
|
|
1780
|
-
state:
|
|
1821
|
+
state: require_internal_annotations.annotateFreshArray(context.state, [...context.state, token])
|
|
1781
1822
|
},
|
|
1782
1823
|
consumed: [token]
|
|
1783
1824
|
};
|
|
@@ -1787,7 +1828,7 @@ function passThrough(options = {}) {
|
|
|
1787
1828
|
next: {
|
|
1788
1829
|
...context,
|
|
1789
1830
|
buffer: context.buffer.slice(2),
|
|
1790
|
-
state:
|
|
1831
|
+
state: require_internal_annotations.annotateFreshArray(context.state, [
|
|
1791
1832
|
...context.state,
|
|
1792
1833
|
token,
|
|
1793
1834
|
nextToken
|
|
@@ -1800,7 +1841,7 @@ function passThrough(options = {}) {
|
|
|
1800
1841
|
next: {
|
|
1801
1842
|
...context,
|
|
1802
1843
|
buffer: context.buffer.slice(1),
|
|
1803
|
-
state:
|
|
1844
|
+
state: require_internal_annotations.annotateFreshArray(context.state, [...context.state, token])
|
|
1804
1845
|
},
|
|
1805
1846
|
consumed: [token]
|
|
1806
1847
|
};
|
|
@@ -1812,7 +1853,7 @@ function passThrough(options = {}) {
|
|
|
1812
1853
|
};
|
|
1813
1854
|
},
|
|
1814
1855
|
complete(state, _exec) {
|
|
1815
|
-
if (
|
|
1856
|
+
if (require_internal_annotations.getAnnotations(state) == null) return {
|
|
1816
1857
|
success: true,
|
|
1817
1858
|
value: state
|
|
1818
1859
|
};
|
package/dist/primitives.d.cts
CHANGED
|
@@ -448,6 +448,16 @@ interface CommandOptions {
|
|
|
448
448
|
* @since 0.9.0
|
|
449
449
|
*/
|
|
450
450
|
readonly hidden?: HiddenVisibility;
|
|
451
|
+
/**
|
|
452
|
+
* Additional names that invoke this command.
|
|
453
|
+
*
|
|
454
|
+
* Aliases are functional at runtime and are suggested by shell completion,
|
|
455
|
+
* but are hidden from usage and documentation output. The `name` parameter
|
|
456
|
+
* passed to {@link command} remains the canonical display name.
|
|
457
|
+
*
|
|
458
|
+
* @since 1.1.0
|
|
459
|
+
*/
|
|
460
|
+
readonly aliases?: readonly [string, ...string[]];
|
|
451
461
|
/**
|
|
452
462
|
* Error messages customization.
|
|
453
463
|
* @since 0.5.0
|
package/dist/primitives.d.ts
CHANGED
|
@@ -448,6 +448,16 @@ interface CommandOptions {
|
|
|
448
448
|
* @since 0.9.0
|
|
449
449
|
*/
|
|
450
450
|
readonly hidden?: HiddenVisibility;
|
|
451
|
+
/**
|
|
452
|
+
* Additional names that invoke this command.
|
|
453
|
+
*
|
|
454
|
+
* Aliases are functional at runtime and are suggested by shell completion,
|
|
455
|
+
* but are hidden from usage and documentation output. The `name` parameter
|
|
456
|
+
* passed to {@link command} remains the canonical display name.
|
|
457
|
+
*
|
|
458
|
+
* @since 1.1.0
|
|
459
|
+
*/
|
|
460
|
+
readonly aliases?: readonly [string, ...string[]];
|
|
451
461
|
/**
|
|
452
462
|
* Error messages customization.
|
|
453
463
|
* @since 0.5.0
|
package/dist/primitives.js
CHANGED
|
@@ -6,9 +6,10 @@ import { dispatchByMode, dispatchIterableByMode, wrapForMode } from "./internal/
|
|
|
6
6
|
import { getDefaultValuesFunction, getDependencyIds, getSnapshottedDefaultDependencyValues, isDerivedValueParser, suggestWithDependency } from "./internal/dependency.js";
|
|
7
7
|
import { replayDerivedParser, replayDerivedParserAsync } from "./dependency-runtime.js";
|
|
8
8
|
import { getWrappedChildParseState, getWrappedChildState, isAnnotationWrappedInitialState, normalizeInjectedAnnotationState } from "./annotation-state.js";
|
|
9
|
+
import { hiddenCommandAliasesKey } from "./internal/command-alias.js";
|
|
9
10
|
import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
|
|
10
11
|
import { completeOrExtractPhase2Seed, extractPhase2SeedKey } from "./phase2-seed.js";
|
|
11
|
-
import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, findSimilar } from "./suggestion.js";
|
|
12
|
+
import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, expandCommandAliasSuggestions, findSimilar } from "./suggestion.js";
|
|
12
13
|
import { extractLeadingCommandNames } from "./usage-internals.js";
|
|
13
14
|
import { extractDependencyMetadata } from "./dependency-metadata.js";
|
|
14
15
|
import { isValueParser } from "./valueparser.js";
|
|
@@ -698,6 +699,7 @@ function option(...args) {
|
|
|
698
699
|
};
|
|
699
700
|
Object.defineProperty(result, "validateValue", {
|
|
700
701
|
value(v) {
|
|
702
|
+
if (typeof vp.validate === "function") return wrapForMode(mode, wrapParseResult(vp.validate(v)));
|
|
701
703
|
let stringified;
|
|
702
704
|
try {
|
|
703
705
|
stringified = vp.format(v);
|
|
@@ -1333,6 +1335,7 @@ function argument(valueParser, options = {}) {
|
|
|
1333
1335
|
};
|
|
1334
1336
|
Object.defineProperty(result, "validateValue", {
|
|
1335
1337
|
value(v) {
|
|
1338
|
+
if (typeof vp.validate === "function") return wrapForMode(vpMode, wrapParseResult(vp.validate(v)));
|
|
1336
1339
|
let stringified;
|
|
1337
1340
|
try {
|
|
1338
1341
|
stringified = vp.format(v);
|
|
@@ -1389,25 +1392,56 @@ function appendCommandPath(exec, name) {
|
|
|
1389
1392
|
commandPath: [...exec.commandPath ?? [], name]
|
|
1390
1393
|
};
|
|
1391
1394
|
}
|
|
1392
|
-
function
|
|
1395
|
+
function getCommandNames(name, options) {
|
|
1396
|
+
return [
|
|
1397
|
+
name,
|
|
1398
|
+
...getVisibleCommandAliases(options),
|
|
1399
|
+
...getHiddenCommandAliases(options)
|
|
1400
|
+
];
|
|
1401
|
+
}
|
|
1402
|
+
function getVisibleCommandAliases(options) {
|
|
1403
|
+
const aliases = options.aliases;
|
|
1404
|
+
if (aliases == null) return [];
|
|
1405
|
+
if (!isNonEmptyStringArray(aliases)) throw new TypeError("Command aliases must be a non-empty array of strings.");
|
|
1406
|
+
return aliases;
|
|
1407
|
+
}
|
|
1408
|
+
function getHiddenCommandAliases(options) {
|
|
1409
|
+
const hiddenAliases = options[hiddenCommandAliasesKey];
|
|
1410
|
+
if (hiddenAliases == null) return [];
|
|
1411
|
+
if (!isNonEmptyStringArray(hiddenAliases)) throw new TypeError("Hidden command aliases must be a non-empty array of strings.");
|
|
1412
|
+
return hiddenAliases;
|
|
1413
|
+
}
|
|
1414
|
+
function isNonEmptyStringArray(value) {
|
|
1415
|
+
if (!Array.isArray(value) || value.length === 0) return false;
|
|
1416
|
+
for (let i = 0; i < value.length; i++) if (typeof value[i] !== "string") return false;
|
|
1417
|
+
return true;
|
|
1418
|
+
}
|
|
1419
|
+
function validateUniqueCommandNames(names) {
|
|
1420
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1421
|
+
for (const name of names) {
|
|
1422
|
+
if (seen.has(name)) throw new TypeError(`Command has a duplicate name: "${name}".`);
|
|
1423
|
+
seen.add(name);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
|
|
1393
1427
|
if (isSuggestionHidden(options.hidden)) return;
|
|
1394
1428
|
const state = normalizeCommandState(context.state);
|
|
1395
1429
|
if (state === void 0) {
|
|
1396
|
-
if (
|
|
1430
|
+
for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
|
|
1397
1431
|
kind: "literal",
|
|
1398
|
-
text:
|
|
1432
|
+
text: commandName,
|
|
1399
1433
|
...options.description && { description: options.description }
|
|
1400
1434
|
};
|
|
1401
1435
|
} else if (state[0] === "matched") yield* parser.suggest(withChildContext(context, name, getCommandChildState(context.state, parser.initialState, parser), parser.usage), prefix);
|
|
1402
1436
|
else if (state[0] === "parsing") yield* parser.suggest(withChildContext(context, name, getCommandChildState(context.state, state[1], parser), parser.usage), prefix);
|
|
1403
1437
|
}
|
|
1404
|
-
async function* suggestCommandAsync(context, prefix, name, parser, options) {
|
|
1438
|
+
async function* suggestCommandAsync(context, prefix, name, aliases, parser, options) {
|
|
1405
1439
|
if (isSuggestionHidden(options.hidden)) return;
|
|
1406
1440
|
const state = normalizeCommandState(context.state);
|
|
1407
1441
|
if (state === void 0) {
|
|
1408
|
-
if (
|
|
1442
|
+
for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
|
|
1409
1443
|
kind: "literal",
|
|
1410
|
-
text:
|
|
1444
|
+
text: commandName,
|
|
1411
1445
|
...options.description && { description: options.description }
|
|
1412
1446
|
};
|
|
1413
1447
|
} else if (state[0] === "matched") {
|
|
@@ -1435,7 +1469,11 @@ async function* suggestCommandAsync(context, prefix, name, parser, options) {
|
|
|
1435
1469
|
* embedded whitespace, or contains control characters.
|
|
1436
1470
|
*/
|
|
1437
1471
|
function command(name, parser, options = {}) {
|
|
1438
|
-
|
|
1472
|
+
const commandNames = getCommandNames(name, options);
|
|
1473
|
+
const aliases = getVisibleCommandAliases(options);
|
|
1474
|
+
const hiddenAliases = getHiddenCommandAliases(options);
|
|
1475
|
+
validateCommandNames(commandNames, "Command");
|
|
1476
|
+
validateUniqueCommandNames(commandNames);
|
|
1439
1477
|
const isAsync = parser.mode === "async";
|
|
1440
1478
|
const syncInnerParser = parser;
|
|
1441
1479
|
const asyncInnerParser = parser;
|
|
@@ -1448,10 +1486,12 @@ function command(name, parser, options = {}) {
|
|
|
1448
1486
|
usage: [{
|
|
1449
1487
|
type: "command",
|
|
1450
1488
|
name,
|
|
1489
|
+
...aliases.length > 0 && { aliases },
|
|
1490
|
+
...hiddenAliases.length > 0 && { hiddenAliases },
|
|
1451
1491
|
...options.usageLine != null && { usageLine: options.usageLine },
|
|
1452
1492
|
...options.hidden != null && { hidden: options.hidden }
|
|
1453
1493
|
}, ...parser.usage],
|
|
1454
|
-
leadingNames: new Set(
|
|
1494
|
+
leadingNames: new Set(commandNames),
|
|
1455
1495
|
acceptingAnyToken: false,
|
|
1456
1496
|
initialState: void 0,
|
|
1457
1497
|
canSkip(state, exec) {
|
|
@@ -1474,10 +1514,11 @@ function command(name, parser, options = {}) {
|
|
|
1474
1514
|
parse(context) {
|
|
1475
1515
|
const state = normalizeCommandState(context.state);
|
|
1476
1516
|
if (state === void 0) {
|
|
1477
|
-
if (context.buffer.length < 1 || context.buffer[0]
|
|
1517
|
+
if (context.buffer.length < 1 || !commandNames.includes(context.buffer[0])) {
|
|
1478
1518
|
const actual = context.buffer.length > 0 ? context.buffer[0] : null;
|
|
1479
1519
|
const leadingCmds = extractLeadingCommandNames(context.usage);
|
|
1480
|
-
const
|
|
1520
|
+
const rawSuggestions = actual ? findSimilar(actual, leadingCmds, DEFAULT_FIND_SIMILAR_OPTIONS) : [];
|
|
1521
|
+
const suggestions = expandCommandAliasSuggestions(context.usage, rawSuggestions);
|
|
1481
1522
|
if (options.errors?.notMatched) {
|
|
1482
1523
|
const errorMessage = options.errors.notMatched;
|
|
1483
1524
|
return {
|
|
@@ -1608,8 +1649,8 @@ function command(name, parser, options = {}) {
|
|
|
1608
1649
|
return wrapForMode(parser.mode, null);
|
|
1609
1650
|
},
|
|
1610
1651
|
suggest(context, prefix) {
|
|
1611
|
-
if (isAsync) return suggestCommandAsync(context, prefix, name, parser, options);
|
|
1612
|
-
return suggestCommandSync(context, prefix, name, parser, options);
|
|
1652
|
+
if (isAsync) return suggestCommandAsync(context, prefix, name, aliases, parser, options);
|
|
1653
|
+
return suggestCommandSync(context, prefix, name, aliases, parser, options);
|
|
1613
1654
|
},
|
|
1614
1655
|
getDocFragments(state, defaultValue) {
|
|
1615
1656
|
const commandState = state.kind === "available" ? normalizeCommandState(state.state) : void 0;
|
package/dist/suggestion.cjs
CHANGED
|
@@ -141,6 +141,73 @@ function createSuggestionMessage(suggestions) {
|
|
|
141
141
|
return messageParts;
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
|
+
* Expands command alias suggestions so an alias typo can point at both the
|
|
145
|
+
* canonical command and the alias that matched.
|
|
146
|
+
*
|
|
147
|
+
* @param usage Usage terms that define command aliases.
|
|
148
|
+
* @param suggestions Candidate suggestions returned by {@link findSimilar}.
|
|
149
|
+
* @returns Suggestions with alias hits expanded to canonical name + alias.
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
152
|
+
function expandCommandAliasSuggestions(usage, suggestions) {
|
|
153
|
+
if (suggestions.length === 0) return suggestions;
|
|
154
|
+
const commandAliasTargets = collectCommandAliasTargets(usage);
|
|
155
|
+
const expanded = [];
|
|
156
|
+
const seen = /* @__PURE__ */ new Set();
|
|
157
|
+
for (const suggestion of suggestions) {
|
|
158
|
+
const targets = commandAliasTargets.get(suggestion) ?? [suggestion];
|
|
159
|
+
for (const target of targets) {
|
|
160
|
+
if (seen.has(target)) continue;
|
|
161
|
+
seen.add(target);
|
|
162
|
+
expanded.push(target);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return expanded;
|
|
166
|
+
}
|
|
167
|
+
function collectCommandAliasTargets(usage) {
|
|
168
|
+
const targets = /* @__PURE__ */ new Map();
|
|
169
|
+
function traverse(terms) {
|
|
170
|
+
if (!terms || !Array.isArray(terms)) return true;
|
|
171
|
+
for (const term of terms) {
|
|
172
|
+
if (term.type === "option") continue;
|
|
173
|
+
if (term.type === "argument") return false;
|
|
174
|
+
if (term.type === "command") {
|
|
175
|
+
if (require_usage.isSuggestionHidden(term.hidden)) return false;
|
|
176
|
+
if (!targets.has(term.name)) targets.set(term.name, [term.name]);
|
|
177
|
+
for (const alias of term.aliases ?? []) if (!targets.has(alias)) targets.set(alias, [term.name, alias]);
|
|
178
|
+
for (const alias of term.hiddenAliases ?? []) if (!targets.has(alias)) targets.set(alias, [term.name]);
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (term.type === "optional") {
|
|
182
|
+
traverse(term.terms);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (term.type === "multiple") {
|
|
186
|
+
const termsSkippable = traverse(term.terms);
|
|
187
|
+
if (term.min === 0 || termsSkippable) continue;
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (term.type === "sequence") {
|
|
191
|
+
if (traverse(term.terms)) continue;
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
if (term.type === "exclusive") {
|
|
195
|
+
let anySkippable = false;
|
|
196
|
+
for (const branch of term.terms) {
|
|
197
|
+
const branchSkippable = traverse(branch);
|
|
198
|
+
anySkippable = anySkippable || branchSkippable;
|
|
199
|
+
}
|
|
200
|
+
if (anySkippable) continue;
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
traverse(usage);
|
|
208
|
+
return targets;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
144
211
|
* Creates an error message with suggestions for similar options or commands.
|
|
145
212
|
*
|
|
146
213
|
* This is a convenience function that combines the functionality of
|
|
@@ -175,7 +242,8 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
|
|
|
175
242
|
if (type === "option" || type === "both") for (const name of require_usage.extractOptionNames(usage)) candidates.add(name);
|
|
176
243
|
if (type === "command" || type === "both") for (const name of require_usage.extractCommandNames(usage)) candidates.add(name);
|
|
177
244
|
const suggestions = findSimilar(invalidInput, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
|
|
178
|
-
const
|
|
245
|
+
const displaySuggestions = type === "option" ? suggestions : expandCommandAliasSuggestions(usage, suggestions);
|
|
246
|
+
const suggestionMsg = customFormatter ? customFormatter(displaySuggestions) : createSuggestionMessage(displaySuggestions);
|
|
179
247
|
return suggestionMsg.length > 0 ? [
|
|
180
248
|
...baseError,
|
|
181
249
|
require_message.text("\n\n"),
|
|
@@ -248,4 +316,6 @@ exports.DEFAULT_FIND_SIMILAR_OPTIONS = DEFAULT_FIND_SIMILAR_OPTIONS;
|
|
|
248
316
|
exports.createErrorWithSuggestions = createErrorWithSuggestions;
|
|
249
317
|
exports.createSuggestionMessage = createSuggestionMessage;
|
|
250
318
|
exports.deduplicateSuggestions = deduplicateSuggestions;
|
|
251
|
-
exports.
|
|
319
|
+
exports.expandCommandAliasSuggestions = expandCommandAliasSuggestions;
|
|
320
|
+
exports.findSimilar = findSimilar;
|
|
321
|
+
exports.levenshteinDistance = levenshteinDistance;
|