@optique/core 1.3.0-dev.2352 → 1.3.0-dev.2357
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/constructs.cjs +124 -41
- package/dist/constructs.js +124 -41
- package/dist/facade.cjs +51 -37
- package/dist/facade.d.cts +15 -1
- package/dist/facade.d.ts +15 -1
- package/dist/facade.js +52 -38
- package/package.json +2 -2
- package/skills/optique/SKILL.md +3 -0
package/dist/constructs.cjs
CHANGED
|
@@ -582,6 +582,35 @@ function createExclusiveState(parentState, index, parser, result) {
|
|
|
582
582
|
return require_internal_annotations.annotateFreshArray(parentState, [index, annotatedResult]);
|
|
583
583
|
}
|
|
584
584
|
/**
|
|
585
|
+
* Detects a branch result that handled only the global options terminator.
|
|
586
|
+
* Such a result advances the shared parser context, but does not identify
|
|
587
|
+
* which exclusive branch matched.
|
|
588
|
+
* @internal
|
|
589
|
+
*/
|
|
590
|
+
function isOptionsTerminatorOnlyResult(context, result) {
|
|
591
|
+
return !context.optionsTerminated && context.buffer[0] === "--" && result.success && result.next.optionsTerminated && result.consumed.length === 1 && result.consumed[0] === "--";
|
|
592
|
+
}
|
|
593
|
+
function isUnselectedBooleanOptionState(state) {
|
|
594
|
+
return state != null && typeof state === "object" && "success" in state && state.success === true && "value" in state && state.value === false;
|
|
595
|
+
}
|
|
596
|
+
function preserveExclusiveStateAfterOptionsTerminator(context, result) {
|
|
597
|
+
const mergedExec = require_execution_context.mergeChildExec(context.exec, result.next.exec);
|
|
598
|
+
return {
|
|
599
|
+
success: true,
|
|
600
|
+
next: {
|
|
601
|
+
...context,
|
|
602
|
+
buffer: result.next.buffer,
|
|
603
|
+
optionsTerminated: true,
|
|
604
|
+
state: context.state,
|
|
605
|
+
...mergedExec != null ? {
|
|
606
|
+
exec: mergedExec,
|
|
607
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
608
|
+
} : {}
|
|
609
|
+
},
|
|
610
|
+
consumed: result.consumed
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
585
614
|
* Creates a complete() method shared by or() and longestMatch().
|
|
586
615
|
* @internal
|
|
587
616
|
*/
|
|
@@ -819,9 +848,15 @@ function or(...args) {
|
|
|
819
848
|
let zeroConsumedBranch = null;
|
|
820
849
|
let zeroConsumedCount = 0;
|
|
821
850
|
let provisionalConsuming = null;
|
|
851
|
+
let optionsTerminatorResult = null;
|
|
822
852
|
let provisionalAmbiguous = false;
|
|
823
853
|
for (const [parser, i] of orderedParsers) {
|
|
824
854
|
const result = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
|
|
855
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
856
|
+
if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
|
|
857
|
+
optionsTerminatorResult ??= result;
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
825
860
|
if (result.success && result.consumed.length > 0) {
|
|
826
861
|
if (result.provisional) {
|
|
827
862
|
const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
|
|
@@ -1002,6 +1037,7 @@ function or(...args) {
|
|
|
1002
1037
|
}
|
|
1003
1038
|
}
|
|
1004
1039
|
}
|
|
1040
|
+
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
1005
1041
|
return {
|
|
1006
1042
|
...error,
|
|
1007
1043
|
success: false
|
|
@@ -1015,10 +1051,16 @@ function or(...args) {
|
|
|
1015
1051
|
let zeroConsumedBranch = null;
|
|
1016
1052
|
let zeroConsumedCount = 0;
|
|
1017
1053
|
let provisionalConsuming = null;
|
|
1054
|
+
let optionsTerminatorResult = null;
|
|
1018
1055
|
let provisionalAmbiguous = false;
|
|
1019
1056
|
for (const [parser, i] of orderedParsers) {
|
|
1020
1057
|
const resultOrPromise = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
|
|
1021
1058
|
const result = await resultOrPromise;
|
|
1059
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1060
|
+
if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
|
|
1061
|
+
optionsTerminatorResult ??= result;
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1022
1064
|
if (result.success && result.consumed.length > 0) {
|
|
1023
1065
|
if (result.provisional) {
|
|
1024
1066
|
const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
|
|
@@ -1201,6 +1243,7 @@ function or(...args) {
|
|
|
1201
1243
|
}
|
|
1202
1244
|
}
|
|
1203
1245
|
}
|
|
1246
|
+
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
1204
1247
|
return {
|
|
1205
1248
|
...error,
|
|
1206
1249
|
success: false
|
|
@@ -1302,82 +1345,122 @@ function createLongestMatch(...args) {
|
|
|
1302
1345
|
return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
|
|
1303
1346
|
})()
|
|
1304
1347
|
});
|
|
1305
|
-
const
|
|
1348
|
+
const commitLongestMatch = (context, match, optionsTerminatorResult) => {
|
|
1349
|
+
const selectedResult = optionsTerminatorResult == null ? match.result : {
|
|
1350
|
+
...match.result,
|
|
1351
|
+
next: {
|
|
1352
|
+
...match.result.next,
|
|
1353
|
+
buffer: optionsTerminatorResult.next.buffer,
|
|
1354
|
+
optionsTerminated: true
|
|
1355
|
+
},
|
|
1356
|
+
consumed: [...optionsTerminatorResult.consumed, ...match.result.consumed]
|
|
1357
|
+
};
|
|
1358
|
+
const parser = parsers[match.index];
|
|
1359
|
+
const mergedExec = require_execution_context.mergeChildExec(context.exec, selectedResult.next.exec);
|
|
1360
|
+
return {
|
|
1361
|
+
success: true,
|
|
1362
|
+
next: {
|
|
1363
|
+
...context,
|
|
1364
|
+
buffer: selectedResult.next.buffer,
|
|
1365
|
+
optionsTerminated: selectedResult.next.optionsTerminated,
|
|
1366
|
+
state: createExclusiveState(context.state, match.index, parser, selectedResult),
|
|
1367
|
+
...mergedExec != null ? {
|
|
1368
|
+
exec: mergedExec,
|
|
1369
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1370
|
+
} : {}
|
|
1371
|
+
},
|
|
1372
|
+
consumed: selectedResult.consumed
|
|
1373
|
+
};
|
|
1374
|
+
};
|
|
1375
|
+
const resolveLongestMatchOptionsTerminator = (context, bestMatch, optionsTerminatorMatches) => {
|
|
1376
|
+
if (optionsTerminatorMatches.length < 1) return null;
|
|
1377
|
+
if (bestMatch != null && bestMatch.consumed > 0) return null;
|
|
1378
|
+
const activeState = normalizeExclusiveState(context.state);
|
|
1379
|
+
if (activeState != null && activeState[1].success && activeState[1].consumed.length > 0) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
|
|
1380
|
+
let selected = bestMatch == null ? null : {
|
|
1381
|
+
match: bestMatch,
|
|
1382
|
+
fromTerminator: false
|
|
1383
|
+
};
|
|
1384
|
+
for (const match of optionsTerminatorMatches) {
|
|
1385
|
+
if (!match.selectedAfterTerminator && (extractRequiredUsage(parsers[match.index].usage).length > 0 || isUnselectedBooleanOptionState(match.result.next.state))) continue;
|
|
1386
|
+
const selectedIsProvisional = selected != null && !!selected.match.result.provisional;
|
|
1387
|
+
const matchIsProvisional = !!match.result.provisional;
|
|
1388
|
+
if (selected == null || selectedIsProvisional && !matchIsProvisional || selectedIsProvisional === matchIsProvisional && match.index < selected.match.index) selected = {
|
|
1389
|
+
match,
|
|
1390
|
+
fromTerminator: true
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
if (selected != null) return commitLongestMatch(context, selected.match, selected.fromTerminator ? void 0 : optionsTerminatorMatches[0].result);
|
|
1394
|
+
return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
|
|
1395
|
+
};
|
|
1396
|
+
const parseSyncBranches = (context) => {
|
|
1306
1397
|
let bestMatch = null;
|
|
1398
|
+
const optionsTerminatorMatches = [];
|
|
1307
1399
|
let error = getInitialError(context);
|
|
1308
1400
|
const activeState = normalizeExclusiveState(context.state);
|
|
1309
1401
|
for (let i = 0; i < syncParsers.length; i++) {
|
|
1310
1402
|
const parser = syncParsers[i];
|
|
1311
|
-
const
|
|
1403
|
+
const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
|
|
1404
|
+
const result = parser.parse(childContext);
|
|
1405
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1406
|
+
optionsTerminatorMatches.push({
|
|
1407
|
+
index: i,
|
|
1408
|
+
result,
|
|
1409
|
+
consumed: 1,
|
|
1410
|
+
...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
|
|
1411
|
+
});
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1312
1414
|
if (result.success) {
|
|
1313
1415
|
const consumed = context.buffer.length - result.next.buffer.length;
|
|
1314
1416
|
const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
|
|
1315
1417
|
if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
|
|
1316
1418
|
index: i,
|
|
1317
|
-
parser,
|
|
1318
1419
|
result,
|
|
1319
1420
|
consumed
|
|
1320
1421
|
};
|
|
1321
1422
|
} else if (error.consumed < result.consumed) error = result;
|
|
1322
1423
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
success: true,
|
|
1327
|
-
next: {
|
|
1328
|
-
...context,
|
|
1329
|
-
buffer: bestMatch.result.next.buffer,
|
|
1330
|
-
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
1331
|
-
state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
|
|
1332
|
-
...mergedExec != null ? {
|
|
1333
|
-
exec: mergedExec,
|
|
1334
|
-
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1335
|
-
} : {}
|
|
1336
|
-
},
|
|
1337
|
-
consumed: bestMatch.result.consumed
|
|
1338
|
-
};
|
|
1339
|
-
}
|
|
1424
|
+
const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
|
|
1425
|
+
if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
|
|
1426
|
+
if (bestMatch != null) return commitLongestMatch(context, bestMatch);
|
|
1340
1427
|
return {
|
|
1341
1428
|
...error,
|
|
1342
1429
|
success: false
|
|
1343
1430
|
};
|
|
1344
1431
|
};
|
|
1345
|
-
const
|
|
1432
|
+
const parseAsyncBranches = async (context) => {
|
|
1346
1433
|
let bestMatch = null;
|
|
1434
|
+
const optionsTerminatorMatches = [];
|
|
1347
1435
|
let error = getInitialError(context);
|
|
1348
1436
|
const activeState = normalizeExclusiveState(context.state);
|
|
1349
1437
|
for (let i = 0; i < parsers.length; i++) {
|
|
1350
1438
|
const parser = parsers[i];
|
|
1351
|
-
const
|
|
1439
|
+
const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
|
|
1440
|
+
const resultOrPromise = parser.parse(childContext);
|
|
1352
1441
|
const result = await resultOrPromise;
|
|
1442
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1443
|
+
optionsTerminatorMatches.push({
|
|
1444
|
+
index: i,
|
|
1445
|
+
result,
|
|
1446
|
+
consumed: 1,
|
|
1447
|
+
...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
|
|
1448
|
+
});
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1353
1451
|
if (result.success) {
|
|
1354
1452
|
const consumed = context.buffer.length - result.next.buffer.length;
|
|
1355
1453
|
const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
|
|
1356
1454
|
if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
|
|
1357
1455
|
index: i,
|
|
1358
|
-
parser,
|
|
1359
1456
|
result,
|
|
1360
1457
|
consumed
|
|
1361
1458
|
};
|
|
1362
1459
|
} else if (error.consumed < result.consumed) error = result;
|
|
1363
1460
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
success: true,
|
|
1368
|
-
next: {
|
|
1369
|
-
...context,
|
|
1370
|
-
buffer: bestMatch.result.next.buffer,
|
|
1371
|
-
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
1372
|
-
state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
|
|
1373
|
-
...mergedExec != null ? {
|
|
1374
|
-
exec: mergedExec,
|
|
1375
|
-
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1376
|
-
} : {}
|
|
1377
|
-
},
|
|
1378
|
-
consumed: bestMatch.result.consumed
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1461
|
+
const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
|
|
1462
|
+
if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
|
|
1463
|
+
if (bestMatch != null) return commitLongestMatch(context, bestMatch);
|
|
1381
1464
|
return {
|
|
1382
1465
|
...error,
|
|
1383
1466
|
success: false
|
|
@@ -1400,7 +1483,7 @@ function createLongestMatch(...args) {
|
|
|
1400
1483
|
return extractExclusivePhase2Seed(parsers, state, exec, combinedMode);
|
|
1401
1484
|
},
|
|
1402
1485
|
parse(context) {
|
|
1403
|
-
return require_mode_dispatch.dispatchByMode(combinedMode, () =>
|
|
1486
|
+
return require_mode_dispatch.dispatchByMode(combinedMode, () => parseSyncBranches(context), () => parseAsyncBranches(context));
|
|
1404
1487
|
},
|
|
1405
1488
|
getSuggestRuntimeNodes(state, path) {
|
|
1406
1489
|
return getExclusiveSuggestRuntimeNodes(parsers, state, path);
|
package/dist/constructs.js
CHANGED
|
@@ -582,6 +582,35 @@ function createExclusiveState(parentState, index, parser, result) {
|
|
|
582
582
|
return annotateFreshArray(parentState, [index, annotatedResult]);
|
|
583
583
|
}
|
|
584
584
|
/**
|
|
585
|
+
* Detects a branch result that handled only the global options terminator.
|
|
586
|
+
* Such a result advances the shared parser context, but does not identify
|
|
587
|
+
* which exclusive branch matched.
|
|
588
|
+
* @internal
|
|
589
|
+
*/
|
|
590
|
+
function isOptionsTerminatorOnlyResult(context, result) {
|
|
591
|
+
return !context.optionsTerminated && context.buffer[0] === "--" && result.success && result.next.optionsTerminated && result.consumed.length === 1 && result.consumed[0] === "--";
|
|
592
|
+
}
|
|
593
|
+
function isUnselectedBooleanOptionState(state) {
|
|
594
|
+
return state != null && typeof state === "object" && "success" in state && state.success === true && "value" in state && state.value === false;
|
|
595
|
+
}
|
|
596
|
+
function preserveExclusiveStateAfterOptionsTerminator(context, result) {
|
|
597
|
+
const mergedExec = mergeChildExec(context.exec, result.next.exec);
|
|
598
|
+
return {
|
|
599
|
+
success: true,
|
|
600
|
+
next: {
|
|
601
|
+
...context,
|
|
602
|
+
buffer: result.next.buffer,
|
|
603
|
+
optionsTerminated: true,
|
|
604
|
+
state: context.state,
|
|
605
|
+
...mergedExec != null ? {
|
|
606
|
+
exec: mergedExec,
|
|
607
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
608
|
+
} : {}
|
|
609
|
+
},
|
|
610
|
+
consumed: result.consumed
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
585
614
|
* Creates a complete() method shared by or() and longestMatch().
|
|
586
615
|
* @internal
|
|
587
616
|
*/
|
|
@@ -819,9 +848,15 @@ function or(...args) {
|
|
|
819
848
|
let zeroConsumedBranch = null;
|
|
820
849
|
let zeroConsumedCount = 0;
|
|
821
850
|
let provisionalConsuming = null;
|
|
851
|
+
let optionsTerminatorResult = null;
|
|
822
852
|
let provisionalAmbiguous = false;
|
|
823
853
|
for (const [parser, i] of orderedParsers) {
|
|
824
854
|
const result = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
|
|
855
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
856
|
+
if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
|
|
857
|
+
optionsTerminatorResult ??= result;
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
825
860
|
if (result.success && result.consumed.length > 0) {
|
|
826
861
|
if (result.provisional) {
|
|
827
862
|
const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
|
|
@@ -1002,6 +1037,7 @@ function or(...args) {
|
|
|
1002
1037
|
}
|
|
1003
1038
|
}
|
|
1004
1039
|
}
|
|
1040
|
+
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
1005
1041
|
return {
|
|
1006
1042
|
...error,
|
|
1007
1043
|
success: false
|
|
@@ -1015,10 +1051,16 @@ function or(...args) {
|
|
|
1015
1051
|
let zeroConsumedBranch = null;
|
|
1016
1052
|
let zeroConsumedCount = 0;
|
|
1017
1053
|
let provisionalConsuming = null;
|
|
1054
|
+
let optionsTerminatorResult = null;
|
|
1018
1055
|
let provisionalAmbiguous = false;
|
|
1019
1056
|
for (const [parser, i] of orderedParsers) {
|
|
1020
1057
|
const resultOrPromise = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
|
|
1021
1058
|
const result = await resultOrPromise;
|
|
1059
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1060
|
+
if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
|
|
1061
|
+
optionsTerminatorResult ??= result;
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1022
1064
|
if (result.success && result.consumed.length > 0) {
|
|
1023
1065
|
if (result.provisional) {
|
|
1024
1066
|
const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
|
|
@@ -1201,6 +1243,7 @@ function or(...args) {
|
|
|
1201
1243
|
}
|
|
1202
1244
|
}
|
|
1203
1245
|
}
|
|
1246
|
+
if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
|
|
1204
1247
|
return {
|
|
1205
1248
|
...error,
|
|
1206
1249
|
success: false
|
|
@@ -1302,82 +1345,122 @@ function createLongestMatch(...args) {
|
|
|
1302
1345
|
return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
|
|
1303
1346
|
})()
|
|
1304
1347
|
});
|
|
1305
|
-
const
|
|
1348
|
+
const commitLongestMatch = (context, match, optionsTerminatorResult) => {
|
|
1349
|
+
const selectedResult = optionsTerminatorResult == null ? match.result : {
|
|
1350
|
+
...match.result,
|
|
1351
|
+
next: {
|
|
1352
|
+
...match.result.next,
|
|
1353
|
+
buffer: optionsTerminatorResult.next.buffer,
|
|
1354
|
+
optionsTerminated: true
|
|
1355
|
+
},
|
|
1356
|
+
consumed: [...optionsTerminatorResult.consumed, ...match.result.consumed]
|
|
1357
|
+
};
|
|
1358
|
+
const parser = parsers[match.index];
|
|
1359
|
+
const mergedExec = mergeChildExec(context.exec, selectedResult.next.exec);
|
|
1360
|
+
return {
|
|
1361
|
+
success: true,
|
|
1362
|
+
next: {
|
|
1363
|
+
...context,
|
|
1364
|
+
buffer: selectedResult.next.buffer,
|
|
1365
|
+
optionsTerminated: selectedResult.next.optionsTerminated,
|
|
1366
|
+
state: createExclusiveState(context.state, match.index, parser, selectedResult),
|
|
1367
|
+
...mergedExec != null ? {
|
|
1368
|
+
exec: mergedExec,
|
|
1369
|
+
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1370
|
+
} : {}
|
|
1371
|
+
},
|
|
1372
|
+
consumed: selectedResult.consumed
|
|
1373
|
+
};
|
|
1374
|
+
};
|
|
1375
|
+
const resolveLongestMatchOptionsTerminator = (context, bestMatch, optionsTerminatorMatches) => {
|
|
1376
|
+
if (optionsTerminatorMatches.length < 1) return null;
|
|
1377
|
+
if (bestMatch != null && bestMatch.consumed > 0) return null;
|
|
1378
|
+
const activeState = normalizeExclusiveState(context.state);
|
|
1379
|
+
if (activeState != null && activeState[1].success && activeState[1].consumed.length > 0) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
|
|
1380
|
+
let selected = bestMatch == null ? null : {
|
|
1381
|
+
match: bestMatch,
|
|
1382
|
+
fromTerminator: false
|
|
1383
|
+
};
|
|
1384
|
+
for (const match of optionsTerminatorMatches) {
|
|
1385
|
+
if (!match.selectedAfterTerminator && (extractRequiredUsage(parsers[match.index].usage).length > 0 || isUnselectedBooleanOptionState(match.result.next.state))) continue;
|
|
1386
|
+
const selectedIsProvisional = selected != null && !!selected.match.result.provisional;
|
|
1387
|
+
const matchIsProvisional = !!match.result.provisional;
|
|
1388
|
+
if (selected == null || selectedIsProvisional && !matchIsProvisional || selectedIsProvisional === matchIsProvisional && match.index < selected.match.index) selected = {
|
|
1389
|
+
match,
|
|
1390
|
+
fromTerminator: true
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
if (selected != null) return commitLongestMatch(context, selected.match, selected.fromTerminator ? void 0 : optionsTerminatorMatches[0].result);
|
|
1394
|
+
return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
|
|
1395
|
+
};
|
|
1396
|
+
const parseSyncBranches = (context) => {
|
|
1306
1397
|
let bestMatch = null;
|
|
1398
|
+
const optionsTerminatorMatches = [];
|
|
1307
1399
|
let error = getInitialError(context);
|
|
1308
1400
|
const activeState = normalizeExclusiveState(context.state);
|
|
1309
1401
|
for (let i = 0; i < syncParsers.length; i++) {
|
|
1310
1402
|
const parser = syncParsers[i];
|
|
1311
|
-
const
|
|
1403
|
+
const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
|
|
1404
|
+
const result = parser.parse(childContext);
|
|
1405
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1406
|
+
optionsTerminatorMatches.push({
|
|
1407
|
+
index: i,
|
|
1408
|
+
result,
|
|
1409
|
+
consumed: 1,
|
|
1410
|
+
...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
|
|
1411
|
+
});
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1312
1414
|
if (result.success) {
|
|
1313
1415
|
const consumed = context.buffer.length - result.next.buffer.length;
|
|
1314
1416
|
const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
|
|
1315
1417
|
if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
|
|
1316
1418
|
index: i,
|
|
1317
|
-
parser,
|
|
1318
1419
|
result,
|
|
1319
1420
|
consumed
|
|
1320
1421
|
};
|
|
1321
1422
|
} else if (error.consumed < result.consumed) error = result;
|
|
1322
1423
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
success: true,
|
|
1327
|
-
next: {
|
|
1328
|
-
...context,
|
|
1329
|
-
buffer: bestMatch.result.next.buffer,
|
|
1330
|
-
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
1331
|
-
state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
|
|
1332
|
-
...mergedExec != null ? {
|
|
1333
|
-
exec: mergedExec,
|
|
1334
|
-
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1335
|
-
} : {}
|
|
1336
|
-
},
|
|
1337
|
-
consumed: bestMatch.result.consumed
|
|
1338
|
-
};
|
|
1339
|
-
}
|
|
1424
|
+
const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
|
|
1425
|
+
if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
|
|
1426
|
+
if (bestMatch != null) return commitLongestMatch(context, bestMatch);
|
|
1340
1427
|
return {
|
|
1341
1428
|
...error,
|
|
1342
1429
|
success: false
|
|
1343
1430
|
};
|
|
1344
1431
|
};
|
|
1345
|
-
const
|
|
1432
|
+
const parseAsyncBranches = async (context) => {
|
|
1346
1433
|
let bestMatch = null;
|
|
1434
|
+
const optionsTerminatorMatches = [];
|
|
1347
1435
|
let error = getInitialError(context);
|
|
1348
1436
|
const activeState = normalizeExclusiveState(context.state);
|
|
1349
1437
|
for (let i = 0; i < parsers.length; i++) {
|
|
1350
1438
|
const parser = parsers[i];
|
|
1351
|
-
const
|
|
1439
|
+
const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
|
|
1440
|
+
const resultOrPromise = parser.parse(childContext);
|
|
1352
1441
|
const result = await resultOrPromise;
|
|
1442
|
+
if (isOptionsTerminatorOnlyResult(context, result)) {
|
|
1443
|
+
optionsTerminatorMatches.push({
|
|
1444
|
+
index: i,
|
|
1445
|
+
result,
|
|
1446
|
+
consumed: 1,
|
|
1447
|
+
...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
|
|
1448
|
+
});
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1353
1451
|
if (result.success) {
|
|
1354
1452
|
const consumed = context.buffer.length - result.next.buffer.length;
|
|
1355
1453
|
const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
|
|
1356
1454
|
if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
|
|
1357
1455
|
index: i,
|
|
1358
|
-
parser,
|
|
1359
1456
|
result,
|
|
1360
1457
|
consumed
|
|
1361
1458
|
};
|
|
1362
1459
|
} else if (error.consumed < result.consumed) error = result;
|
|
1363
1460
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
success: true,
|
|
1368
|
-
next: {
|
|
1369
|
-
...context,
|
|
1370
|
-
buffer: bestMatch.result.next.buffer,
|
|
1371
|
-
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
1372
|
-
state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
|
|
1373
|
-
...mergedExec != null ? {
|
|
1374
|
-
exec: mergedExec,
|
|
1375
|
-
dependencyRegistry: mergedExec.dependencyRegistry
|
|
1376
|
-
} : {}
|
|
1377
|
-
},
|
|
1378
|
-
consumed: bestMatch.result.consumed
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1461
|
+
const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
|
|
1462
|
+
if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
|
|
1463
|
+
if (bestMatch != null) return commitLongestMatch(context, bestMatch);
|
|
1381
1464
|
return {
|
|
1382
1465
|
...error,
|
|
1383
1466
|
success: false
|
|
@@ -1400,7 +1483,7 @@ function createLongestMatch(...args) {
|
|
|
1400
1483
|
return extractExclusivePhase2Seed(parsers, state, exec, combinedMode);
|
|
1401
1484
|
},
|
|
1402
1485
|
parse(context) {
|
|
1403
|
-
return dispatchByMode(combinedMode, () =>
|
|
1486
|
+
return dispatchByMode(combinedMode, () => parseSyncBranches(context), () => parseAsyncBranches(context));
|
|
1404
1487
|
},
|
|
1405
1488
|
getSuggestRuntimeNodes(state, path) {
|
|
1406
1489
|
return getExclusiveSuggestRuntimeNodes(parsers, state, path);
|
package/dist/facade.cjs
CHANGED
|
@@ -952,7 +952,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
952
952
|
options = optionsParam ?? {};
|
|
953
953
|
}
|
|
954
954
|
require_validate.validateProgramName(programName);
|
|
955
|
-
const { colors, maxWidth, showDefault, showChoices, sectionOrder, showUsage, commandList = "recursive", aboveError = "usage", onError = () => {
|
|
955
|
+
const { colors, maxWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
|
|
956
956
|
throw new RunParserError("Failed to parse command line arguments.");
|
|
957
957
|
}, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
|
|
958
958
|
const norm = (c) => c === true ? {} : c;
|
|
@@ -1045,6 +1045,41 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1045
1045
|
completionCommandGroup: completionCommandConfig?.group,
|
|
1046
1046
|
completionOptionGroup: completionOptionConfig?.group
|
|
1047
1047
|
});
|
|
1048
|
+
const helpAsCommand = helpCommandConfig != null;
|
|
1049
|
+
const versionAsCommand = versionCommandConfig != null;
|
|
1050
|
+
const completionAsCommand = completionCommandConfig != null;
|
|
1051
|
+
const helpAsOption = helpOptionConfig != null;
|
|
1052
|
+
const versionAsOption = versionOptionConfig != null;
|
|
1053
|
+
const completionAsOption = completionOptionConfig != null;
|
|
1054
|
+
let cachedRootHelpGeneratorParser;
|
|
1055
|
+
const getRootHelpGeneratorParser = () => {
|
|
1056
|
+
if (cachedRootHelpGeneratorParser != null) return cachedRootHelpGeneratorParser;
|
|
1057
|
+
const commandParsers = [parser];
|
|
1058
|
+
const groupedMeta = {};
|
|
1059
|
+
const ungroupedMeta = [];
|
|
1060
|
+
const addMeta = (metaParser, groupLabel) => {
|
|
1061
|
+
if (groupLabel) (groupedMeta[groupLabel] ??= []).push(metaParser);
|
|
1062
|
+
else ungroupedMeta.push(metaParser);
|
|
1063
|
+
};
|
|
1064
|
+
if (helpAsCommand && helpParsers.helpCommand) addMeta(helpParsers.helpCommand, helpCommandConfig?.group);
|
|
1065
|
+
if (versionAsCommand && versionParsers.versionCommand) addMeta(versionParsers.versionCommand, versionCommandConfig?.group);
|
|
1066
|
+
if (completionAsCommand && completionParsers.completionCommand) addMeta(completionParsers.completionCommand, completionCommandConfig?.group);
|
|
1067
|
+
commandParsers.push(...ungroupedMeta);
|
|
1068
|
+
for (const [label, parsers] of Object.entries(groupedMeta)) commandParsers.push(require_constructs.group(label, parsers.length === 1 ? parsers[0] : require_constructs.longestMatch(...parsers)));
|
|
1069
|
+
const groupedMetaOptions = {};
|
|
1070
|
+
const ungroupedMetaOptions = [];
|
|
1071
|
+
const addMetaOption = (metaParser, groupLabel) => {
|
|
1072
|
+
if (groupLabel) (groupedMetaOptions[groupLabel] ??= []).push(metaParser);
|
|
1073
|
+
else ungroupedMetaOptions.push(metaParser);
|
|
1074
|
+
};
|
|
1075
|
+
if (helpAsOption && helpParsers.helpOption) addMetaOption(helpParsers.helpOption, helpOptionConfig?.group);
|
|
1076
|
+
if (versionAsOption && versionParsers.versionOption) addMetaOption(versionParsers.versionOption, versionOptionConfig?.group);
|
|
1077
|
+
if (completionAsOption && completionParsers.completionOption) addMetaOption(completionParsers.completionOption, completionOptionConfig?.group);
|
|
1078
|
+
commandParsers.push(...ungroupedMetaOptions);
|
|
1079
|
+
for (const [label, parsers] of Object.entries(groupedMetaOptions)) commandParsers.push(require_constructs.group(label, parsers.length === 1 ? parsers[0] : require_constructs.longestMatch(...parsers)));
|
|
1080
|
+
cachedRootHelpGeneratorParser = commandParsers.length === 1 ? commandParsers[0] : longestMatchForMetaCommands(...commandParsers);
|
|
1081
|
+
return cachedRootHelpGeneratorParser;
|
|
1082
|
+
};
|
|
1048
1083
|
const handleResult = (classified) => {
|
|
1049
1084
|
switch (classified.type) {
|
|
1050
1085
|
case "success": return classified.value;
|
|
@@ -1059,12 +1094,6 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1059
1094
|
case "help": {
|
|
1060
1095
|
let helpGeneratorParser;
|
|
1061
1096
|
let docGeneratorParser;
|
|
1062
|
-
const helpAsCommand = helpCommandConfig != null;
|
|
1063
|
-
const versionAsCommand = versionCommandConfig != null;
|
|
1064
|
-
const completionAsCommand = completionCommandConfig != null;
|
|
1065
|
-
const helpAsOption = helpOptionConfig != null;
|
|
1066
|
-
const versionAsOption = versionOptionConfig != null;
|
|
1067
|
-
const completionAsOption = completionOptionConfig != null;
|
|
1068
1097
|
const requestedCommand = classified.commands[0];
|
|
1069
1098
|
if (requestedCommand != null && !classified.preferUserCommandDocs && completionCommandNames.includes(requestedCommand) && completionAsCommand && completionParsers.completionCommand) {
|
|
1070
1099
|
helpGeneratorParser = completionParsers.completionCommand;
|
|
@@ -1076,33 +1105,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1076
1105
|
helpGeneratorParser = versionParsers.versionCommand;
|
|
1077
1106
|
docGeneratorParser = helpGeneratorParser;
|
|
1078
1107
|
} else {
|
|
1079
|
-
|
|
1080
|
-
const groupedMeta = {};
|
|
1081
|
-
const ungroupedMeta = [];
|
|
1082
|
-
const addMeta = (p, groupLabel) => {
|
|
1083
|
-
if (groupLabel) (groupedMeta[groupLabel] ??= []).push(p);
|
|
1084
|
-
else ungroupedMeta.push(p);
|
|
1085
|
-
};
|
|
1086
|
-
if (helpAsCommand && helpParsers.helpCommand) addMeta(helpParsers.helpCommand, helpCommandConfig?.group);
|
|
1087
|
-
if (versionAsCommand && versionParsers.versionCommand) addMeta(versionParsers.versionCommand, versionCommandConfig?.group);
|
|
1088
|
-
if (completionAsCommand && completionParsers.completionCommand) addMeta(completionParsers.completionCommand, completionCommandConfig?.group);
|
|
1089
|
-
commandParsers.push(...ungroupedMeta);
|
|
1090
|
-
for (const [label, parsers] of Object.entries(groupedMeta)) if (parsers.length === 1) commandParsers.push(require_constructs.group(label, parsers[0]));
|
|
1091
|
-
else commandParsers.push(require_constructs.group(label, require_constructs.longestMatch(...parsers)));
|
|
1092
|
-
const groupedMetaOptions = {};
|
|
1093
|
-
const ungroupedMetaOptions = [];
|
|
1094
|
-
const addMetaOption = (p, groupLabel) => {
|
|
1095
|
-
if (groupLabel) (groupedMetaOptions[groupLabel] ??= []).push(p);
|
|
1096
|
-
else ungroupedMetaOptions.push(p);
|
|
1097
|
-
};
|
|
1098
|
-
if (helpAsOption && helpParsers.helpOption) addMetaOption(helpParsers.helpOption, helpOptionConfig?.group);
|
|
1099
|
-
if (versionAsOption && versionParsers.versionOption) addMetaOption(versionParsers.versionOption, versionOptionConfig?.group);
|
|
1100
|
-
if (completionAsOption && completionParsers.completionOption) addMetaOption(completionParsers.completionOption, completionOptionConfig?.group);
|
|
1101
|
-
commandParsers.push(...ungroupedMetaOptions);
|
|
1102
|
-
for (const [label, optParsers] of Object.entries(groupedMetaOptions)) if (optParsers.length === 1) commandParsers.push(require_constructs.group(label, optParsers[0]));
|
|
1103
|
-
else commandParsers.push(require_constructs.group(label, require_constructs.longestMatch(...optParsers)));
|
|
1104
|
-
if (commandParsers.length === 1) helpGeneratorParser = commandParsers[0];
|
|
1105
|
-
else helpGeneratorParser = longestMatchForMetaCommands(...commandParsers);
|
|
1108
|
+
helpGeneratorParser = getRootHelpGeneratorParser();
|
|
1106
1109
|
docGeneratorParser = classified.commands.length > 0 ? parser : helpGeneratorParser;
|
|
1107
1110
|
}
|
|
1108
1111
|
const reportInvalidHelpCommand = (validationError) => {
|
|
@@ -1133,7 +1136,8 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1133
1136
|
bugs: isTopLevel && !isMetaCommandHelp ? bugs ?? doc.bugs : void 0,
|
|
1134
1137
|
footer: shouldOverride ? footer ?? doc.footer : doc.footer ?? footer
|
|
1135
1138
|
}, commandList, isTopLevel);
|
|
1136
|
-
|
|
1139
|
+
const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine) : augmentedDoc;
|
|
1140
|
+
stdout(require_doc.formatDocPage(programName, renderedDoc, {
|
|
1137
1141
|
colors,
|
|
1138
1142
|
maxWidth,
|
|
1139
1143
|
showDefault,
|
|
@@ -1193,6 +1197,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1193
1197
|
let effectiveAboveError = currentAboveError;
|
|
1194
1198
|
if (effectiveAboveError === "help") if (doc == null) effectiveAboveError = "usage";
|
|
1195
1199
|
else {
|
|
1200
|
+
const isTopLevel = classified.commandPath.length < 1;
|
|
1196
1201
|
const augmentedDoc = maybeCollapseCommandList({
|
|
1197
1202
|
...doc,
|
|
1198
1203
|
brief: brief ?? doc.brief,
|
|
@@ -1201,8 +1206,10 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1201
1206
|
author: author ?? doc.author,
|
|
1202
1207
|
bugs: bugs ?? doc.bugs,
|
|
1203
1208
|
footer: footer ?? doc.footer
|
|
1204
|
-
}, commandList,
|
|
1205
|
-
|
|
1209
|
+
}, commandList, isTopLevel);
|
|
1210
|
+
const defaultRootUsage = typeof usageLine === "function" && (options.help || options.version || options.completion) ? require_usage.normalizeUsage(getRootHelpGeneratorParser().usage) : augmentedDoc.usage ?? [];
|
|
1211
|
+
const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine, defaultRootUsage) : augmentedDoc;
|
|
1212
|
+
stderr(require_doc.formatDocPage(programName, renderedDoc, {
|
|
1206
1213
|
colors,
|
|
1207
1214
|
maxWidth,
|
|
1208
1215
|
showDefault,
|
|
@@ -1298,6 +1305,13 @@ function runParserAsync(parser, programName, args, options) {
|
|
|
1298
1305
|
const result = runParser(parser, programName, args, options);
|
|
1299
1306
|
return Promise.resolve(result);
|
|
1300
1307
|
}
|
|
1308
|
+
function applyUsageLine(doc, usageLine, defaultUsageLine = doc.usage ?? []) {
|
|
1309
|
+
const customUsageLine = typeof usageLine === "function" ? usageLine(require_usage.cloneUsage(defaultUsageLine)) : usageLine;
|
|
1310
|
+
return {
|
|
1311
|
+
...doc,
|
|
1312
|
+
usage: require_usage.normalizeUsage(customUsageLine)
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1301
1315
|
function maybeCollapseCommandList(doc, commandList, isTopLevel) {
|
|
1302
1316
|
if (commandList !== "top-level" || !isTopLevel) return doc;
|
|
1303
1317
|
return {
|
package/dist/facade.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Message } from "./message.cjs";
|
|
2
|
-
import { HiddenVisibility, OptionName } from "./usage.cjs";
|
|
2
|
+
import { HiddenVisibility, OptionName, Usage } from "./usage.cjs";
|
|
3
3
|
import { DocSection, ShowChoicesOptions, ShowDefaultOptions } from "./doc.cjs";
|
|
4
4
|
import { InferMode, InferValue, Mode, ModeValue, Parser } from "./internal/parser.cjs";
|
|
5
5
|
import { ShellCompletion } from "./completion.cjs";
|
|
@@ -127,6 +127,20 @@ interface RunOptions<THelp, TError> {
|
|
|
127
127
|
* @since 1.2.0
|
|
128
128
|
*/
|
|
129
129
|
readonly showUsage?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Usage line override for top-level full help.
|
|
132
|
+
*
|
|
133
|
+
* This option customizes the usage shown by `--help`, the root help command,
|
|
134
|
+
* and `aboveError: "help"` at the root. It does not affect subcommand help,
|
|
135
|
+
* parsing, shell completion, or usage-only error preambles from
|
|
136
|
+
* `aboveError: "usage"`.
|
|
137
|
+
*
|
|
138
|
+
* The callback form receives the generated root usage after built-in help,
|
|
139
|
+
* version, and completion entries have been added.
|
|
140
|
+
*
|
|
141
|
+
* @since 1.3.0
|
|
142
|
+
*/
|
|
143
|
+
readonly usageLine?: Usage | ((defaultUsageLine: Usage) => Usage);
|
|
130
144
|
/**
|
|
131
145
|
* How to render command lists in top-level help pages.
|
|
132
146
|
*
|
package/dist/facade.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Message } from "./message.js";
|
|
2
|
-
import { HiddenVisibility, OptionName } from "./usage.js";
|
|
2
|
+
import { HiddenVisibility, OptionName, Usage } from "./usage.js";
|
|
3
3
|
import { DocSection, ShowChoicesOptions, ShowDefaultOptions } from "./doc.js";
|
|
4
4
|
import { InferMode, InferValue, Mode, ModeValue, Parser } from "./internal/parser.js";
|
|
5
5
|
import { ShellCompletion } from "./completion.js";
|
|
@@ -127,6 +127,20 @@ interface RunOptions<THelp, TError> {
|
|
|
127
127
|
* @since 1.2.0
|
|
128
128
|
*/
|
|
129
129
|
readonly showUsage?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Usage line override for top-level full help.
|
|
132
|
+
*
|
|
133
|
+
* This option customizes the usage shown by `--help`, the root help command,
|
|
134
|
+
* and `aboveError: "help"` at the root. It does not affect subcommand help,
|
|
135
|
+
* parsing, shell completion, or usage-only error preambles from
|
|
136
|
+
* `aboveError: "usage"`.
|
|
137
|
+
*
|
|
138
|
+
* The callback form receives the generated root usage after built-in help,
|
|
139
|
+
* version, and completion entries have been added.
|
|
140
|
+
*
|
|
141
|
+
* @since 1.3.0
|
|
142
|
+
*/
|
|
143
|
+
readonly usageLine?: Usage | ((defaultUsageLine: Usage) => Usage);
|
|
130
144
|
/**
|
|
131
145
|
* How to render command lists in top-level help pages.
|
|
132
146
|
*
|
package/dist/facade.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { injectAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper } from "./internal/annotations.js";
|
|
2
2
|
import { commandLine, formatMessage, lineBreak, message, optionName, text, value } from "./message.js";
|
|
3
3
|
import { validateCommandNames, validateContextIds, validateMetaNameCollisions, validateOptionNames, validateProgramName } from "./validate.js";
|
|
4
|
-
import { formatUsage, isSuggestionHidden } from "./usage.js";
|
|
4
|
+
import { cloneUsage, formatUsage, isSuggestionHidden, normalizeUsage } from "./usage.js";
|
|
5
5
|
import { formatDocPage } from "./doc.js";
|
|
6
6
|
import { dispatchByMode } from "./internal/mode-dispatch.js";
|
|
7
7
|
import { collectExplicitSourceValues, collectExplicitSourceValuesAsync, createDependencyRuntimeContext } from "./dependency-runtime.js";
|
|
@@ -952,7 +952,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
952
952
|
options = optionsParam ?? {};
|
|
953
953
|
}
|
|
954
954
|
validateProgramName(programName);
|
|
955
|
-
const { colors, maxWidth, showDefault, showChoices, sectionOrder, showUsage, commandList = "recursive", aboveError = "usage", onError = () => {
|
|
955
|
+
const { colors, maxWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
|
|
956
956
|
throw new RunParserError("Failed to parse command line arguments.");
|
|
957
957
|
}, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
|
|
958
958
|
const norm = (c) => c === true ? {} : c;
|
|
@@ -1045,6 +1045,41 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1045
1045
|
completionCommandGroup: completionCommandConfig?.group,
|
|
1046
1046
|
completionOptionGroup: completionOptionConfig?.group
|
|
1047
1047
|
});
|
|
1048
|
+
const helpAsCommand = helpCommandConfig != null;
|
|
1049
|
+
const versionAsCommand = versionCommandConfig != null;
|
|
1050
|
+
const completionAsCommand = completionCommandConfig != null;
|
|
1051
|
+
const helpAsOption = helpOptionConfig != null;
|
|
1052
|
+
const versionAsOption = versionOptionConfig != null;
|
|
1053
|
+
const completionAsOption = completionOptionConfig != null;
|
|
1054
|
+
let cachedRootHelpGeneratorParser;
|
|
1055
|
+
const getRootHelpGeneratorParser = () => {
|
|
1056
|
+
if (cachedRootHelpGeneratorParser != null) return cachedRootHelpGeneratorParser;
|
|
1057
|
+
const commandParsers = [parser];
|
|
1058
|
+
const groupedMeta = {};
|
|
1059
|
+
const ungroupedMeta = [];
|
|
1060
|
+
const addMeta = (metaParser, groupLabel) => {
|
|
1061
|
+
if (groupLabel) (groupedMeta[groupLabel] ??= []).push(metaParser);
|
|
1062
|
+
else ungroupedMeta.push(metaParser);
|
|
1063
|
+
};
|
|
1064
|
+
if (helpAsCommand && helpParsers.helpCommand) addMeta(helpParsers.helpCommand, helpCommandConfig?.group);
|
|
1065
|
+
if (versionAsCommand && versionParsers.versionCommand) addMeta(versionParsers.versionCommand, versionCommandConfig?.group);
|
|
1066
|
+
if (completionAsCommand && completionParsers.completionCommand) addMeta(completionParsers.completionCommand, completionCommandConfig?.group);
|
|
1067
|
+
commandParsers.push(...ungroupedMeta);
|
|
1068
|
+
for (const [label, parsers] of Object.entries(groupedMeta)) commandParsers.push(group(label, parsers.length === 1 ? parsers[0] : longestMatch(...parsers)));
|
|
1069
|
+
const groupedMetaOptions = {};
|
|
1070
|
+
const ungroupedMetaOptions = [];
|
|
1071
|
+
const addMetaOption = (metaParser, groupLabel) => {
|
|
1072
|
+
if (groupLabel) (groupedMetaOptions[groupLabel] ??= []).push(metaParser);
|
|
1073
|
+
else ungroupedMetaOptions.push(metaParser);
|
|
1074
|
+
};
|
|
1075
|
+
if (helpAsOption && helpParsers.helpOption) addMetaOption(helpParsers.helpOption, helpOptionConfig?.group);
|
|
1076
|
+
if (versionAsOption && versionParsers.versionOption) addMetaOption(versionParsers.versionOption, versionOptionConfig?.group);
|
|
1077
|
+
if (completionAsOption && completionParsers.completionOption) addMetaOption(completionParsers.completionOption, completionOptionConfig?.group);
|
|
1078
|
+
commandParsers.push(...ungroupedMetaOptions);
|
|
1079
|
+
for (const [label, parsers] of Object.entries(groupedMetaOptions)) commandParsers.push(group(label, parsers.length === 1 ? parsers[0] : longestMatch(...parsers)));
|
|
1080
|
+
cachedRootHelpGeneratorParser = commandParsers.length === 1 ? commandParsers[0] : longestMatchForMetaCommands(...commandParsers);
|
|
1081
|
+
return cachedRootHelpGeneratorParser;
|
|
1082
|
+
};
|
|
1048
1083
|
const handleResult = (classified) => {
|
|
1049
1084
|
switch (classified.type) {
|
|
1050
1085
|
case "success": return classified.value;
|
|
@@ -1059,12 +1094,6 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1059
1094
|
case "help": {
|
|
1060
1095
|
let helpGeneratorParser;
|
|
1061
1096
|
let docGeneratorParser;
|
|
1062
|
-
const helpAsCommand = helpCommandConfig != null;
|
|
1063
|
-
const versionAsCommand = versionCommandConfig != null;
|
|
1064
|
-
const completionAsCommand = completionCommandConfig != null;
|
|
1065
|
-
const helpAsOption = helpOptionConfig != null;
|
|
1066
|
-
const versionAsOption = versionOptionConfig != null;
|
|
1067
|
-
const completionAsOption = completionOptionConfig != null;
|
|
1068
1097
|
const requestedCommand = classified.commands[0];
|
|
1069
1098
|
if (requestedCommand != null && !classified.preferUserCommandDocs && completionCommandNames.includes(requestedCommand) && completionAsCommand && completionParsers.completionCommand) {
|
|
1070
1099
|
helpGeneratorParser = completionParsers.completionCommand;
|
|
@@ -1076,33 +1105,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1076
1105
|
helpGeneratorParser = versionParsers.versionCommand;
|
|
1077
1106
|
docGeneratorParser = helpGeneratorParser;
|
|
1078
1107
|
} else {
|
|
1079
|
-
|
|
1080
|
-
const groupedMeta = {};
|
|
1081
|
-
const ungroupedMeta = [];
|
|
1082
|
-
const addMeta = (p, groupLabel) => {
|
|
1083
|
-
if (groupLabel) (groupedMeta[groupLabel] ??= []).push(p);
|
|
1084
|
-
else ungroupedMeta.push(p);
|
|
1085
|
-
};
|
|
1086
|
-
if (helpAsCommand && helpParsers.helpCommand) addMeta(helpParsers.helpCommand, helpCommandConfig?.group);
|
|
1087
|
-
if (versionAsCommand && versionParsers.versionCommand) addMeta(versionParsers.versionCommand, versionCommandConfig?.group);
|
|
1088
|
-
if (completionAsCommand && completionParsers.completionCommand) addMeta(completionParsers.completionCommand, completionCommandConfig?.group);
|
|
1089
|
-
commandParsers.push(...ungroupedMeta);
|
|
1090
|
-
for (const [label, parsers] of Object.entries(groupedMeta)) if (parsers.length === 1) commandParsers.push(group(label, parsers[0]));
|
|
1091
|
-
else commandParsers.push(group(label, longestMatch(...parsers)));
|
|
1092
|
-
const groupedMetaOptions = {};
|
|
1093
|
-
const ungroupedMetaOptions = [];
|
|
1094
|
-
const addMetaOption = (p, groupLabel) => {
|
|
1095
|
-
if (groupLabel) (groupedMetaOptions[groupLabel] ??= []).push(p);
|
|
1096
|
-
else ungroupedMetaOptions.push(p);
|
|
1097
|
-
};
|
|
1098
|
-
if (helpAsOption && helpParsers.helpOption) addMetaOption(helpParsers.helpOption, helpOptionConfig?.group);
|
|
1099
|
-
if (versionAsOption && versionParsers.versionOption) addMetaOption(versionParsers.versionOption, versionOptionConfig?.group);
|
|
1100
|
-
if (completionAsOption && completionParsers.completionOption) addMetaOption(completionParsers.completionOption, completionOptionConfig?.group);
|
|
1101
|
-
commandParsers.push(...ungroupedMetaOptions);
|
|
1102
|
-
for (const [label, optParsers] of Object.entries(groupedMetaOptions)) if (optParsers.length === 1) commandParsers.push(group(label, optParsers[0]));
|
|
1103
|
-
else commandParsers.push(group(label, longestMatch(...optParsers)));
|
|
1104
|
-
if (commandParsers.length === 1) helpGeneratorParser = commandParsers[0];
|
|
1105
|
-
else helpGeneratorParser = longestMatchForMetaCommands(...commandParsers);
|
|
1108
|
+
helpGeneratorParser = getRootHelpGeneratorParser();
|
|
1106
1109
|
docGeneratorParser = classified.commands.length > 0 ? parser : helpGeneratorParser;
|
|
1107
1110
|
}
|
|
1108
1111
|
const reportInvalidHelpCommand = (validationError) => {
|
|
@@ -1133,7 +1136,8 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1133
1136
|
bugs: isTopLevel && !isMetaCommandHelp ? bugs ?? doc.bugs : void 0,
|
|
1134
1137
|
footer: shouldOverride ? footer ?? doc.footer : doc.footer ?? footer
|
|
1135
1138
|
}, commandList, isTopLevel);
|
|
1136
|
-
|
|
1139
|
+
const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine) : augmentedDoc;
|
|
1140
|
+
stdout(formatDocPage(programName, renderedDoc, {
|
|
1137
1141
|
colors,
|
|
1138
1142
|
maxWidth,
|
|
1139
1143
|
showDefault,
|
|
@@ -1193,6 +1197,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1193
1197
|
let effectiveAboveError = currentAboveError;
|
|
1194
1198
|
if (effectiveAboveError === "help") if (doc == null) effectiveAboveError = "usage";
|
|
1195
1199
|
else {
|
|
1200
|
+
const isTopLevel = classified.commandPath.length < 1;
|
|
1196
1201
|
const augmentedDoc = maybeCollapseCommandList({
|
|
1197
1202
|
...doc,
|
|
1198
1203
|
brief: brief ?? doc.brief,
|
|
@@ -1201,8 +1206,10 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
1201
1206
|
author: author ?? doc.author,
|
|
1202
1207
|
bugs: bugs ?? doc.bugs,
|
|
1203
1208
|
footer: footer ?? doc.footer
|
|
1204
|
-
}, commandList,
|
|
1205
|
-
|
|
1209
|
+
}, commandList, isTopLevel);
|
|
1210
|
+
const defaultRootUsage = typeof usageLine === "function" && (options.help || options.version || options.completion) ? normalizeUsage(getRootHelpGeneratorParser().usage) : augmentedDoc.usage ?? [];
|
|
1211
|
+
const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine, defaultRootUsage) : augmentedDoc;
|
|
1212
|
+
stderr(formatDocPage(programName, renderedDoc, {
|
|
1206
1213
|
colors,
|
|
1207
1214
|
maxWidth,
|
|
1208
1215
|
showDefault,
|
|
@@ -1298,6 +1305,13 @@ function runParserAsync(parser, programName, args, options) {
|
|
|
1298
1305
|
const result = runParser(parser, programName, args, options);
|
|
1299
1306
|
return Promise.resolve(result);
|
|
1300
1307
|
}
|
|
1308
|
+
function applyUsageLine(doc, usageLine, defaultUsageLine = doc.usage ?? []) {
|
|
1309
|
+
const customUsageLine = typeof usageLine === "function" ? usageLine(cloneUsage(defaultUsageLine)) : usageLine;
|
|
1310
|
+
return {
|
|
1311
|
+
...doc,
|
|
1312
|
+
usage: normalizeUsage(customUsageLine)
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1301
1315
|
function maybeCollapseCommandList(doc, commandList, isTopLevel) {
|
|
1302
1316
|
if (commandList !== "top-level" || !isTopLevel) return doc;
|
|
1303
1317
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optique/core",
|
|
3
|
-
"version": "1.3.0-dev.
|
|
3
|
+
"version": "1.3.0-dev.2357",
|
|
4
4
|
"description": "Type-safe combinatorial command-line interface parser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CLI",
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
"fast-check": "^4.7.0",
|
|
226
226
|
"tsdown": "^0.13.0",
|
|
227
227
|
"typescript": "^5.8.3",
|
|
228
|
-
"@optique/env": "1.3.0-dev.
|
|
228
|
+
"@optique/env": "1.3.0-dev.2357+dcd8c5c2"
|
|
229
229
|
},
|
|
230
230
|
"scripts": {
|
|
231
231
|
"build": "tsdown",
|
package/skills/optique/SKILL.md
CHANGED
|
@@ -56,6 +56,9 @@ Core rules
|
|
|
56
56
|
discriminated union.
|
|
57
57
|
- Enable completion through `run(parser, { completion: "both" })` for CLI
|
|
58
58
|
apps. Do not hand-write completion scripts from parser metadata.
|
|
59
|
+
- Use `usageLine: [{ type: "ellipsis" }]` in runner options when a large root
|
|
60
|
+
synopsis should become a compact `Usage: myapp ...` line. This applies only
|
|
61
|
+
to root full help; use `command()`'s `usageLine` for subcommand help.
|
|
59
62
|
- Use `showUsage: false` in runner options when full help should show the
|
|
60
63
|
brief and command or option sections without the `Usage:` synopsis.
|
|
61
64
|
For deeply nested command trees, add `commandList: "top-level"` when root
|